From bae35d8adbfe828eca92dfb80c47b71335fecb95 Mon Sep 17 00:00:00 2001 From: joshvanl Date: Wed, 25 Aug 2021 17:29:10 +0100 Subject: [PATCH] Add ValidArgs functions to all commands to allow auto-completion of runtime object names Signed-off-by: joshvanl --- cmd/ctl/pkg/approve/approve.go | 11 +- cmd/ctl/pkg/check/api/api.go | 2 +- .../certificaterequest/certificaterequest.go | 13 +- .../certificatesigningrequest.go | 13 +- cmd/ctl/pkg/deny/deny.go | 11 +- cmd/ctl/pkg/factory/BUILD.bazel | 6 +- cmd/ctl/pkg/factory/factory.go | 5 +- cmd/ctl/pkg/factory/validargs.go | 152 ++++++++++++++++++ cmd/ctl/pkg/inspect/secret/secret.go | 11 +- cmd/ctl/pkg/renew/renew.go | 11 +- cmd/ctl/pkg/status/certificate/certificate.go | 11 +- cmd/ctl/pkg/version/version.go | 2 +- 12 files changed, 207 insertions(+), 41 deletions(-) create mode 100644 cmd/ctl/pkg/factory/validargs.go diff --git a/cmd/ctl/pkg/approve/approve.go b/cmd/ctl/pkg/approve/approve.go index e8c80f1b0..83a0d5a32 100644 --- a/cmd/ctl/pkg/approve/approve.go +++ b/cmd/ctl/pkg/approve/approve.go @@ -71,10 +71,11 @@ func NewCmdApprove(ctx context.Context, ioStreams genericclioptions.IOStreams) * 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: example, + ValidArgsFunction: factory.ValidArgsListCertificateRequests(ctx, &o.Factory), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Validate(args)) cmdutil.CheckErr(o.Run(ctx, args)) @@ -86,7 +87,7 @@ func NewCmdApprove(ctx context.Context, ioStreams genericclioptions.IOStreams) * cmd.Flags().StringVar(&o.Message, "message", `manually approved by "kubectl cert-manager"`, "The message to give as to why this CertificateRequest was approved.") - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/check/api/api.go b/cmd/ctl/pkg/check/api/api.go index 6a2dc96bd..efca3252b 100644 --- a/cmd/ctl/pkg/check/api/api.go +++ b/cmd/ctl/pkg/check/api/api.go @@ -106,7 +106,7 @@ func NewCmdCheckApi(ctx context.Context, ioStreams genericclioptions.IOStreams) cmd.Flags().DurationVar(&o.Interval, "interval", 5*time.Second, "Time between checks when waiting, must include unit, e.g. 1m or 10m") cmd.Flags().BoolVarP(&o.Verbose, "verbose", "v", false, "Print detailed error messages") - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/create/certificaterequest/certificaterequest.go b/cmd/ctl/pkg/create/certificaterequest/certificaterequest.go index 38204ee8b..92d259dde 100644 --- a/cmd/ctl/pkg/create/certificaterequest/certificaterequest.go +++ b/cmd/ctl/pkg/create/certificaterequest/certificaterequest.go @@ -106,11 +106,12 @@ func NewCmdCreateCR(ctx context.Context, ioStreams genericclioptions.IOStreams) 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: long, + Example: example, + ValidArgsFunction: factory.ValidArgsListCertificateRequests(ctx, &o.Factory), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Validate(args)) cmdutil.CheckErr(o.Run(ctx, args)) @@ -127,7 +128,7 @@ func NewCmdCreateCR(ctx context.Context, ioStreams genericclioptions.IOStreams) cmd.Flags().DurationVar(&o.Timeout, "timeout", 5*time.Minute, "Time before timeout when waiting for CertificateRequest to be signed, must include unit, e.g. 10m or 1h") - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/create/certificatesigningrequest/certificatesigningrequest.go b/cmd/ctl/pkg/create/certificatesigningrequest/certificatesigningrequest.go index 0ec7eab82..a952cb211 100644 --- a/cmd/ctl/pkg/create/certificatesigningrequest/certificatesigningrequest.go +++ b/cmd/ctl/pkg/create/certificatesigningrequest/certificatesigningrequest.go @@ -118,11 +118,12 @@ func NewCmdCreateCSR(ctx context.Context, ioStreams genericclioptions.IOStreams) 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: long, + Example: example, + ValidArgsFunction: factory.ValidArgsListCertificateSigningRequests(ctx, &o.Factory), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Validate(args)) cmdutil.CheckErr(o.Run(ctx, args)) @@ -139,7 +140,7 @@ func NewCmdCreateCSR(ctx context.Context, ioStreams genericclioptions.IOStreams) cmd.Flags().DurationVar(&o.Timeout, "timeout", 5*time.Minute, "Time before timeout when waiting for CertificateSigningRequest to be signed, must include unit, e.g. 10m or 1h") - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/deny/deny.go b/cmd/ctl/pkg/deny/deny.go index c47c35085..6edef80ee 100644 --- a/cmd/ctl/pkg/deny/deny.go +++ b/cmd/ctl/pkg/deny/deny.go @@ -71,10 +71,11 @@ func NewCmdDeny(ctx context.Context, ioStreams genericclioptions.IOStreams) *cob 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: example, + ValidArgsFunction: factory.ValidArgsListCertificateRequests(ctx, &o.Factory), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Validate(args)) cmdutil.CheckErr(o.Run(ctx, args)) @@ -86,7 +87,7 @@ func NewCmdDeny(ctx context.Context, ioStreams genericclioptions.IOStreams) *cob cmd.Flags().StringVar(&o.Message, "message", `manually denied by "kubectl cert-manager"`, "The message to give as to why this CertificateRequest was denied.") - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/factory/BUILD.bazel b/cmd/ctl/pkg/factory/BUILD.bazel index fead217dd..9e74d569f 100644 --- a/cmd/ctl/pkg/factory/BUILD.bazel +++ b/cmd/ctl/pkg/factory/BUILD.bazel @@ -2,12 +2,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", - srcs = ["factory.go"], + srcs = [ + "factory.go", + "validargs.go", + ], importpath = "github.com/jetstack/cert-manager/cmd/ctl/pkg/factory", visibility = ["//visibility:public"], deps = [ "//pkg/client/clientset/versioned:go_default_library", "@com_github_spf13_cobra//:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_cli_runtime//pkg/genericclioptions:go_default_library", "@io_k8s_client_go//kubernetes:go_default_library", "@io_k8s_client_go//plugin/pkg/client/auth:go_default_library", diff --git a/cmd/ctl/pkg/factory/factory.go b/cmd/ctl/pkg/factory/factory.go index 5cb37dc97..b50dd45d0 100644 --- a/cmd/ctl/pkg/factory/factory.go +++ b/cmd/ctl/pkg/factory/factory.go @@ -17,6 +17,8 @@ limitations under the License. package factory import ( + "context" + "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes" @@ -63,10 +65,11 @@ type Factory struct { // populated when the command is executed using the cobra PreRun. If a PreRun // is already defined, it will be executed _after_ Factory has been populated, // making it available. -func New(cmd *cobra.Command) *Factory { +func New(ctx context.Context, cmd *cobra.Command) *Factory { f := new(Factory) kubeConfigFlags.AddFlags(cmd.Flags()) + cmd.RegisterFlagCompletionFunc("namespace", validArgsListNamespaces(ctx, f)) // Setup a PreRun to populate the Factory. Catch the existing PreRun command // if one was defined, and execute it second. diff --git a/cmd/ctl/pkg/factory/validargs.go b/cmd/ctl/pkg/factory/validargs.go new file mode 100644 index 000000000..9fd6b9e86 --- /dev/null +++ b/cmd/ctl/pkg/factory/validargs.go @@ -0,0 +1,152 @@ +/* +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 factory + +import ( + "context" + + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ValidArgsCertificates returns a cobra ValidArgsFunction for listing Certificates. +func ValidArgsListCertificates(ctx context.Context, factory **Factory) func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + return func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + f := (*factory) + if err := f.complete(); err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + certList, err := f.CMClient.CertmanagerV1().Certificates(f.Namespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var names []string + for _, cert := range certList.Items { + names = append(names, cert.Name) + } + + return names, cobra.ShellCompDirectiveNoFileComp + } +} + +// ValidArgsSecrets returns a cobra ValidArgsFunction for listing Secrets. +func ValidArgsListSecrets(ctx context.Context, factory **Factory) func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + return func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + f := (*factory) + if err := f.complete(); err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + secretsList, err := f.KubeClient.CoreV1().Secrets(f.Namespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var names []string + for _, secret := range secretsList.Items { + names = append(names, secret.Name) + } + + return names, cobra.ShellCompDirectiveNoFileComp + } +} + +// ValidArgsCertificateSigningRequests returns a cobra ValidArgsFunction for +// listing CertificateSigningRequests. +func ValidArgsListCertificateSigningRequests(ctx context.Context, factory **Factory) func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + return func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + f := (*factory) + if err := f.complete(); err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + csrList, err := f.KubeClient.CertificatesV1().CertificateSigningRequests().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var names []string + for _, csr := range csrList.Items { + names = append(names, csr.Name) + } + + return names, cobra.ShellCompDirectiveNoFileComp + } +} + +// ValidArgsCertificateRequests returns a cobra ValidArgsFunction for listing +// CertificateRequests. +func ValidArgsListCertificateRequests(ctx context.Context, factory **Factory) func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + return func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + f := (*factory) + if err := f.complete(); err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + crList, err := f.CMClient.CertmanagerV1().CertificateRequests(f.Namespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var names []string + for _, cr := range crList.Items { + names = append(names, cr.Name) + } + return names, cobra.ShellCompDirectiveNoFileComp + } +} + +// validArgsListNamespaces returns a cobra ValidArgsFunction for listing +// namespaces. +func validArgsListNamespaces(ctx context.Context, factory *Factory) func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + return func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + if err := factory.complete(); err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + namespaceList, err := factory.KubeClient.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var names []string + for _, namespace := range namespaceList.Items { + names = append(names, namespace.Name) + } + + return names, cobra.ShellCompDirectiveNoFileComp + } +} diff --git a/cmd/ctl/pkg/inspect/secret/secret.go b/cmd/ctl/pkg/inspect/secret/secret.go index aad0098cd..6745e3210 100644 --- a/cmd/ctl/pkg/inspect/secret/secret.go +++ b/cmd/ctl/pkg/inspect/secret/secret.go @@ -108,17 +108,18 @@ func NewCmdInspectSecret(ctx context.Context, ioStreams genericclioptions.IOStre 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: long, + Example: example, + ValidArgsFunction: factory.ValidArgsListSecrets(ctx, &o.Factory), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Validate(args)) cmdutil.CheckErr(o.Run(ctx, args)) }, } - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/renew/renew.go b/cmd/ctl/pkg/renew/renew.go index b4aaf2a4b..8ff86fe3d 100644 --- a/cmd/ctl/pkg/renew/renew.go +++ b/cmd/ctl/pkg/renew/renew.go @@ -73,10 +73,11 @@ func NewOptions(ioStreams genericclioptions.IOStreams) *Options { func NewCmdRenew(ctx 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: long, + Example: example, + ValidArgsFunction: factory.ValidArgsListCertificates(ctx, &o.Factory), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Validate(cmd, args)) cmdutil.CheckErr(o.Run(ctx, args)) @@ -87,7 +88,7 @@ func NewCmdRenew(ctx context.Context, ioStreams genericclioptions.IOStreams) *co cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, mark Certificates across namespaces for manual renewal. Namespace in current context is ignored even if specified with --namespace.") cmd.Flags().BoolVar(&o.All, "all", o.All, "Renew all Certificates in the given Namespace, or all namespaces with --all-namespaces enabled.") - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/status/certificate/certificate.go b/cmd/ctl/pkg/status/certificate/certificate.go index ac2c8fcd0..936c7264e 100644 --- a/cmd/ctl/pkg/status/certificate/certificate.go +++ b/cmd/ctl/pkg/status/certificate/certificate.go @@ -88,17 +88,18 @@ func NewCmdStatusCert(ctx context.Context, ioStreams genericclioptions.IOStreams 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: long, + Example: example, + ValidArgsFunction: factory.ValidArgsListCertificates(ctx, &o.Factory), Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(o.Validate(args)) cmdutil.CheckErr(o.Run(ctx, args)) }, } - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd } diff --git a/cmd/ctl/pkg/version/version.go b/cmd/ctl/pkg/version/version.go index 8576a141d..fdbb3628f 100644 --- a/cmd/ctl/pkg/version/version.go +++ b/cmd/ctl/pkg/version/version.go @@ -107,7 +107,7 @@ func NewCmdVersion(ctx context.Context, ioStreams genericclioptions.IOStreams) * cmd.Flags().BoolVar(&o.Short, "short", o.Short, "If true, print just the version number.") cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "One of 'yaml' or 'json'.") - o.Factory = factory.New(cmd) + o.Factory = factory.New(ctx, cmd) return cmd }