From bca958091a2fd53ec64b5785d6520a379a6a4735 Mon Sep 17 00:00:00 2001 From: Tim Ramlot <42113979+inteon@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:06:48 +0200 Subject: [PATCH] make the binary dynamically determine whether it is a kubectl plugin Signed-off-by: Tim Ramlot <42113979+inteon@users.noreply.github.com> --- .goreleaser.yml | 1 - cmd/cmd.go | 39 +++----------- main.go | 6 ++- make/00_mod.mk | 12 +---- pkg/approve/approve.go | 33 +++++------- pkg/build/build.go | 52 ++++++++++++++++--- pkg/build/commands/commands.go | 10 +--- pkg/check/api/api.go | 3 +- pkg/completion/bash.go | 6 ++- pkg/completion/completion.go | 13 +++-- pkg/completion/fish.go | 6 ++- pkg/completion/kubectl.go | 45 ++++++++++++++++ pkg/completion/powershell.go | 6 ++- pkg/completion/zsh.go | 6 ++- pkg/convert/convert.go | 43 +++++++-------- .../certificaterequest/certificaterequest.go | 49 ++++++++--------- .../certificatesigningrequest.go | 49 ++++++++--------- pkg/deny/deny.go | 33 +++++------- pkg/inspect/secret/secret.go | 23 +++----- pkg/install/install.go | 6 +-- pkg/renew/renew.go | 33 +++++------- pkg/status/certificate/certificate.go | 23 +++----- pkg/uninstall/uninstall.go | 6 +-- pkg/upgrade/migrateapiversion/command.go | 45 +++++++--------- pkg/version/version.go | 6 +-- 25 files changed, 276 insertions(+), 278 deletions(-) create mode 100644 pkg/completion/kubectl.go diff --git a/.goreleaser.yml b/.goreleaser.yml index e2c6c7f..6dafd50 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,7 +2,6 @@ # to this builds array (environment variables, flags, ...) builds: - id: cmctl - - id: kubectl_cert-manager # config the checksum filename # https://goreleaser.com/customization/checksum diff --git a/cmd/cmd.go b/cmd/cmd.go index 619beb8..403bb46 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -18,7 +18,6 @@ package cmd import ( "context" - "fmt" "io" logf "github.com/cert-manager/cert-manager/pkg/logs" @@ -31,14 +30,18 @@ import ( ) func NewCertManagerCtlCommand(ctx context.Context, in io.Reader, out, err io.Writer) *cobra.Command { - ctx = logf.NewContext(ctx, logf.Log) - logOptions := logs.NewOptions() cmds := &cobra.Command{ - Use: build.Name(), + Use: build.Name(ctx), + Annotations: map[string]string{ + // For commands that have a space (eg. kubectl cert-manager), the name + // is not correctly determined based on just the Use field. + cobra.CommandDisplayNameAnnotation: build.Name(ctx), + }, + Short: "cert-manager CLI tool to manage and configure cert-manager resources", - Long: build.WithTemplate(` + Long: build.WithTemplate(ctx, ` {{.BuildName}} is a CLI tool manage and configure cert-manager resources for Kubernetes`), CompletionOptions: cobra.CompletionOptions{ DisableDefaultCmd: true, @@ -49,7 +52,6 @@ func NewCertManagerCtlCommand(ctx context.Context, in io.Reader, out, err io.Wri SilenceErrors: true, // Errors are already logged when calling cmd.Execute() SilenceUsage: true, // Don't print usage when an error occurs } - cmds.SetUsageTemplate(usageTemplate()) logf.AddFlagsNonDeprecated(logOptions, cmds.PersistentFlags()) @@ -60,28 +62,3 @@ func NewCertManagerCtlCommand(ctx context.Context, in io.Reader, out, err io.Wri return cmds } - -func usageTemplate() string { - return fmt.Sprintf(`Usage:{{if .Runnable}} %s {{end}}{{if .HasAvailableSubCommands}} %s [command]{{end}}{{if gt (len .Aliases) 0}} - -Aliases: - {{.NameAndAliases}}{{end}}{{if .HasExample}} - -Examples: -{{.Example}}{{end}}{{if .HasAvailableSubCommands}} - -Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -Use "%s [command] --help" for more information about a command.{{end}} -`, build.Name(), build.Name(), build.Name()) -} diff --git a/main.go b/main.go index 34f9c45..a2a80d7 100644 --- a/main.go +++ b/main.go @@ -29,16 +29,20 @@ import ( ctlcmd "github.com/cert-manager/cmctl/v2/cmd" "github.com/cert-manager/cmctl/v2/internal/util" + "github.com/cert-manager/cmctl/v2/pkg/build" ) func main() { ctx, exit := util.SetupExitHandler(context.Background(), util.AlwaysErrCode) defer exit() // This function might call os.Exit, so defer last + ctlName, isKubectlPlugin := build.DetectCtlInfo(os.Args) + logf.InitLogs() defer logf.FlushLogs() ctrl.SetLogger(logf.Log) - ctx = logf.NewContext(ctx, logf.Log, "cmctl") + ctx = logf.NewContext(ctx, logf.Log, ctlName) + ctx = build.WithCtlInfo(ctx, ctlName, isKubectlPlugin) // In cmctl, we are using cmdutil.CheckErr, a kubectl utility function that creates human readable // error messages from errors. By default, this function will call os.Exit(1) if it receives an error. diff --git a/make/00_mod.mk b/make/00_mod.mk index 911a2bc..485f4f0 100644 --- a/make/00_mod.mk +++ b/make/00_mod.mk @@ -14,23 +14,13 @@ repo_name := github.com/cert-manager/cmctl/v2 -exe_build_names := cmctl kubectl_cert-manager +exe_build_names := cmctl gorelease_file := .goreleaser.yml go_cmctl_main_dir := . go_cmctl_mod_dir := . go_cmctl_ldflags := \ - -X $(repo_name)/pkg/build.name=cmctl \ - -X $(repo_name)/pkg/build/commands.registerCompletion=true \ -X github.com/cert-manager/cert-manager/pkg/util.AppVersion=$(VERSION) \ -X github.com/cert-manager/cert-manager/pkg/util.AppGitCommit=$(GITCOMMIT) -go_kubectl_cert-manager_main_dir := . -go_kubectl_cert-manager_mod_dir := . -go_kubectl_cert-manager_ldflags := \ - -X $(repo_name)/pkg/build.name=kubectl \ - -X $(repo_name)/pkg/build/commands.registerCompletion=false \ - -X github.com/cert-manager/cert-manager/pkg/util/version.AppVersion=$(VERSION) \ - -X github.com/cert-manager/cert-manager/pkg/util/version.AppGitCommit=$(GITCOMMIT) - golangci_lint_config := .golangci.yaml diff --git a/pkg/approve/approve.go b/pkg/approve/approve.go index 143bc82..2372723 100644 --- a/pkg/approve/approve.go +++ b/pkg/approve/approve.go @@ -27,26 +27,12 @@ import ( "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "github.com/cert-manager/cmctl/v2/pkg/build" "github.com/cert-manager/cmctl/v2/pkg/factory" ) -var ( - example = templates.Examples(i18n.T(build.WithTemplate(` -# Approve a CertificateRequest with the name 'my-cr' -{{.BuildName}} approve my-cr - -# Approve a CertificateRequest in namespace default -{{.BuildName}} approve my-cr --namespace default - -# Approve a CertificateRequest giving a custom reason and message -{{.BuildName}} approve my-cr --reason "ManualApproval" --reason "Approved by PKI department" -`))) -) - // Options is a struct to support create certificaterequest command type Options struct { // Reason is the string that will be set on the Reason field of the Approved @@ -71,10 +57,19 @@ func NewCmdApprove(setupCtx context.Context, ioStreams genericclioptions.IOStrea o := newOptions(ioStreams) cmd := &cobra.Command{ - Use: "approve", - Short: "Approve a CertificateRequest", - Long: `Mark a CertificateRequest as Approved, so it may be signed by a configured Issuer.`, - Example: example, + Use: "approve", + Short: "Approve a CertificateRequest", + Long: `Mark a CertificateRequest as Approved, so it may be signed by a configured Issuer.`, + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Approve a CertificateRequest with the name 'my-cr' +{{.BuildName}} approve my-cr + +# Approve a CertificateRequest in namespace default +{{.BuildName}} approve my-cr --namespace default + +# Approve a CertificateRequest giving a custom reason and message +{{.BuildName}} approve my-cr --reason "ManualApproval" --reason "Approved by PKI department" +`)), ValidArgsFunction: factory.ValidArgsListCertificateRequests(&o.Factory), PreRunE: func(cmd *cobra.Command, args []string) error { return o.Validate(args) @@ -86,7 +81,7 @@ func NewCmdApprove(setupCtx context.Context, ioStreams genericclioptions.IOStrea cmd.Flags().StringVar(&o.Reason, "reason", "KubectlCertManager", "The reason to give as to what approved this CertificateRequest.") - cmd.Flags().StringVar(&o.Message, "message", fmt.Sprintf("manually approved by %q", build.Name()), + cmd.Flags().StringVar(&o.Message, "message", fmt.Sprintf("manually approved by %q", build.Name(setupCtx)), "The message to give as to why this CertificateRequest was approved.") o.Factory = factory.New(cmd) diff --git a/pkg/build/build.go b/pkg/build/build.go index 144be39..97c6631 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -18,24 +18,62 @@ package build import ( "bytes" + "context" + "os" + "path/filepath" + "strings" "text/template" ) -// name is the build time configurable name of the build (name of the target -// binary name). -var name = "cmctl" +var defaultCtlName string = "cmctl" +var defaultIsKubectlPlugin bool = false + +func DetectCtlInfo(args []string) (name string, isKubectlPlugin bool) { + commandName := filepath.Base(os.Args[0]) + if strings.HasPrefix(commandName, "kubectl-") || strings.HasPrefix(commandName, "kubectl_") { + return "kubectl cert-manager", true + } + + return commandName, false +} + +// contextNameKey is how we find the ctl name in a context.Context. +type contextNameKey struct{} + +// contextIsKubectlPluginKey is how we find if the ctl is a Kubectl plugin in a context.Context. +type contextIsKubectlPluginKey struct{} + +func WithCtlInfo(ctx context.Context, name string, isKubectlPlugin bool) context.Context { + ctx = context.WithValue(ctx, contextNameKey{}, name) + ctx = context.WithValue(ctx, contextIsKubectlPluginKey{}, isKubectlPlugin) + return ctx +} + +func Name(ctx context.Context) string { + name, ok := ctx.Value(contextNameKey{}).(string) + if !ok { + return defaultCtlName + } -// Name returns the build name. -func Name() string { return name } +func IsKubectlPlugin(ctx context.Context) bool { + isKubectlPlugin, ok := ctx.Value(contextIsKubectlPluginKey{}).(bool) + if !ok { + return defaultIsKubectlPlugin + } + + return isKubectlPlugin +} + // WithTemplate returns a string that has the build name templated out with the // configured build name. Build name templates on '{{ .BuildName }}' variable. -func WithTemplate(str string) string { +func WithTemplate(ctx context.Context, str string) string { + buildName := Name(ctx) tmpl := template.Must(template.New("build-name").Parse(str)) var buf bytes.Buffer - if err := tmpl.Execute(&buf, struct{ BuildName string }{name}); err != nil { + if err := tmpl.Execute(&buf, struct{ BuildName string }{buildName}); err != nil { // We panic here as it should never be possible that this template fails. panic(err) } diff --git a/pkg/build/commands/commands.go b/pkg/build/commands/commands.go index 6707c75..fd92298 100644 --- a/pkg/build/commands/commands.go +++ b/pkg/build/commands/commands.go @@ -18,7 +18,6 @@ package commands import ( "context" - "strings" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -37,11 +36,6 @@ import ( "github.com/cert-manager/cmctl/v2/pkg/version" ) -// registerCompletion gates whether the completion command is registered. -// Specifically useful when building the CLI as a kubectl plugin which does not -// support completion. -var registerCompletion = "false" - type RegisterCommandFunc func(context.Context, genericclioptions.IOStreams) *cobra.Command // Commands returns the cobra Commands that should be registered for the CLI @@ -61,10 +55,8 @@ func Commands() []RegisterCommandFunc { // Experimental features experimental.NewCmdExperimental, - } - if strings.ToLower(registerCompletion) == "true" { - cmds = append(cmds, completion.NewCmdCompletion) + completion.NewCmdCompletion, } return cmds diff --git a/pkg/check/api/api.go b/pkg/check/api/api.go index e963ded..13dbc13 100644 --- a/pkg/check/api/api.go +++ b/pkg/check/api/api.go @@ -28,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" cmcmdutil "github.com/cert-manager/cmctl/v2/internal/util" @@ -51,7 +50,7 @@ type Options struct { *factory.Factory } -var checkApiDesc = templates.LongDesc(i18n.T(` +var checkApiDesc = templates.LongDesc((` This check attempts to perform a dry-run create of a cert-manager *v1alpha2* Certificate resource in order to verify that CRDs are installed and all the required webhooks are reachable by the K8S API server. diff --git a/pkg/completion/bash.go b/pkg/completion/bash.go index c7f703d..4a54b23 100644 --- a/pkg/completion/bash.go +++ b/pkg/completion/bash.go @@ -17,17 +17,19 @@ limitations under the License. package completion import ( + "context" + "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "github.com/cert-manager/cmctl/v2/pkg/build" ) -func newCmdCompletionBash(ioStreams genericclioptions.IOStreams) *cobra.Command { +func newCmdCompletionBash(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { return &cobra.Command{ Use: "bash", Short: "Generate cert-manager CLI scripts for a Bash shell", - Long: build.WithTemplate(`To load completions: + Long: build.WithTemplate(setupCtx, `To load completions: Bash: $ source <({{.BuildName}} completion bash) # To load completions for each session, execute once: diff --git a/pkg/completion/completion.go b/pkg/completion/completion.go index 5798460..2f308ff 100644 --- a/pkg/completion/completion.go +++ b/pkg/completion/completion.go @@ -19,6 +19,7 @@ package completion import ( "context" + "github.com/cert-manager/cmctl/v2/pkg/build" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" ) @@ -30,10 +31,14 @@ func NewCmdCompletion(setupCtx context.Context, ioStreams genericclioptions.IOSt Long: "Generate completion for the cert-manager CLI so arguments and flags can be suggested and auto-completed", } - cmds.AddCommand(newCmdCompletionBash(ioStreams)) - cmds.AddCommand(newCmdCompletionZSH(ioStreams)) - cmds.AddCommand(newCmdCompletionFish(ioStreams)) - cmds.AddCommand(newCmdCompletionPowerShell(ioStreams)) + if build.IsKubectlPlugin(setupCtx) { + cmds.AddCommand(newCmdCompletionKubectl(setupCtx, ioStreams)) + } else { + cmds.AddCommand(newCmdCompletionBash(setupCtx, ioStreams)) + cmds.AddCommand(newCmdCompletionZSH(setupCtx, ioStreams)) + cmds.AddCommand(newCmdCompletionFish(setupCtx, ioStreams)) + cmds.AddCommand(newCmdCompletionPowerShell(setupCtx, ioStreams)) + } return cmds } diff --git a/pkg/completion/fish.go b/pkg/completion/fish.go index d999baa..dd72f2c 100644 --- a/pkg/completion/fish.go +++ b/pkg/completion/fish.go @@ -17,17 +17,19 @@ limitations under the License. package completion import ( + "context" + "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "github.com/cert-manager/cmctl/v2/pkg/build" ) -func newCmdCompletionFish(ioStreams genericclioptions.IOStreams) *cobra.Command { +func newCmdCompletionFish(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { return &cobra.Command{ Use: "fish", Short: "Generate cert-manager CLI scripts for a Fish shell", - Long: build.WithTemplate(`To load completions: + Long: build.WithTemplate(setupCtx, `To load completions: $ {{.BuildName}} completion fish | source # To load completions for each session, execute once: diff --git a/pkg/completion/kubectl.go b/pkg/completion/kubectl.go new file mode 100644 index 0000000..140c28d --- /dev/null +++ b/pkg/completion/kubectl.go @@ -0,0 +1,45 @@ +/* +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 completion + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericclioptions" + + "github.com/cert-manager/cmctl/v2/pkg/build" +) + +func newCmdCompletionKubectl(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { + return &cobra.Command{ + Use: "kubectl", + Short: "Generate cert-manager CLI scripts for Kubectl", + Long: build.WithTemplate(setupCtx, `To load completions: +$ {{.BuildName}} completion kubectl > kubectl_complete-cert_manager +$ sudo install kubectl_complete-cert_manager /usr/local/bin +`), + DisableFlagsInUseLine: true, + RunE: func(cmd *cobra.Command, args []string) error { + _, err := fmt.Fprint(ioStreams.Out, `#!/usr/bin/env sh +kubectl cert-manager __complete "$@" +`) + return err + }, + } +} diff --git a/pkg/completion/powershell.go b/pkg/completion/powershell.go index 8ec0931..7ab4429 100644 --- a/pkg/completion/powershell.go +++ b/pkg/completion/powershell.go @@ -17,17 +17,19 @@ limitations under the License. package completion import ( + "context" + "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "github.com/cert-manager/cmctl/v2/pkg/build" ) -func newCmdCompletionPowerShell(ioStreams genericclioptions.IOStreams) *cobra.Command { +func newCmdCompletionPowerShell(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { return &cobra.Command{ Use: "powershell", Short: "Generate cert-manager CLI scripts for a PowerShell shell", - Long: build.WithTemplate(`To load completions: + Long: build.WithTemplate(setupCtx, `To load completions: PS> {{.BuildName}} completion powershell | Out-String | Invoke-Expression # To load completions for every new session, run: diff --git a/pkg/completion/zsh.go b/pkg/completion/zsh.go index b917aca..4f80197 100644 --- a/pkg/completion/zsh.go +++ b/pkg/completion/zsh.go @@ -17,17 +17,19 @@ limitations under the License. package completion import ( + "context" + "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "github.com/cert-manager/cmctl/v2/pkg/build" ) -func newCmdCompletionZSH(ioStreams genericclioptions.IOStreams) *cobra.Command { +func newCmdCompletionZSH(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { return &cobra.Command{ Use: "zsh", Short: "Generation cert-manager CLI scripts for a ZSH shell", - Long: build.WithTemplate(`To load completions: + Long: build.WithTemplate(setupCtx, `To load completions: # If shell completion is not already enabled in your environment, # you will need to enable it. You can execute the following once: $ echo "autoload -U compinit; compinit" >> ~/.zshrc diff --git a/pkg/convert/convert.go b/pkg/convert/convert.go index 68f0989..5fc7989 100644 --- a/pkg/convert/convert.go +++ b/pkg/convert/convert.go @@ -32,32 +32,11 @@ import ( "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "github.com/cert-manager/cmctl/v2/pkg/build" ) -var ( - example = templates.Examples(i18n.T(build.WithTemplate(` - # Convert 'cert.yaml' to latest version and print to stdout. - {{.BuildName}} convert -f cert.yaml - - # Convert kustomize overlay under current directory to 'cert-manager.io/v1alpha3' - {{.BuildName}} convert -k . --output-version cert-manager.io/v1alpha3`))) - - longDesc = templates.LongDesc(i18n.T(` -Convert cert-manager config files between different API versions. Both YAML -and JSON formats are accepted. - -The command takes filename, directory, or URL as input, and converts into the -format of the version specified by --output-version flag. If target version is -not specified or not supported, it will convert to the latest version - -The default output will be printed to stdout in YAML format. One can use -o option -to change to output destination.`)) -) - var ( // Use this scheme as it has the internal cert-manager types // and their conversion functions registered. @@ -88,10 +67,24 @@ func NewCmdConvert(setupCtx context.Context, ioStreams genericclioptions.IOStrea o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "convert", - Short: "Convert cert-manager config files between different API versions", - Long: longDesc, - Example: example, + Use: "convert", + Short: "Convert cert-manager config files between different API versions", + Long: templates.LongDesc(` +Convert cert-manager config files between different API versions. Both YAML +and JSON formats are accepted. + +The command takes filename, directory, or URL as input, and converts into the +format of the version specified by --output-version flag. If target version is +not specified or not supported, it will convert to the latest version + +The default output will be printed to stdout in YAML format. One can use -o option +to change to output destination.`), + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Convert 'cert.yaml' to latest version and print to stdout. +{{.BuildName}} convert -f cert.yaml + +# Convert kustomize overlay under current directory to 'cert-manager.io/v1alpha3' +{{.BuildName}} convert -k . --output-version cert-manager.io/v1alpha3`)), DisableFlagsInUseLine: true, PreRunE: func(cmd *cobra.Command, args []string) error { return o.Complete() diff --git a/pkg/create/certificaterequest/certificaterequest.go b/pkg/create/certificaterequest/certificaterequest.go index 8121612..af3737f 100644 --- a/pkg/create/certificaterequest/certificaterequest.go +++ b/pkg/create/certificaterequest/certificaterequest.go @@ -36,35 +36,12 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "github.com/cert-manager/cmctl/v2/pkg/build" "github.com/cert-manager/cmctl/v2/pkg/factory" ) -var ( - long = templates.LongDesc(i18n.T(` -Create a new CertificateRequest resource based on a Certificate resource, by generating a private key locally and create a 'certificate signing request' to be submitted to a cert-manager Issuer.`)) - - example = templates.Examples(i18n.T(build.WithTemplate(` -# Create a CertificateRequest with the name 'my-cr', saving the private key in a file named 'my-cr.key'. -{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml - -# Create a CertificateRequest in namespace default, provided no conflict with namespace defined in file. -{{.BuildName}} create certificaterequest my-cr --namespace default --from-certificate-file my-certificate.yaml - -# Create a CertificateRequest and store private key in file 'new.key'. -{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --output-key-file new.key - -# Create a CertificateRequest, wait for it to be signed for up to 5 minutes (default) and store the x509 certificate in file 'new.crt'. -{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --output-cert-file new.crt - -# Create a CertificateRequest, wait for it to be signed for up to 20 minutes and store the x509 certificate in file 'my-cr.crt'. -{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --timeout 20m -`))) -) - var ( // Dedicated scheme used by the ctl tool that has the internal cert-manager types, // and their conversion functions registered @@ -106,11 +83,27 @@ func NewCmdCreateCR(setupCtx context.Context, ioStreams genericclioptions.IOStre o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "certificaterequest", - Aliases: []string{"cr"}, - Short: "Create a cert-manager CertificateRequest resource, using a Certificate resource as a template", - Long: long, - Example: example, + Use: "certificaterequest", + Aliases: []string{"cr"}, + Short: "Create a cert-manager CertificateRequest resource, using a Certificate resource as a template", + Long: templates.LongDesc(` +Create a new CertificateRequest resource based on a Certificate resource, by generating a private key locally and create a 'certificate signing request' to be submitted to a cert-manager Issuer.`), + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Create a CertificateRequest with the name 'my-cr', saving the private key in a file named 'my-cr.key'. +{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml + +# Create a CertificateRequest in namespace default, provided no conflict with namespace defined in file. +{{.BuildName}} create certificaterequest my-cr --namespace default --from-certificate-file my-certificate.yaml + +# Create a CertificateRequest and store private key in file 'new.key'. +{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --output-key-file new.key + +# Create a CertificateRequest, wait for it to be signed for up to 5 minutes (default) and store the x509 certificate in file 'new.crt'. +{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --output-cert-file new.crt + +# Create a CertificateRequest, wait for it to be signed for up to 20 minutes and store the x509 certificate in file 'my-cr.crt'. +{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --timeout 20m +`)), ValidArgsFunction: factory.ValidArgsListCertificateRequests(&o.Factory), PreRunE: func(cmd *cobra.Command, args []string) error { return o.Validate(args) diff --git a/pkg/create/certificatesigningrequest/certificatesigningrequest.go b/pkg/create/certificatesigningrequest/certificatesigningrequest.go index e2195ac..b02ae50 100644 --- a/pkg/create/certificatesigningrequest/certificatesigningrequest.go +++ b/pkg/create/certificatesigningrequest/certificatesigningrequest.go @@ -40,35 +40,12 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/discovery" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "github.com/cert-manager/cmctl/v2/pkg/build" "github.com/cert-manager/cmctl/v2/pkg/factory" ) -var ( - long = templates.LongDesc(i18n.T(` -Experimental. Only supported for Kubernetes versions 1.19+. Requires -cert-manager versions 1.4+ with experimental controllers enabled. - -Create a new CertificateSigningRequest resource based on a Certificate resource, by generating a private key locally and create a 'certificate signing request' to be submitted to a cert-manager Issuer.`)) - - example = templates.Examples(i18n.T(build.WithTemplate(` -# Create a CertificateSigningRequest with the name 'my-csr', saving the private key in a file named 'my-cr.key'. -{{.BuildName}} x create certificatesigningrequest my-csr --from-certificate-file my-certificate.yaml - -# Create a CertificateSigningRequest and store private key in file 'new.key'. -{{.BuildName}} x create certificatesigningrequest my-csr --from-certificate-file my-certificate.yaml --output-key-file new.key - -# Create a CertificateSigningRequest, wait for it to be signed for up to 5 minutes (default) and store the x509 certificate in file 'new.crt'. -{{.BuildName}} x create csr my-cr -f my-certificate.yaml -c new.crt -w - -# Create a CertificateSigningRequest, wait for it to be signed for up to 20 minutes and store the x509 certificate in file 'my-cr.crt'. -{{.BuildName}} x create csr my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --timeout 20m -`))) -) - var ( // Dedicated scheme used by the ctl tool that has the internal cert-manager types, // and their conversion functions registered @@ -118,11 +95,27 @@ func NewCmdCreateCSR(setupCtx context.Context, ioStreams genericclioptions.IOStr o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "certificatesigningrequest", - Aliases: []string{"csr"}, - Short: "Create a Kubernetes CertificateSigningRequest resource, using a Certificate resource as a template", - Long: long, - Example: example, + Use: "certificatesigningrequest", + Aliases: []string{"csr"}, + Short: "Create a Kubernetes CertificateSigningRequest resource, using a Certificate resource as a template", + Long: templates.LongDesc(` +Experimental. Only supported for Kubernetes versions 1.19+. Requires +cert-manager versions 1.4+ with experimental controllers enabled. + +Create a new CertificateSigningRequest resource based on a Certificate resource, by generating a private key locally and create a 'certificate signing request' to be submitted to a cert-manager Issuer.`), + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Create a CertificateSigningRequest with the name 'my-csr', saving the private key in a file named 'my-cr.key'. +{{.BuildName}} x create certificatesigningrequest my-csr --from-certificate-file my-certificate.yaml + +# Create a CertificateSigningRequest and store private key in file 'new.key'. +{{.BuildName}} x create certificatesigningrequest my-csr --from-certificate-file my-certificate.yaml --output-key-file new.key + +# Create a CertificateSigningRequest, wait for it to be signed for up to 5 minutes (default) and store the x509 certificate in file 'new.crt'. +{{.BuildName}} x create csr my-cr -f my-certificate.yaml -c new.crt -w + +# Create a CertificateSigningRequest, wait for it to be signed for up to 20 minutes and store the x509 certificate in file 'my-cr.crt'. +{{.BuildName}} x create csr my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --timeout 20m +`)), ValidArgsFunction: factory.ValidArgsListCertificateSigningRequests(&o.Factory), PreRunE: func(cmd *cobra.Command, args []string) error { return o.Validate(args) diff --git a/pkg/deny/deny.go b/pkg/deny/deny.go index c08f83f..54f0d93 100644 --- a/pkg/deny/deny.go +++ b/pkg/deny/deny.go @@ -27,26 +27,12 @@ import ( "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "github.com/cert-manager/cmctl/v2/pkg/build" "github.com/cert-manager/cmctl/v2/pkg/factory" ) -var ( - example = templates.Examples(i18n.T(build.WithTemplate(` -# Deny a CertificateRequest with the name 'my-cr' -{{.BuildName}} deny my-cr - -# Deny a CertificateRequest in namespace default -{{.BuildName}} deny my-cr --namespace default - -# Deny a CertificateRequest giving a custom reason and message -{{.BuildName}} deny my-cr --reason "ManualDenial" --reason "Denied by PKI department" -`))) -) - // Options is a struct to support create certificaterequest command type Options struct { // Reason is the string that will be set on the Reason field of the Denied @@ -71,10 +57,19 @@ func NewCmdDeny(setupCtx context.Context, ioStreams genericclioptions.IOStreams) o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "deny", - Short: "Deny a CertificateRequest", - Long: `Mark a CertificateRequest as Denied, so it may never be signed by a configured Issuer.`, - Example: example, + Use: "deny", + Short: "Deny a CertificateRequest", + Long: `Mark a CertificateRequest as Denied, so it may never be signed by a configured Issuer.`, + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Deny a CertificateRequest with the name 'my-cr' +{{.BuildName}} deny my-cr + +# Deny a CertificateRequest in namespace default +{{.BuildName}} deny my-cr --namespace default + +# Deny a CertificateRequest giving a custom reason and message +{{.BuildName}} deny my-cr --reason "ManualDenial" --reason "Denied by PKI department" +`)), ValidArgsFunction: factory.ValidArgsListCertificateRequests(&o.Factory), PreRunE: func(cmd *cobra.Command, args []string) error { return o.Validate(args) @@ -86,7 +81,7 @@ func NewCmdDeny(setupCtx context.Context, ioStreams genericclioptions.IOStreams) cmd.Flags().StringVar(&o.Reason, "reason", "KubectlCertManager", "The reason to give as to what denied this CertificateRequest.") - cmd.Flags().StringVar(&o.Message, "message", fmt.Sprintf("manually denied by %q", build.Name()), + cmd.Flags().StringVar(&o.Message, "message", fmt.Sprintf("manually denied by %q", build.Name(setupCtx)), "The message to give as to why this CertificateRequest was denied.") o.Factory = factory.New(cmd) diff --git a/pkg/inspect/secret/secret.go b/pkg/inspect/secret/secret.go index ac99988..a62c4f1 100644 --- a/pkg/inspect/secret/secret.go +++ b/pkg/inspect/secret/secret.go @@ -34,7 +34,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" k8sclock "k8s.io/utils/clock" @@ -81,16 +80,6 @@ const debuggingTemplate = `Debugging: CRL Status: {{ .CRLStatus }} OCSP Status: {{ .OCSPStatus }}` -var ( - long = templates.LongDesc(i18n.T(` -Get details about a kubernetes.io/tls typed secret`)) - - example = templates.Examples(i18n.T(build.WithTemplate(` -# Query information about a secret with name 'my-crt' in namespace 'my-namespace' -{{.BuildName}} inspect secret my-crt --namespace my-namespace -`))) -) - // Options is a struct to support status certificate command type Options struct { genericclioptions.IOStreams @@ -109,10 +98,14 @@ func NewCmdInspectSecret(setupCtx context.Context, ioStreams genericclioptions.I o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "secret", - Short: "Get details about a kubernetes.io/tls typed secret", - Long: long, - Example: example, + Use: "secret", + Short: "Get details about a kubernetes.io/tls typed secret", + Long: templates.LongDesc(` +Get details about a kubernetes.io/tls typed secret`), + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Query information about a secret with name 'my-crt' in namespace 'my-namespace' +{{.BuildName}} inspect secret my-crt --namespace my-namespace +`)), ValidArgsFunction: factory.ValidArgsListSecrets(&o.Factory), PreRunE: func(cmd *cobra.Command, args []string) error { return o.Validate(args) diff --git a/pkg/install/install.go b/pkg/install/install.go index 1ecf116..ecd84c2 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -54,8 +54,8 @@ const ( installCRDsFlagName = "installCRDs" ) -func installDesc() string { - return build.WithTemplate(`This command installs cert-manager. It uses the Helm libraries to do so. +func installDesc(ctx context.Context) string { + return build.WithTemplate(ctx, `This command installs cert-manager. It uses the Helm libraries to do so. The latest published cert-manager chart in the "https://charts.jetstack.io" repo is used. Most of the features supported by 'helm install' are also supported by this command. @@ -89,7 +89,7 @@ func NewCmdInstall(setupCtx context.Context, ioStreams genericclioptions.IOStrea cmd := &cobra.Command{ Use: "install", Short: "Install cert-manager", - Long: installDesc(), + Long: installDesc(setupCtx), RunE: func(cmd *cobra.Command, args []string) error { options.client.Namespace = settings.Namespace() diff --git a/pkg/renew/renew.go b/pkg/renew/renew.go index 1b71ae9..1249b78 100644 --- a/pkg/renew/renew.go +++ b/pkg/renew/renew.go @@ -32,28 +32,12 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes" cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "github.com/cert-manager/cmctl/v2/pkg/build" "github.com/cert-manager/cmctl/v2/pkg/factory" ) -var ( - long = templates.LongDesc(i18n.T(` -Mark cert-manager Certificate resources for manual renewal.`)) - - example = templates.Examples(i18n.T(build.WithTemplate(` -# Renew the Certificates named 'my-app' and 'vault' in the current context namespace. -{{.BuildName}} renew my-app vault - -# Renew all Certificates in the 'kube-system' namespace. -{{.BuildName}} renew --namespace kube-system --all - -# Renew all Certificates in all namespaces, provided those Certificates have the label 'app=my-service' -{{.BuildName}} renew --all-namespaces -l app=my-service`))) -) - // Options is a struct to support renew command type Options struct { LabelSelector string @@ -75,10 +59,19 @@ func NewOptions(ioStreams genericclioptions.IOStreams) *Options { func NewCmdRenew(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "renew", - Short: "Mark a Certificate for manual renewal", - Long: long, - Example: example, + Use: "renew", + Short: "Mark a Certificate for manual renewal", + Long: templates.LongDesc(` +Mark cert-manager Certificate resources for manual renewal.`), + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Renew the Certificates named 'my-app' and 'vault' in the current context namespace. +{{.BuildName}} renew my-app vault + +# Renew all Certificates in the 'kube-system' namespace. +{{.BuildName}} renew --namespace kube-system --all + +# Renew all Certificates in all namespaces, provided those Certificates have the label 'app=my-service' +{{.BuildName}} renew --all-namespaces -l app=my-service`)), ValidArgsFunction: factory.ValidArgsListCertificates(&o.Factory), PreRunE: func(cmd *cobra.Command, args []string) error { return o.Validate(cmd, args) diff --git a/pkg/status/certificate/certificate.go b/pkg/status/certificate/certificate.go index 7db08b4..e84d906 100644 --- a/pkg/status/certificate/certificate.go +++ b/pkg/status/certificate/certificate.go @@ -33,23 +33,12 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/reference" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "github.com/cert-manager/cmctl/v2/pkg/build" "github.com/cert-manager/cmctl/v2/pkg/factory" ) -var ( - long = templates.LongDesc(i18n.T(` -Get details about the current status of a cert-manager Certificate resource, including information on related resources like CertificateRequest or Order.`)) - - example = templates.Examples(i18n.T(build.WithTemplate(` -# Query status of Certificate with name 'my-crt' in namespace 'my-namespace' -{{.BuildName}} status certificate my-crt --namespace my-namespace -`))) -) - // Options is a struct to support status certificate command type Options struct { genericclioptions.IOStreams @@ -88,10 +77,14 @@ func NewCmdStatusCert(setupCtx context.Context, ioStreams genericclioptions.IOSt o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "certificate", - Short: "Get details about the current status of a cert-manager Certificate resource", - Long: long, - Example: example, + Use: "certificate", + Short: "Get details about the current status of a cert-manager Certificate resource", + Long: templates.LongDesc(` +Get details about the current status of a cert-manager Certificate resource, including information on related resources like CertificateRequest or Order.`), + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Query status of Certificate with name 'my-crt' in namespace 'my-namespace' +{{.BuildName}} status certificate my-crt --namespace my-namespace +`)), ValidArgsFunction: factory.ValidArgsListCertificates(&o.Factory), PreRunE: func(cmd *cobra.Command, args []string) error { return o.Validate(args) diff --git a/pkg/uninstall/uninstall.go b/pkg/uninstall/uninstall.go index d534ba6..0a84fa9 100644 --- a/pkg/uninstall/uninstall.go +++ b/pkg/uninstall/uninstall.go @@ -53,8 +53,8 @@ const ( releaseName = "cert-manager" ) -func description() string { - return build.WithTemplate(`This command safely uninstalls any Helm-managed release of cert-manager. +func description(ctx context.Context) string { + return build.WithTemplate(ctx, `This command safely uninstalls any Helm-managed release of cert-manager. This command is safe because it will not delete any of the cert-manager CRDs even if they were installed as part of the Helm release. This is to avoid accidentally deleting CRDs and custom resources. @@ -86,7 +86,7 @@ func NewCmd(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *co cmd := &cobra.Command{ Use: "uninstall", Short: "Uninstall cert-manager", - Long: description(), + Long: description(setupCtx), RunE: func(cmd *cobra.Command, args []string) error { res, err := run(cmd.Context(), options) if err != nil { diff --git a/pkg/upgrade/migrateapiversion/command.go b/pkg/upgrade/migrateapiversion/command.go index f2a4121..568ad7c 100644 --- a/pkg/upgrade/migrateapiversion/command.go +++ b/pkg/upgrade/migrateapiversion/command.go @@ -23,7 +23,6 @@ import ( apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" "sigs.k8s.io/controller-runtime/pkg/client" @@ -31,27 +30,6 @@ import ( "github.com/cert-manager/cmctl/v2/pkg/factory" ) -var ( - long = templates.LongDesc(i18n.T(` -Ensures resources in your Kubernetes cluster are persisted in the v1 API version. - -This must be run prior to upgrading to ensure your cluster is ready to upgrade to cert-manager v1.7 and beyond. - -This command must be run with a cluster running cert-manager v1.0 or greater.`)) - - example = templates.Examples(i18n.T(build.WithTemplate(` -# Check the cert-manager installation is ready to be upgraded to v1.7 and perform necessary migrations -# to ensure that the kube-apiserver has stored only v1 API versions. -{{.BuildName}} upgrade migrate-api-version - -# Force migrations to be run, even if the 'status.storedVersion' field on the CRDs does not contain -# old, deprecated API versions. -# This should only be used if you have manually edited/patched the CRDs already. -# It will force a read and a write of ALL cert-manager resources unconditionally. -{{.BuildName}} upgrade migrate-api-version --skip-stored-version-check -`))) -) - // Options is a struct to support renew command type Options struct { genericclioptions.IOStreams @@ -75,10 +53,25 @@ func NewOptions(ioStreams genericclioptions.IOStreams) *Options { func NewCmdMigrate(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command { o := NewOptions(ioStreams) cmd := &cobra.Command{ - Use: "migrate-api-version", - Short: "Migrate all existing persisted cert-manager resources to the v1 API version", - Long: long, - Example: example, + Use: "migrate-api-version", + Short: "Migrate all existing persisted cert-manager resources to the v1 API version", + Long: templates.LongDesc(` +Ensures resources in your Kubernetes cluster are persisted in the v1 API version. + +This must be run prior to upgrading to ensure your cluster is ready to upgrade to cert-manager v1.7 and beyond. + +This command must be run with a cluster running cert-manager v1.0 or greater.`), + Example: templates.Examples(build.WithTemplate(setupCtx, ` +# Check the cert-manager installation is ready to be upgraded to v1.7 and perform necessary migrations +# to ensure that the kube-apiserver has stored only v1 API versions. +{{.BuildName}} upgrade migrate-api-version + +# Force migrations to be run, even if the 'status.storedVersion' field on the CRDs does not contain +# old, deprecated API versions. +# This should only be used if you have manually edited/patched the CRDs already. +# It will force a read and a write of ALL cert-manager resources unconditionally. +{{.BuildName}} upgrade migrate-api-version --skip-stored-version-check +`)), PreRunE: func(cmd *cobra.Command, args []string) error { if err := o.Validate(args); err != nil { return err diff --git a/pkg/version/version.go b/pkg/version/version.go index 141a6af..c76ee09 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -64,8 +64,8 @@ func NewOptions(ioStreams genericclioptions.IOStreams) *Options { } } -func versionLong() string { - return build.WithTemplate(`Print the cert-manager CLI version and the deployed cert-manager version. +func versionLong(ctx context.Context) string { + return build.WithTemplate(ctx, `Print the cert-manager CLI version and the deployed cert-manager version. The CLI version is embedded in the binary and directly displayed. Determining the deployed cert-manager version is done by querying the cert-manger resources. First, the tool looks at the labels of the cert-manager CRD @@ -97,7 +97,7 @@ func NewCmdVersion(setupCtx context.Context, ioStreams genericclioptions.IOStrea cmd := &cobra.Command{ Use: "version", Short: "Print the cert-manager CLI version and the deployed cert-manager version", - Long: versionLong(), + Long: versionLong(setupCtx), PreRunE: func(cmd *cobra.Command, args []string) error { if err := o.Validate(); err != nil { return err