From 6e83949f649830f73e1b0de166e2d1748892fe1b Mon Sep 17 00:00:00 2001 From: Adam Talbot Date: Tue, 19 Dec 2023 16:33:34 +0000 Subject: [PATCH] fix: normalise install flags to match other commands Signed-off-by: Adam Talbot --- cmd/ctl/pkg/factory/factory.go | 6 ++ cmd/ctl/pkg/install/helm/settings.go | 90 ++++++++++++++++++++++++++++ cmd/ctl/pkg/install/install.go | 38 +++++------- cmd/ctl/pkg/uninstall/uninstall.go | 41 ++++++------- 4 files changed, 128 insertions(+), 47 deletions(-) create mode 100644 cmd/ctl/pkg/install/helm/settings.go diff --git a/cmd/ctl/pkg/factory/factory.go b/cmd/ctl/pkg/factory/factory.go index 87f1eb189..7090bd14c 100644 --- a/cmd/ctl/pkg/factory/factory.go +++ b/cmd/ctl/pkg/factory/factory.go @@ -58,6 +58,10 @@ type Factory struct { // KubeClient is a Kubernetes clientset for interacting with the base // Kubernetes APIs. KubeClient kubernetes.Interface + + // RESTClientGetter is used to get RESTConfig, DiscoveryClients and + // RESTMapper implementations + RESTClientGetter genericclioptions.RESTClientGetter } // New returns a new Factory. The supplied command will have flags registered @@ -109,5 +113,7 @@ func (f *Factory) complete() error { return err } + f.RESTClientGetter = factory + return nil } diff --git a/cmd/ctl/pkg/install/helm/settings.go b/cmd/ctl/pkg/install/helm/settings.go new file mode 100644 index 000000000..a7cc82478 --- /dev/null +++ b/cmd/ctl/pkg/install/helm/settings.go @@ -0,0 +1,90 @@ +/* +Copyright 2021 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package helm + +import ( + "context" + "strconv" + + "github.com/cert-manager/cert-manager/cmd/ctl/pkg/factory" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "helm.sh/helm/v3/pkg/cli" +) + +const defaultCertManagerNamespace = "cert-manager" +const debugLogLevel = 3 + +type NormalisedEnvSettings struct { + EnvSettings *cli.EnvSettings + Factory *factory.Factory +} + +func NewNormalisedEnvSettings() *NormalisedEnvSettings { + return &NormalisedEnvSettings{ + EnvSettings: cli.New(), + } +} + +func (n *NormalisedEnvSettings) Namespace() string { + return n.Factory.Namespace +} + +func (n *NormalisedEnvSettings) Setup(ctx context.Context, cmd *cobra.Command) { + n.Factory = factory.New(ctx, cmd) + n.addEnvSettingsFlags(cmd) + + // Fix the default namespace to be cert-manager + cmd.Flag("namespace").DefValue = defaultCertManagerNamespace + cmd.Flag("namespace").Value.Set(defaultCertManagerNamespace) +} + +func (n *NormalisedEnvSettings) addEnvSettingsFlags(cmd *cobra.Command) { + fs := cmd.Flags() + + // Create a tempoary flag set to add the EnvSettings flags to, this + // can then be iterated over to copy the flags we want to the command + var tmpFlagSet pflag.FlagSet + n.EnvSettings.AddFlags(&tmpFlagSet) + + tmpFlagSet.VisitAll(func(f *pflag.Flag) { + switch f.Name { + case "debug": + // Setup a PreRun to set the helm debug flag. Catch the + // existing PreRun Debug command if one was defined, and execute + // it second. + existingPreRun := cmd.PreRun + cmd.PreRun = func(cmd *cobra.Command, args []string) { + if isLogLevelDebug(cmd) { + f.Value.Set("true") + } + + if existingPreRun != nil { + existingPreRun(cmd, args) + } + } + case "registry-config", "repository-config", "repository-cache": + fs.AddFlag(f) + } + }) +} + +func isLogLevelDebug(cmd *cobra.Command) bool { + flagValue := cmd.Flag("v").Value.String() + logLevel, _ := strconv.Atoi(flagValue) + return logLevel >= debugLogLevel +} diff --git a/cmd/ctl/pkg/install/install.go b/cmd/ctl/pkg/install/install.go index fbfc232bf..627091c97 100644 --- a/cmd/ctl/pkg/install/install.go +++ b/cmd/ctl/pkg/install/install.go @@ -29,7 +29,6 @@ import ( "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/release" @@ -41,10 +40,11 @@ import ( ) type InstallOptions struct { - settings *cli.EnvSettings + settings *helm.NormalisedEnvSettings client *action.Install cfg *action.Configuration valueOpts *values.Options + logFn func(format string, v ...interface{}) ChartName string DryRun bool @@ -54,8 +54,7 @@ type InstallOptions struct { } const ( - installCRDsFlagName = "installCRDs" - defaultCertManagerNamespace = "cert-manager" + installCRDsFlagName = "installCRDs" ) func installDesc() string { @@ -80,12 +79,18 @@ pass in a file or use the '--set' flag and pass configuration from the command l } func NewCmdInstall(ctx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { - settings := cli.New() - cfg := new(action.Configuration) + log := logf.FromContext(ctx, "install") + logFn := func(format string, v ...interface{}) { + log.Info(fmt.Sprintf(format, v...)) + } + + settings := helm.NewNormalisedEnvSettings() + cfg := &action.Configuration{Log: logFn} options := &InstallOptions{ settings: settings, cfg: cfg, + logFn: logFn, client: action.NewInstall(cfg), valueOpts: &values.Options{}, @@ -116,18 +121,7 @@ func NewCmdInstall(ctx context.Context, ioStreams genericclioptions.IOStreams) * SilenceErrors: true, } - settings.AddFlags(cmd.Flags()) - - // The Helm cli.New function does not provide an easy way to - // override the default of the namespace flag. - // See https://github.com/helm/helm/issues/9790 - // - // Here we set the default value shown in the usage message. - cmd.Flag("namespace").DefValue = defaultCertManagerNamespace - // Here we set the default value. - // The returned error is ignored because - // pflag.stringValue.Set always returns a nil. - cmd.Flag("namespace").Value.Set(defaultCertManagerNamespace) + settings.Setup(ctx, cmd) addInstallUninstallFlags(cmd.Flags(), &options.client.Timeout, &options.Wait) @@ -163,7 +157,7 @@ func (o *InstallOptions) runInstall(ctx context.Context) (*release.Release, erro log := logf.FromContext(ctx, "install") // Find chart - cp, err := o.client.ChartPathOptions.LocateChart(o.ChartName, o.settings) + cp, err := o.client.ChartPathOptions.LocateChart(o.ChartName, o.settings.EnvSettings) if err != nil { return nil, err } @@ -184,7 +178,7 @@ func (o *InstallOptions) runInstall(ctx context.Context) (*release.Release, erro } // Merge all values flags - p := getter.All(o.settings) + p := getter.All(o.settings.EnvSettings) chartValues, err := o.valueOpts.MergeValues(p) if err != nil { return nil, err @@ -213,9 +207,7 @@ func (o *InstallOptions) runInstall(ctx context.Context) (*release.Release, erro return dryRunResult, nil } - if err := o.cfg.Init(o.settings.RESTClientGetter(), o.settings.Namespace(), os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) { - log.Info(fmt.Sprintf(format, v...)) - }); err != nil { + if err := o.cfg.Init(o.settings.Factory.RESTClientGetter, o.settings.Namespace(), os.Getenv("HELM_DRIVER"), o.logFn); err != nil { return nil, err } diff --git a/cmd/ctl/pkg/uninstall/uninstall.go b/cmd/ctl/pkg/uninstall/uninstall.go index 0e2d62976..4f760b8c6 100644 --- a/cmd/ctl/pkg/uninstall/uninstall.go +++ b/cmd/ctl/pkg/uninstall/uninstall.go @@ -25,20 +25,22 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/storage/driver" "k8s.io/cli-runtime/pkg/genericclioptions" "github.com/cert-manager/cert-manager/cmd/ctl/pkg/build" + "github.com/cert-manager/cert-manager/cmd/ctl/pkg/install/helm" logf "github.com/cert-manager/cert-manager/pkg/logs" ) type options struct { - settings *cli.EnvSettings + settings *helm.NormalisedEnvSettings client *action.Uninstall cfg *action.Configuration + logFn func(format string, v ...interface{}) + releaseName string disableHooks bool dryRun bool wait bool @@ -47,8 +49,7 @@ type options struct { } const ( - defaultCertManagerNamespace = "cert-manager" - releaseName = "cert-manager" + releaseName = "cert-manager" ) func description() string { @@ -70,13 +71,19 @@ or } func NewCmd(ctx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { - settings := cli.New() - cfg := new(action.Configuration) + log := logf.FromContext(ctx, "install") + logFn := func(format string, v ...interface{}) { + log.Info(fmt.Sprintf(format, v...)) + } + + settings := helm.NewNormalisedEnvSettings() + cfg := &action.Configuration{Log: logFn} options := options{ settings: settings, cfg: cfg, client: action.NewUninstall(cfg), + logFn: logFn, IOStreams: ioStreams, } @@ -102,20 +109,10 @@ func NewCmd(ctx context.Context, ioStreams genericclioptions.IOStreams) *cobra.C SilenceErrors: true, } - settings.AddFlags(cmd.Flags()) - - // The Helm cli.New function does not provide an easy way to - // override the default of the namespace flag. - // See https://github.com/helm/helm/issues/9790 - // - // set the default value shown in the usage message. - cmd.Flag("namespace").DefValue = defaultCertManagerNamespace - - // The returned error is ignored because - // pflag.stringValue.Set always returns a nil. - cmd.Flag("namespace").Value.Set(defaultCertManagerNamespace) + settings.Setup(ctx, cmd) cmd.Flags().DurationVar(&options.client.Timeout, "timeout", 5*time.Minute, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") + cmd.Flags().StringVar(&options.releaseName, "release-name", releaseName, "name of the helm release to uninstall") cmd.Flags().BoolVar(&options.wait, "wait", true, "if set, will wait until all the resources are deleted before returning. It will wait for as long as --timeout") cmd.Flags().BoolVar(&options.dryRun, "dry-run", false, "simulate uninstall and output manifests to be deleted") cmd.Flags().BoolVar(&options.disableHooks, "no-hooks", false, "prevent hooks from running during uninstallation (pre- and post-uninstall hooks)") @@ -126,11 +123,7 @@ func NewCmd(ctx context.Context, ioStreams genericclioptions.IOStreams) *cobra.C // run assumes cert-manager was installed as a Helm release named cert-manager. // this is not configurable to avoid uninstalling non-cert-manager releases. func run(ctx context.Context, o options) (*release.UninstallReleaseResponse, error) { - log := logf.FromContext(ctx, "install") - - if err := o.cfg.Init(o.settings.RESTClientGetter(), o.settings.Namespace(), os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) { - log.Info(fmt.Sprintf(format, v...)) - }); err != nil { + if err := o.cfg.Init(o.settings.Factory.RESTClientGetter, o.settings.Namespace(), os.Getenv("HELM_DRIVER"), o.logFn); err != nil { return nil, fmt.Errorf("o.cfg.Init: %v", err) } @@ -138,7 +131,7 @@ func run(ctx context.Context, o options) (*release.UninstallReleaseResponse, err o.client.DryRun = o.dryRun o.client.Wait = o.wait - res, err := o.client.Run(releaseName) + res, err := o.client.Run(o.releaseName) if errors.Is(err, driver.ErrReleaseNotFound) { return nil, fmt.Errorf("release %v not found in namespace %v, did you use the correct namespace?", releaseName, o.settings.Namespace())