diff --git a/client.go b/client.go index 5f167c88..e496d3a7 100644 --- a/client.go +++ b/client.go @@ -46,7 +46,7 @@ func New(options *Options) (Client, error) { return newClient(options, settings.RESTClientGetter(), settings) } -// NewClientFromKubeConf returns a new Helm client constructed with the provided kubeconfig options +// NewClientFromKubeConf returns a new Helm client constructed with the provided kubeconfig options. func NewClientFromKubeConf(options *KubeConfClientOptions) (Client, error) { settings := cli.New() if options.KubeConfig == nil { @@ -62,21 +62,17 @@ func NewClientFromKubeConf(options *KubeConfClientOptions) (Client, error) { return newClient(options.Options, clientGetter, settings) } -// NewClientFromRestConf returns a new Helm client constructed with the provided REST config options +// NewClientFromRestConf returns a new Helm client constructed with the provided REST config options. func NewClientFromRestConf(options *RestConfClientOptions) (Client, error) { settings := cli.New() clientGetter := NewRESTClientGetter(options.Namespace, nil, options.RestConfig) - err := setEnvSettings(options.Options, settings) - if err != nil { - return nil, err - } - return newClient(options.Options, clientGetter, settings) } -// newClient returns a new Helm client via the provided options and REST config +// newClient is used by both NewClientFromKubeConf and NewClientFromRestConf +// and returns a new Helm client via the provided options and REST config. func newClient(options *Options, clientGetter genericclioptions.RESTClientGetter, settings *cli.EnvSettings) (Client, error) { err := setEnvSettings(options, settings) if err != nil { @@ -111,7 +107,7 @@ func newClient(options *Options, clientGetter genericclioptions.RESTClientGetter }, nil } -// setEnvSettings sets the client's environment settings based on the provided client configuration +// setEnvSettings sets the client's environment settings based on the provided client configuration. func setEnvSettings(options *Options, settings *cli.EnvSettings) error { if options == nil { options = &Options{ @@ -147,7 +143,7 @@ func setEnvSettings(options *Options, settings *cli.EnvSettings) error { return nil } -// AddOrUpdateChartRepo adds or updates the provided helm chart repository +// AddOrUpdateChartRepo adds or updates the provided helm chart repository. func (c *HelmClient) AddOrUpdateChartRepo(entry repo.Entry) error { chartRepo, err := repo.NewChartRepository(&entry, c.Providers) if err != nil { @@ -175,7 +171,7 @@ func (c *HelmClient) AddOrUpdateChartRepo(entry repo.Entry) error { return nil } -// UpdateChartRepos updates the list of chart repositories stored in the client's cache +// UpdateChartRepos updates the list of chart repositories stored in the client's cache. func (c *HelmClient) UpdateChartRepos() error { for _, entry := range c.storage.Repositories { chartRepo, err := repo.NewChartRepository(entry, c.Providers) @@ -196,6 +192,7 @@ func (c *HelmClient) UpdateChartRepos() error { } // InstallOrUpgradeChart installs or upgrades the provided chart and returns the corresponding release. +// Namespace and other context is provided via the helmclient.Options struct when instantiating a client. func (c *HelmClient) InstallOrUpgradeChart(ctx context.Context, spec *ChartSpec) (*release.Release, error) { installed, err := c.chartIsInstalled(spec) if err != nil { @@ -210,7 +207,7 @@ func (c *HelmClient) InstallOrUpgradeChart(ctx context.Context, spec *ChartSpec) } // ListDeployedReleases lists all deployed releases. -// Namespace and other context is provided via the Options struct when instantiating a client. +// Namespace and other context is provided via the helmclient.Options struct when instantiating a client. func (c *HelmClient) ListDeployedReleases() ([]*release.Release, error) { return c.listDeployedReleases() } @@ -241,7 +238,8 @@ func (c *HelmClient) UninstallReleaseByName(name string) error { return c.uninstallReleaseByName(name) } -// install lints and installs the provided chart +// install installs the provided chart. +// Optionally lints the chart if the linting flag is set. func (c *HelmClient) install(spec *ChartSpec) (*release.Release, error) { client := action.NewInstall(c.ActionConfig) mergeInstallOptions(spec, client) @@ -305,7 +303,8 @@ func (c *HelmClient) install(spec *ChartSpec) (*release.Release, error) { return rel, nil } -// upgrade upgrades a chart and CRDs +// upgrade upgrades a chart and CRDs. +// Optionally lints the chart if the linting flag is set. func (c *HelmClient) upgrade(ctx context.Context, spec *ChartSpec) (*release.Release, error) { client := action.NewUpgrade(c.ActionConfig) mergeUpgradeOptions(spec, client) @@ -355,7 +354,7 @@ func (c *HelmClient) upgrade(ctx context.Context, spec *ChartSpec) (*release.Rel return rel, nil } -// uninstallRelease uninstalls the provided release +// uninstallRelease uninstalls the provided release. func (c *HelmClient) uninstallRelease(spec *ChartSpec) error { client := action.NewUninstall(c.ActionConfig) @@ -385,7 +384,7 @@ func (c *HelmClient) uninstallReleaseByName(name string) error { return nil } -// lint lints a chart's values +// lint lints a chart's values. func (c *HelmClient) lint(chartPath string, values map[string]interface{}) error { client := action.NewLint() @@ -402,7 +401,7 @@ func (c *HelmClient) lint(chartPath string, values map[string]interface{}) error return nil } -// TemplateChart returns a rendered version of the provided ChartSpec 'spec' by performing a "dry-run" install +// TemplateChart returns a rendered version of the provided ChartSpec 'spec' by performing a "dry-run" install. func (c *HelmClient) TemplateChart(spec *ChartSpec) ([]byte, error) { client := action.NewInstall(c.ActionConfig) mergeInstallOptions(spec, client) @@ -508,7 +507,7 @@ func (c *HelmClient) ListReleaseHistory(name string, max int) ([]*release.Releas return client.Run(name) } -// upgradeCRDs upgrades the CRDs of the provided chart +// upgradeCRDs upgrades the CRDs of the provided chart. func (c *HelmClient) upgradeCRDs(ctx context.Context, chartInstance *chart.Chart) error { cfg, err := c.ActionConfig.RESTClientGetter.ToRESTConfig() if err != nil { @@ -530,6 +529,7 @@ func (c *HelmClient) upgradeCRDs(ctx context.Context, chartInstance *chart.Chart return nil } +// upgradeCRD upgrades the CRD 'crd' using the provided k8s client. func (c *HelmClient) upgradeCRD(ctx context.Context, k8sClient *clientset.Clientset, crd chart.CRD) error { // use this ugly detour to parse the crdYaml to a CustomResourceDefinitions-Object because direct // yaml-unmarshalling does not find the correct keys @@ -554,6 +554,7 @@ func (c *HelmClient) upgradeCRD(ctx context.Context, k8sClient *clientset.Client } } +// upgradeCRDV1Beta1 upgrades a CRD of the v1beta1 API version using the provided k8s client and CRD yaml. func (c *HelmClient) upgradeCRDV1Beta1(ctx context.Context, cl *clientset.Clientset, rawCRD []byte) error { var crdObj v1beta1.CustomResourceDefinition if err := json.Unmarshal(rawCRD, &crdObj); err != nil { @@ -606,6 +607,7 @@ func (c *HelmClient) upgradeCRDV1Beta1(ctx context.Context, cl *clientset.Client return nil } +// upgradeCRDV1Beta1 upgrades a CRD of the v1 API version using the provided k8s client and CRD yaml. func (c *HelmClient) upgradeCRDV1(ctx context.Context, cl *clientset.Clientset, rawCRD []byte) error { var crdObj v1.CustomResourceDefinition if err := json.Unmarshal(rawCRD, &crdObj); err != nil { @@ -665,7 +667,7 @@ func (c *HelmClient) upgradeCRDV1(ctx context.Context, cl *clientset.Clientset, return nil } -// getChart returns a chart matching the provided chart name and options +// getChart returns a chart matching the provided chart name and options. func (c *HelmClient) getChart(chartName string, chartPathOptions *action.ChartPathOptions) (*chart.Chart, string, error) { chartPath, err := chartPathOptions.LocateChart(chartName, c.Settings) if err != nil { @@ -740,7 +742,7 @@ func (c *HelmClient) rollbackRelease(spec *ChartSpec, version int) error { return client.Run(spec.ReleaseName) } -// mergeRollbackOptions merges values of the provided chart to helm rollback options used by the client +// mergeRollbackOptions merges values of the provided chart to helm rollback options used by the client. func mergeRollbackOptions(chartSpec *ChartSpec, rollbackOptions *action.Rollback) { rollbackOptions.DisableHooks = chartSpec.DisableHooks rollbackOptions.DryRun = chartSpec.DryRun @@ -752,7 +754,7 @@ func mergeRollbackOptions(chartSpec *ChartSpec, rollbackOptions *action.Rollback rollbackOptions.Wait = chartSpec.Wait } -// mergeInstallOptions merges values of the provided chart to helm install options used by the client +// mergeInstallOptions merges values of the provided chart to helm install options used by the client. func mergeInstallOptions(chartSpec *ChartSpec, installOptions *action.Install) { installOptions.CreateNamespace = chartSpec.CreateNamespace installOptions.DisableHooks = chartSpec.DisableHooks @@ -771,7 +773,7 @@ func mergeInstallOptions(chartSpec *ChartSpec, installOptions *action.Install) { installOptions.SubNotes = chartSpec.SubNotes } -// mergeUpgradeOptions merges values of the provided chart to helm upgrade options used by the client +// mergeUpgradeOptions merges values of the provided chart to helm upgrade options used by the client. func mergeUpgradeOptions(chartSpec *ChartSpec, upgradeOptions *action.Upgrade) { upgradeOptions.Version = chartSpec.Version upgradeOptions.Namespace = chartSpec.Namespace @@ -789,7 +791,7 @@ func mergeUpgradeOptions(chartSpec *ChartSpec, upgradeOptions *action.Upgrade) { upgradeOptions.SubNotes = chartSpec.SubNotes } -// mergeUninstallReleaseOptions merges values of the provided chart to helm uninstall options used by the client +// mergeUninstallReleaseOptions merges values of the provided chart to helm uninstall options used by the client. func mergeUninstallReleaseOptions(chartSpec *ChartSpec, uninstallReleaseOptions *action.Uninstall) { uninstallReleaseOptions.DisableHooks = chartSpec.DisableHooks uninstallReleaseOptions.Timeout = chartSpec.Timeout diff --git a/client_test.go b/client_test.go index ab0c484c..b7ed5475 100644 --- a/client_test.go +++ b/client_test.go @@ -9,10 +9,12 @@ import ( func ExampleNew() { opt := &Options{ + Namespace: "default", // Change this to the namespace you wish the client to operate in. RepositoryCache: "/tmp/.helmcache", RepositoryConfig: "/tmp/.helmrepo", Debug: true, Linting: true, + DebugLog: func(format string, v ...interface{}) {}, } helmClient, err := New(opt) @@ -25,10 +27,14 @@ func ExampleNew() { func ExampleNewClientFromRestConf() { opt := &RestConfClientOptions{ Options: &Options{ + Namespace: "default", // Change this to the namespace you wish the client to operate in. RepositoryCache: "/tmp/.helmcache", RepositoryConfig: "/tmp/.helmrepo", Debug: true, - Linting: true, + Linting: true, // Change this to false if you don't want linting. + DebugLog: func(format string, v ...interface{}) { + // Change this to your own logger. Default is 'log.Printf(format, v...)'. + }, }, RestConfig: &rest.Config{}, } @@ -43,10 +49,14 @@ func ExampleNewClientFromRestConf() { func ExampleNewClientFromKubeConf() { opt := &KubeConfClientOptions{ Options: &Options{ + Namespace: "default", // Change this to the namespace you wish to install the chart in. RepositoryCache: "/tmp/.helmcache", RepositoryConfig: "/tmp/.helmrepo", Debug: true, - Linting: true, + Linting: true, // Change this to false if you don't want linting. + DebugLog: func(format string, v ...interface{}) { + // Change this to your own logger. Default is 'log.Printf(format, v...)'. + }, }, KubeContext: "", KubeConfig: []byte{}, @@ -60,13 +70,13 @@ func ExampleNewClientFromKubeConf() { } func ExampleHelmClient_AddOrUpdateChartRepo_public() { - // Define a public chart repository + // Define a public chart repository. chartRepo := repo.Entry{ Name: "stable", URL: "https://kubernetes-charts.storage.googleapis.com", } - // Add a chart-repository to the client + // Add a chart-repository to the client. if err := helmClient.AddOrUpdateChartRepo(chartRepo); err != nil { panic(err) } @@ -79,11 +89,11 @@ func ExampleHelmClient_AddOrUpdateChartRepo_private() { URL: "https://private-chartrepo.somedomain.com", Username: "foo", Password: "bar", - // Since helm 3.6.1 it is necessary to pass PassCredentialsAll = true + // Since helm 3.6.1 it is necessary to pass 'PassCredentialsAll = true'. PassCredentialsAll: true, } - // Add a chart-repository to the client + // Add a chart-repository to the client. if err := helmClient.AddOrUpdateChartRepo(chartRepo); err != nil { panic(err) } @@ -99,13 +109,15 @@ func ExampleHelmClient_InstallOrUpgradeChart() { Wait: true, } - if err, _ := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + // Install a chart release. + // Note that helmclient.Options.Namespace should ideally match the namespace in chartSpec.Namespace. + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { panic(err) } } func ExampleHelmClient_InstallOrUpgradeChart_useChartDirectory() { - // Use an unpacked chart directory + // Use an unpacked chart directory. chartSpec := ChartSpec{ ReleaseName: "etcd-operator", ChartName: "/path/to/stable/etcd-operator", @@ -114,13 +126,13 @@ func ExampleHelmClient_InstallOrUpgradeChart_useChartDirectory() { Wait: true, } - if err, _ := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { panic(err) } } func ExampleHelmClient_InstallOrUpgradeChart_useLocalChartArchive() { - // Use an archived chart directory + // Use an archived chart directory. chartSpec := ChartSpec{ ReleaseName: "etcd-operator", ChartName: "/path/to/stable/etcd-operator.tar.gz", @@ -129,13 +141,13 @@ func ExampleHelmClient_InstallOrUpgradeChart_useLocalChartArchive() { Wait: true, } - if err, _ := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { panic(err) } } func ExampleHelmClient_InstallOrUpgradeChart_useURL() { - // Use an archived chart directory via URL + // Use an archived chart directory via URL. chartSpec := ChartSpec{ ReleaseName: "etcd-operator", ChartName: "http://helm.whatever.com/repo/etcd-operator.tar.gz", @@ -144,13 +156,13 @@ func ExampleHelmClient_InstallOrUpgradeChart_useURL() { Wait: true, } - if err, _ := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { panic(err) } } func ExampleHelmClient_LintChart() { - // Define a chart with custom values to be tested + // Define a chart with custom values to be tested. chartSpec := ChartSpec{ ReleaseName: "etcd-operator", ChartName: "stable/etcd-operator", @@ -186,13 +198,14 @@ func ExampleHelmClient_TemplateChart() { } func ExampleHelmClient_UpdateChartRepos() { + // Update the list of chart repositories. if err := helmClient.UpdateChartRepos(); err != nil { panic(err) } } func ExampleHelmClient_UninstallRelease() { - // Define the released chart to be installed + // Define the released chart to be installed. chartSpec := ChartSpec{ ReleaseName: "etcd-operator", ChartName: "stable/etcd-operator", @@ -201,30 +214,36 @@ func ExampleHelmClient_UninstallRelease() { Wait: true, } + // Uninstall the chart release. + // Note that helmclient.Options.Namespace should ideally match the namespace in chartSpec.Namespace. if err := helmClient.UninstallRelease(&chartSpec); err != nil { panic(err) } } func ExampleHelmClient_UninstallReleaseByName() { + // Uninstall a release by name. if err := helmClient.UninstallReleaseByName("etcd-operator"); err != nil { panic(err) } } func ExampleHelmClient_ListDeployedReleases() { + // List all deployed releases. if _, err := helmClient.ListDeployedReleases(); err != nil { panic(err) } } func ExampleHelmClient_GetReleaseValues() { + // Get the values of a deployed release. if _, err := helmClient.GetReleaseValues("etcd-operator", true); err != nil { panic(err) } } func ExampleHelmClient_GetRelease() { + // Get specific details of a deployed release. if _, err := helmClient.GetRelease("etcd-operator"); err != nil { panic(err) } diff --git a/interface.go b/interface.go index 22c523e5..c2e9c38a 100644 --- a/interface.go +++ b/interface.go @@ -11,6 +11,7 @@ import ( //go:generate mockgen -source=interface.go -package mockhelmclient -destination=./mock/interface.go -self_package=. Client // Client holds the method signatures for a Helm client. +// NOTE: This is an interface to allow for mocking in tests. type Client interface { AddOrUpdateChartRepo(entry repo.Entry) error UpdateChartRepos() error diff --git a/types.go b/types.go index 26520bc4..39bc7f03 100644 --- a/types.go +++ b/types.go @@ -11,20 +11,23 @@ import ( "helm.sh/helm/v3/pkg/repo" ) -// KubeConfClientOptions defines the options used for constructing a client via kubeconfig +// Type Guard asserting that HelmClient satisfies the HelmClient interface. +var _ Client = &HelmClient{} + +// KubeConfClientOptions defines the options used for constructing a client via kubeconfig. type KubeConfClientOptions struct { *Options KubeContext string KubeConfig []byte } -// RestConfClientOptions defines the options used for constructing a client via REST config +// RestConfClientOptions defines the options used for constructing a client via REST config. type RestConfClientOptions struct { *Options RestConfig *rest.Config } -// Options defines the options of a client +// Options defines the options of a client. type Options struct { Namespace string RepositoryConfig string @@ -34,18 +37,20 @@ type Options struct { DebugLog action.DebugLog } -// RESTClientGetter defines the values of a helm REST client +// RESTClientGetter defines the values of a helm REST client. type RESTClientGetter struct { namespace string kubeConfig []byte restConfig *rest.Config } -// HelmClient Client defines the values of a helm client +// HelmClient Client defines the values of a helm client. type HelmClient struct { - Settings *cli.EnvSettings - Providers getter.Providers - storage *repo.File + // Settings defines the environment settings of a client. + Settings *cli.EnvSettings + Providers getter.Providers + storage *repo.File + // ActionConfig is the helm action configuration. ActionConfig *action.Configuration linting bool DebugLog action.DebugLog @@ -55,71 +60,74 @@ type HelmClient struct { type ChartSpec struct { ReleaseName string `json:"release"` ChartName string `json:"chart"` - Namespace string `json:"namespace"` - + // Namespace where the chart release is deployed. + // Note that helmclient.Options.Namespace should ideally match the namespace configured here. + Namespace string `json:"namespace"` + // ValuesYaml is the values.yaml content. // use string instead of map[string]interface{} // https://github.com/kubernetes-sigs/kubebuilder/issues/528#issuecomment-466449483 // and https://github.com/kubernetes-sigs/controller-tools/pull/317 // +optional ValuesYaml string `json:"valuesYaml,omitempty"` - + // Version of the chart release. // +optional Version string `json:"version,omitempty"` - + // CreateNamespace indicates whether to create the namespace if it does not exist. // +optional CreateNamespace bool `json:"createNamespace,omitempty"` - + // DisableHooks indicates whether to disable hooks. // +optional DisableHooks bool `json:"disableHooks,omitempty"` - + // Replace indicates whether to replace the chart release if it already exists. // +optional Replace bool `json:"replace,omitempty"` - + // Wait indicates whether to wait for the release to be deployed or not. // +optional Wait bool `json:"wait,omitempty"` - + // DependencyUpdate indicates whether to update the chart release if the dependencies have changed. // +optional DependencyUpdate bool `json:"dependencyUpdate,omitempty"` - + // Timeout configures the time to wait for any individual Kubernetes operation (like Jobs for hooks). // +optional Timeout time.Duration `json:"timeout,omitempty"` - + // GenerateName indicates that the release name should be generated. // +optional GenerateName bool `json:"generateName,omitempty"` - + // NameTemplate is the template used to generate the release name if GenerateName is configured. // +optional NameTemplate string `json:"NameTemplate,omitempty"` - + // Atomic indicates whether to install resources atomically. + // 'Wait' will automatically be set to true when using Atomic. // +optional Atomic bool `json:"atomic,omitempty"` - + // SkipCRDs indicates whether to skip CRDs during installation. // +optional SkipCRDs bool `json:"skipCRDs,omitempty"` - + // Upgrade indicates whether to perform a CRD upgrade during installation. // +optional UpgradeCRDs bool `json:"upgradeCRDs,omitempty"` - + // SubNotes indicates whether to print sub-notes. // +optional SubNotes bool `json:"subNotes,omitempty"` - + // Force indicates whether to force the operation. // +optional Force bool `json:"force,omitempty"` - + // ResetValues indicates whether to reset the values.yaml file during installation. // +optional ResetValues bool `json:"resetValues,omitempty"` - + // ReuseValues indicates whether to reuse the values.yaml file during installation. // +optional ReuseValues bool `json:"reuseValues,omitempty"` - + // Recreate indicates whether to recreate the release if it already exists. // +optional Recreate bool `json:"recreate,omitempty"` - + // MaxHistory limits the maximum number of revisions saved per release. // +optional MaxHistory int `json:"maxHistory,omitempty"` - + // CleanupOnFail indicates whether to cleanup the release on failure. // +optional CleanupOnFail bool `json:"cleanupOnFail,omitempty"` - + // DryRun indicates whether to perform a dry run. // +optional DryRun bool `json:"dryRun,omitempty"` }