From f5fa510b524b4a0724f7f3260c7673f77d7051eb Mon Sep 17 00:00:00 2001 From: Jian Qiu Date: Tue, 28 Mar 2023 16:33:49 +0800 Subject: [PATCH] Refactor cluster and clusters flags (#327) Also remove unused flag in legacy kubeconfig flags Signed-off-by: Jian Qiu --- cmd/clusteradm/clusteradm.go | 7 +++- pkg/cmd/accept/cmd.go | 2 +- pkg/cmd/accept/exec.go | 24 ++++--------- pkg/cmd/accept/options.go | 3 +- pkg/cmd/addon/disable/cmd.go | 3 +- pkg/cmd/addon/disable/exec.go | 26 ++++++-------- pkg/cmd/addon/disable/options.go | 6 ++-- pkg/cmd/addon/enable/cmd.go | 2 +- pkg/cmd/addon/enable/exec.go | 8 ++--- pkg/cmd/addon/enable/options.go | 5 +-- pkg/cmd/create/work/cmd.go | 2 +- pkg/cmd/create/work/exec.go | 19 ++++++---- pkg/cmd/create/work/options.go | 6 ++-- pkg/cmd/delete/work/cmd.go | 2 +- pkg/cmd/delete/work/exec.go | 35 +++++++++++------- pkg/cmd/delete/work/options.go | 5 +-- pkg/cmd/get/addon/cmd.go | 3 +- pkg/cmd/get/addon/exec.go | 18 +++++----- pkg/cmd/get/addon/options.go | 4 +-- pkg/cmd/get/work/cmd.go | 3 +- pkg/cmd/get/work/exec.go | 42 +++++++++++----------- pkg/cmd/get/work/options.go | 3 +- pkg/cmd/proxy/health/cmd.go | 3 +- pkg/cmd/proxy/health/exec.go | 8 +++-- pkg/cmd/proxy/health/options.go | 3 +- pkg/cmd/proxy/kubectl/cmd.go | 10 +++--- pkg/cmd/proxy/kubectl/options.go | 7 ++-- pkg/genericclioptions/cluster_flags.go | 50 ++++++++++++++++++++++++++ 28 files changed, 183 insertions(+), 126 deletions(-) create mode 100644 pkg/genericclioptions/cluster_flags.go diff --git a/cmd/clusteradm/clusteradm.go b/cmd/clusteradm/clusteradm.go index bbc5d3dcd..06e22dff8 100644 --- a/cmd/clusteradm/clusteradm.go +++ b/cmd/clusteradm/clusteradm.go @@ -18,6 +18,7 @@ import ( "k8s.io/kubectl/pkg/cmd/plugin" cmdutil "k8s.io/kubectl/pkg/cmd/util" ktemplates "k8s.io/kubectl/pkg/util/templates" + utilpointer "k8s.io/utils/pointer" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -59,7 +60,11 @@ func main() { flags.SetNormalizeFunc(cliflag.WarnWordSepNormalizeFunc) // Warn for "_" flags flags.SetNormalizeFunc(cliflag.WordSepNormalizeFunc) - kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() + //kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() + kubeConfigFlags := &genericclioptions.ConfigFlags{ + KubeConfig: utilpointer.String(""), + Context: utilpointer.String(""), + } kubeConfigFlags.AddFlags(flags) matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags) matchVersionKubeConfigFlags.AddFlags(flags) diff --git a/pkg/cmd/accept/cmd.go b/pkg/cmd/accept/cmd.go index 8904f08da..3fc7937da 100644 --- a/pkg/cmd/accept/cmd.go +++ b/pkg/cmd/accept/cmd.go @@ -47,7 +47,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } - cmd.Flags().StringVar(&o.Clusters, "clusters", "", "Names of the cluster to accept (comma separated)") + o.ClusterOptions.AddFlags(cmd.Flags()) cmd.Flags().BoolVar(&o.Wait, "wait", false, "If set, wait for the managedcluster and CSR in foreground.") cmd.Flags().BoolVar(&o.SkipApproveCheck, "skip-approve-check", false, "If set, then skip check and approve csr directly.") return cmd diff --git a/pkg/cmd/accept/exec.go b/pkg/cmd/accept/exec.go index 34180e227..b78c4a119 100644 --- a/pkg/cmd/accept/exec.go +++ b/pkg/cmd/accept/exec.go @@ -31,28 +31,16 @@ const ( ) func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { - klog.V(1).InfoS("accept options:", "dry-run", o.ClusteradmFlags.DryRun, "clusters", o.Clusters, "wait", o.Wait) - alreadyProvidedCluster := make(map[string]bool) - clusters := make([]string, 0) - if o.Clusters != "" { - cs := strings.Split(o.Clusters, ",") - for _, c := range cs { - if _, ok := alreadyProvidedCluster[c]; !ok { - alreadyProvidedCluster[c] = true - clusters = append(clusters, strings.TrimSpace(c)) - } - } - o.Values.Clusters = clusters - } else { - return fmt.Errorf("values or name are missing") - } - klog.V(3).InfoS("values:", "clusters", o.Values.Clusters) + o.Values.Clusters = o.ClusterOptions.AllClusters().List() + klog.V(1).InfoS("accept options:", "dry-run", o.ClusteradmFlags.DryRun, "clusters", o.Values.Clusters, "wait", o.Wait) return nil } func (o *Options) Validate() error { - err := o.ClusteradmFlags.ValidateHub() - if err != nil { + if err := o.ClusteradmFlags.ValidateHub(); err != nil { + return err + } + if err := o.ClusterOptions.Validate(); err != nil { return err } diff --git a/pkg/cmd/accept/options.go b/pkg/cmd/accept/options.go index df521a289..391509c53 100644 --- a/pkg/cmd/accept/options.go +++ b/pkg/cmd/accept/options.go @@ -10,7 +10,7 @@ type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags //A list of comma separated cluster names - Clusters string + ClusterOptions *genericclioptionsclusteradm.ClusterOption //Wait to wait for managedcluster and CSR Wait bool //If true the csr will approve directly and check of requester will skip. @@ -29,6 +29,7 @@ type Values struct { func NewOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ ClusteradmFlags: clusteradmFlags, + ClusterOptions: genericclioptionsclusteradm.NewClusterOption(), Streams: streams, } } diff --git a/pkg/cmd/addon/disable/cmd.go b/pkg/cmd/addon/disable/cmd.go index 394e90679..6ab31b8ff 100644 --- a/pkg/cmd/addon/disable/cmd.go +++ b/pkg/cmd/addon/disable/cmd.go @@ -66,9 +66,8 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } + o.ClusterOptions.AddFlags(cmd.Flags()) cmd.Flags().StringSliceVar(&o.Names, "names", []string{}, "Names of the add-on to deploy (comma separated)") - cmd.Flags().StringSliceVar(&o.Clusters, "clusters", []string{}, "Names of the managed cluster to deploy the add-on to (comma separated)") - cmd.Flags().BoolVar(&o.Allclusters, "all-clusters", false, "Make all managed clusters to disable the add-on") return cmd } diff --git a/pkg/cmd/addon/disable/exec.go b/pkg/cmd/addon/disable/exec.go index 92e85037b..71871ea72 100644 --- a/pkg/cmd/addon/disable/exec.go +++ b/pkg/cmd/addon/disable/exec.go @@ -18,28 +18,24 @@ import ( "open-cluster-management.io/clusteradm/pkg/helpers" ) -func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { - klog.V(1).InfoS("disable options:", "dry-run", o.ClusteradmFlags.DryRun, "names", o.Names, "clusters", o.Clusters, "all-clusters", o.Allclusters) +func (o *Options) complete(cmd *cobra.Command, args []string) error { + klog.V(1).InfoS("disable options:", "dry-run", o.ClusteradmFlags.DryRun, "names", o.Names, "clusters", o.ClusterOptions.AllClusters()) return nil } -func (o *Options) Validate() (err error) { - err = o.ClusteradmFlags.ValidateHub() - if err != nil { - return err - } +func (o *Options) Validate() error { - if len(o.Names) == 0 { - return fmt.Errorf("names is missing") + if err := o.ClusteradmFlags.ValidateHub(); err != nil { + return err } - if !o.Allclusters && len(o.Clusters) == 0 { - return fmt.Errorf("clusters is missing") + if err := o.ClusterOptions.Validate(); err != nil { + return err } - if o.Allclusters && len(o.Clusters) != 0 { - return fmt.Errorf("flag --all-clusters and --clusters can not be set together") + if len(o.Names) == 0 { + return fmt.Errorf("names is missing") } return nil @@ -68,7 +64,7 @@ func (o *Options) Run() (err error) { addons := sets.NewString(o.Names...) var clusters sets.String - if o.Allclusters { + if o.ClusterOptions.AllClusters().Len() == 0 { clusters = sets.NewString() mcllist, err := clusterClient.ClusterV1().ManagedClusters().List(context.TODO(), metav1.ListOptions{}) @@ -79,7 +75,7 @@ func (o *Options) Run() (err error) { clusters.Insert(item.ObjectMeta.Name) } } else { - clusters = sets.NewString(o.Clusters...) + clusters = o.ClusterOptions.AllClusters() } klog.V(3).InfoS("addon to be disabled with cluster values:", "addon", addons.List(), "clusters", clusters.List()) diff --git a/pkg/cmd/addon/disable/options.go b/pkg/cmd/addon/disable/options.go index ca2fb1b5c..d790d3e61 100644 --- a/pkg/cmd/addon/disable/options.go +++ b/pkg/cmd/addon/disable/options.go @@ -9,14 +9,11 @@ import ( type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags + ClusterOptions *genericclioptionsclusteradm.ClusterOption //A list of comma separated addon names Names []string //The specified namespace for addon to disable Namespace string - //A list of comma separated cluster names - Clusters []string - //A bool value shows whether specified add-on will be disable in all managed clusters. - Allclusters bool Streams genericclioptions.IOStreams } @@ -25,5 +22,6 @@ func NewOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, st return &Options{ ClusteradmFlags: clusteradmFlags, Streams: streams, + ClusterOptions: genericclioptionsclusteradm.NewClusterOption().AllowUnset(), } } diff --git a/pkg/cmd/addon/enable/cmd.go b/pkg/cmd/addon/enable/cmd.go index 52e1e654c..03954b8f3 100644 --- a/pkg/cmd/addon/enable/cmd.go +++ b/pkg/cmd/addon/enable/cmd.go @@ -64,9 +64,9 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } + o.ClusterOptions.AddFlags(cmd.Flags()) cmd.Flags().StringSliceVar(&o.Names, "names", []string{}, "Names of the add-on to deploy (comma separated)") cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "open-cluster-management-agent-addon", "Specified namespace to addon addon") - cmd.Flags().StringSliceVar(&o.Clusters, "clusters", []string{}, "Names of the managed cluster to deploy the add-on to (comma separated)") cmd.Flags().StringVar(&o.OutputFile, "output-file", "", "The generated resources will be copied in the specified file") cmd.Flags().StringSliceVar(&o.Annotate, "annotate", []string{}, "Annotations to add to the ManagedClusterAddon (eg. key1=value1,key2=value2)") diff --git a/pkg/cmd/addon/enable/exec.go b/pkg/cmd/addon/enable/exec.go index 63d53f38a..8d96e4add 100644 --- a/pkg/cmd/addon/enable/exec.go +++ b/pkg/cmd/addon/enable/exec.go @@ -46,7 +46,7 @@ func NewClusterAddonInfo(cn string, o *Options, an string) (ClusterAddonInfo, er } func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { - klog.V(1).InfoS("enable options:", "dry-run", o.ClusteradmFlags.DryRun, "names", o.Names, "clusters", o.Clusters, "output-file", o.OutputFile) + klog.V(1).InfoS("enable options:", "dry-run", o.ClusteradmFlags.DryRun, "names", o.Names, "clusters", o.ClusterOptions.AllClusters().List(), "output-file", o.OutputFile) return nil } @@ -61,8 +61,8 @@ func (o *Options) Validate() (err error) { return fmt.Errorf("names is missing") } - if len(o.Clusters) == 0 { - return fmt.Errorf("clusters is missing") + if err := o.ClusterOptions.Validate(); err != nil { + return err } return nil @@ -70,7 +70,7 @@ func (o *Options) Validate() (err error) { func (o *Options) Run() error { addons := sets.NewString(o.Names...) - clusters := sets.NewString(o.Clusters...) + clusters := o.ClusterOptions.AllClusters() klog.V(3).InfoS("values:", "addon", addons, "clusters", clusters) diff --git a/pkg/cmd/addon/enable/options.go b/pkg/cmd/addon/enable/options.go index 26e5e0d1a..c7439b78b 100644 --- a/pkg/cmd/addon/enable/options.go +++ b/pkg/cmd/addon/enable/options.go @@ -9,12 +9,12 @@ import ( type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags + // ClusterOptions is the option for setting clusters + ClusterOptions *genericclioptionsclusteradm.ClusterOption //A list of comma separated addon names Names []string //The specified namespace for addon to install Namespace string - //A list of comma separated cluster names - Clusters []string //The file to output the resources will be sent to the file. OutputFile string //Annotations to add to the addon @@ -27,5 +27,6 @@ func NewOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, st return &Options{ ClusteradmFlags: clusteradmFlags, Streams: streams, + ClusterOptions: genericclioptionsclusteradm.NewClusterOption(), } } diff --git a/pkg/cmd/create/work/cmd.go b/pkg/cmd/create/work/cmd.go index 11b524c0f..4dac1a5b4 100644 --- a/pkg/cmd/create/work/cmd.go +++ b/pkg/cmd/create/work/cmd.go @@ -59,7 +59,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } - cmd.Flags().StringVar(&o.Cluster, "clusters", "", "Names of the managed cluster to apply work") + o.ClusterOption.AddFlags(cmd.Flags()) cmd.Flags().StringVar(&o.Placement, "placement", "", "Specify an existing placement with format /") cmd.Flags().BoolVar(&o.Overwrite, "overwrite", false, "Overwrite the existing work if it exists already") o.FileNameFlags.AddFlags(cmd.Flags()) diff --git a/pkg/cmd/create/work/exec.go b/pkg/cmd/create/work/exec.go index 4ed523648..68b348799 100644 --- a/pkg/cmd/create/work/exec.go +++ b/pkg/cmd/create/work/exec.go @@ -33,16 +33,20 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { return nil } -func (o *Options) validate() (err error) { - err = o.ClusteradmFlags.ValidateHub() - if err != nil { +func (o *Options) validate() error { + + if err := o.ClusteradmFlags.ValidateHub(); err != nil { + return err + } + if err := o.ClusterOption.Validate(); err != nil { return err } - if len(o.Cluster) == 0 && len(o.Placement) == 0 { + clusters := o.ClusterOption.AllClusters() + if clusters.Len() == 0 && len(o.Placement) == 0 { return fmt.Errorf("--clusters or --placement must be specified") } - if len(o.Cluster) > 0 && len(o.Placement) > 0 { + if clusters.Len() > 0 && len(o.Placement) > 0 { return fmt.Errorf("--clusters and --placement can only specify one") } if len(o.Placement) > 0 && len(strings.Split(o.Placement, "/")) != 2 { @@ -150,8 +154,9 @@ func (o *Options) getClusters(workClient workclientset.Interface, clusterClient } // if define --clusters, return that as addedClusters and no deletedClusters - if len(o.Cluster) > 0 { - return sets.NewString().Insert(o.Cluster), nil, nil + clusters := o.ClusterOption.AllClusters() + if clusters.Len() > 0 { + return clusters, nil, nil } placement, err := o.getPlacement(clusterClient) diff --git a/pkg/cmd/create/work/options.go b/pkg/cmd/create/work/options.go index d34ece114..2af87a3a9 100644 --- a/pkg/cmd/create/work/options.go +++ b/pkg/cmd/create/work/options.go @@ -10,9 +10,9 @@ type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags - Streams genericclioptions.IOStreams + ClusterOption *genericclioptionsclusteradm.ClusterOption - Cluster string + Streams genericclioptions.IOStreams Placement string @@ -27,7 +27,7 @@ func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, st return &Options{ ClusteradmFlags: clusteradmFlags, Streams: streams, - Cluster: "", + ClusterOption: genericclioptionsclusteradm.NewClusterOption().AllowUnset(), FileNameFlags: genericclioptions.FileNameFlags{ Filenames: &[]string{}, Recursive: boolPtr(true), diff --git a/pkg/cmd/delete/work/cmd.go b/pkg/cmd/delete/work/cmd.go index b6526ebd4..d9d66a578 100644 --- a/pkg/cmd/delete/work/cmd.go +++ b/pkg/cmd/delete/work/cmd.go @@ -43,7 +43,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } - cmd.Flags().StringVar(&o.Cluster, "cluster", "", "Name of the managed cluster applied work") + o.ClusterOptions.AddFlags(cmd.Flags()) cmd.Flags().BoolVar(&o.Force, "force", false, "set force flag to enable force delete") return cmd diff --git a/pkg/cmd/delete/work/exec.go b/pkg/cmd/delete/work/exec.go index a34df1626..e79d6aa9b 100644 --- a/pkg/cmd/delete/work/exec.go +++ b/pkg/cmd/delete/work/exec.go @@ -4,6 +4,7 @@ package work import ( "context" "fmt" + utilerrors "k8s.io/apimachinery/pkg/util/errors" "time" "github.com/spf13/cobra" @@ -28,14 +29,14 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { return nil } -func (o *Options) validate() (err error) { - err = o.ClusteradmFlags.ValidateHub() - if err != nil { +func (o *Options) validate() error { + + if err := o.ClusteradmFlags.ValidateHub(); err != nil { return err } - if len(o.Cluster) == 0 { - return fmt.Errorf("the name of the cluster must be specified") + if err := o.ClusterOptions.Validate(); err != nil { + return err } return nil @@ -51,11 +52,19 @@ func (o *Options) run() error { return err } - return o.deleteWork(workClient) + var errs []error + for cluster := range o.ClusterOptions.AllClusters() { + err := o.deleteWork(workClient, cluster) + if err != nil { + errs = append(errs, err) + } + } + + return utilerrors.NewAggregate(errs) } -func (o *Options) deleteWork(workClient *workclientset.Clientset) error { - _, err := workClient.WorkV1().ManifestWorks(o.Cluster).Get(context.TODO(), o.Workname, metav1.GetOptions{}) +func (o *Options) deleteWork(workClient *workclientset.Clientset, cluster string) error { + _, err := workClient.WorkV1().ManifestWorks(cluster).Get(context.TODO(), o.Workname, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { fmt.Fprintf(o.Streams.Out, "work %s not found or is already deleted\n", o.Workname) @@ -76,7 +85,7 @@ func (o *Options) deleteWork(workClient *workclientset.Clientset) error { // watch until clusterset is removed e := helpers.WatchUntil( func() (watch.Interface, error) { - return workClient.WorkV1().ManifestWorks(o.Cluster).Watch(context.TODO(), metav1.ListOptions{}) + return workClient.WorkV1().ManifestWorks(cluster).Watch(context.TODO(), metav1.ListOptions{}) }, func(event watch.Event) bool { return event.Type == watch.Deleted @@ -86,14 +95,14 @@ func (o *Options) deleteWork(workClient *workclientset.Clientset) error { }(errChannel) - err = workClient.WorkV1().ManifestWorks(o.Cluster).Delete(context.TODO(), o.Workname, metav1.DeleteOptions{}) + err = workClient.WorkV1().ManifestWorks(cluster).Delete(context.TODO(), o.Workname, metav1.DeleteOptions{}) if err != nil && !errors.IsNotFound(err) { return err } if o.Force { // check whether work is already deleted, if not, remove the finalizer - work, err := workClient.WorkV1().ManifestWorks(o.Cluster).Get(context.TODO(), o.Workname, metav1.GetOptions{}) + work, err := workClient.WorkV1().ManifestWorks(cluster).Get(context.TODO(), o.Workname, metav1.GetOptions{}) if errors.IsNotFound(err) { fmt.Fprintf(o.Streams.Out, "work %s is deleted\n", o.Workname) return nil @@ -108,7 +117,7 @@ func (o *Options) deleteWork(workClient *workclientset.Clientset) error { if len(work.ObjectMeta.Finalizers) != 0 { work.ObjectMeta.Finalizers = work.ObjectMeta.Finalizers[:0] - _, err = workClient.WorkV1().ManifestWorks(o.Cluster).Update(context.TODO(), work, metav1.UpdateOptions{}) + _, err = workClient.WorkV1().ManifestWorks(cluster).Update(context.TODO(), work, metav1.UpdateOptions{}) if err != nil { return err } @@ -121,6 +130,6 @@ func (o *Options) deleteWork(workClient *workclientset.Clientset) error { return err } - fmt.Fprintf(o.Streams.Out, "work %s in cluster %s is deleted\n", o.Workname, o.Cluster) + fmt.Fprintf(o.Streams.Out, "work %s in cluster %s is deleted\n", o.Workname, cluster) return nil } diff --git a/pkg/cmd/delete/work/options.go b/pkg/cmd/delete/work/options.go index be430af11..a31867828 100644 --- a/pkg/cmd/delete/work/options.go +++ b/pkg/cmd/delete/work/options.go @@ -11,9 +11,9 @@ type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags - Streams genericclioptions.IOStreams + ClusterOptions *genericclioptionsclusteradm.ClusterOption - Cluster string + Streams genericclioptions.IOStreams Workname string @@ -24,5 +24,6 @@ func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, st return &Options{ ClusteradmFlags: clusteradmFlags, Streams: streams, + ClusterOptions: genericclioptionsclusteradm.NewClusterOption(), } } diff --git a/pkg/cmd/get/addon/cmd.go b/pkg/cmd/get/addon/cmd.go index ab7c1a06d..43ed9c3de 100644 --- a/pkg/cmd/get/addon/cmd.go +++ b/pkg/cmd/get/addon/cmd.go @@ -47,9 +47,8 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } - cmd.Flags().StringSliceVar(&o.clusters, "clusters", []string{}, - "Names of the managed cluster to display (comma separated)") o.printer.AddFlag(cmd.Flags()) + o.ClusterOptions.AddFlags(cmd.Flags()) return cmd } diff --git a/pkg/cmd/get/addon/exec.go b/pkg/cmd/get/addon/exec.go index 4dc350963..1e5727ea5 100644 --- a/pkg/cmd/get/addon/exec.go +++ b/pkg/cmd/get/addon/exec.go @@ -22,20 +22,22 @@ import ( func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { o.printer.Competele() - klog.V(1).InfoS("addon options:", "dry-run", o.ClusteradmFlags.DryRun, "clusters", o.clusters) + klog.V(1).InfoS("addon options:", "dry-run", o.ClusteradmFlags.DryRun, "clusters", o.ClusterOptions.AllClusters().List()) o.addons = args return nil } -func (o *Options) validate() (err error) { - err = o.ClusteradmFlags.ValidateHub() - if err != nil { +func (o *Options) validate() error { + if err := o.ClusteradmFlags.ValidateHub(); err != nil { return err } - err = o.printer.Validate() - if err != nil { + if err := o.printer.Validate(); err != nil { + return err + } + + if err := o.ClusterOptions.Validate(); err != nil { return err } @@ -61,7 +63,7 @@ func (o *Options) run() (err error) { } var clusters sets.String - if len(o.clusters) == 0 { + if o.ClusterOptions.AllClusters().Len() == 0 { clusters = sets.NewString() mcllist, err := clusterClient.ClusterV1().ManagedClusters().List(context.TODO(), metav1.ListOptions{}) @@ -73,7 +75,7 @@ func (o *Options) run() (err error) { clusters.Insert(item.ObjectMeta.Name) } } else { - clusters = sets.NewString(o.clusters...) + clusters = o.ClusterOptions.AllClusters() } cmaList, err := addonClient.AddonV1alpha1().ClusterManagementAddOns().List(context.TODO(), metav1.ListOptions{}) diff --git a/pkg/cmd/get/addon/options.go b/pkg/cmd/get/addon/options.go index fa05f8716..9ccc1ee42 100644 --- a/pkg/cmd/get/addon/options.go +++ b/pkg/cmd/get/addon/options.go @@ -12,8 +12,7 @@ import ( type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags - // A list of cluster to show - clusters []string + ClusterOptions *genericclioptionsclusteradm.ClusterOption // A list of addon name to show addons []string @@ -25,6 +24,7 @@ type Options struct { func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ ClusteradmFlags: clusteradmFlags, + ClusterOptions: genericclioptionsclusteradm.NewClusterOption().AllowUnset(), Streams: streams, printer: printer.NewPrinterOption(pntOpt), } diff --git a/pkg/cmd/get/work/cmd.go b/pkg/cmd/get/work/cmd.go index bcedc0d7f..45e2d7880 100644 --- a/pkg/cmd/get/work/cmd.go +++ b/pkg/cmd/get/work/cmd.go @@ -47,9 +47,8 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } - cmd.Flags().StringVar(&o.cluster, "cluster", "", "Names of the managed cluster") - o.printer.AddFlag(cmd.Flags()) + o.ClusterOption.AddFlags(cmd.Flags()) return cmd } diff --git a/pkg/cmd/get/work/exec.go b/pkg/cmd/get/work/exec.go index 6bb09311a..ee78596e9 100644 --- a/pkg/cmd/get/work/exec.go +++ b/pkg/cmd/get/work/exec.go @@ -30,17 +30,14 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { } func (o *Options) validate() (err error) { - err = o.ClusteradmFlags.ValidateHub() - if err != nil { + if err := o.ClusteradmFlags.ValidateHub(); err != nil { return err } - if len(o.cluster) == 0 { - return fmt.Errorf("cluster name must be specified") + if err := o.ClusterOption.Validate(); err != nil { + return err } - - err = o.printer.Validate() - if err != nil { + if err := o.printer.Validate(); err != nil { return err } @@ -61,21 +58,24 @@ func (o *Options) run() (err error) { return err } - _, err = clusterClient.ClusterV1().ManagedClusters().Get(context.TODO(), o.cluster, metav1.GetOptions{}) - if err != nil { - return err - } + clusters := o.ClusterOption.AllClusters() - var workList *workapiv1.ManifestWorkList - if len(o.workName) == 0 { - workList, err = workClient.WorkV1().ManifestWorks(o.cluster).List(context.TODO(), metav1.ListOptions{}) - } else { - workList, err = workClient.WorkV1().ManifestWorks(o.cluster).List(context.TODO(), metav1.ListOptions{ - FieldSelector: fmt.Sprintf("metadata.name=%s", o.workName), - }) - } - if err != nil { - return err + workList := &workapiv1.ManifestWorkList{Items: []workapiv1.ManifestWork{}} + for cluster := range clusters { + _, err = clusterClient.ClusterV1().ManagedClusters().Get(context.TODO(), cluster, metav1.GetOptions{}) + if err != nil { + return err + } + + listOpts := metav1.ListOptions{} + if len(o.workName) > 0 { + listOpts.FieldSelector = fmt.Sprintf("metadata.name=%s", o.workName) + } + works, err := workClient.WorkV1().ManifestWorks(cluster).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return err + } + workList.Items = append(workList.Items, works.Items...) } o.printer.WithTreeConverter(o.convertToTree).WithTableConverter(o.converToTable) diff --git a/pkg/cmd/get/work/options.go b/pkg/cmd/get/work/options.go index 22d52c83f..8d009520f 100644 --- a/pkg/cmd/get/work/options.go +++ b/pkg/cmd/get/work/options.go @@ -13,7 +13,7 @@ type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags //A list of comma separated cluster names - cluster string + ClusterOption *genericclioptionsclusteradm.ClusterOption workName string @@ -27,6 +27,7 @@ func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, st ClusteradmFlags: clusteradmFlags, Streams: streams, printer: printer.NewPrinterOption(pntOpt), + ClusterOption: genericclioptionsclusteradm.NewClusterOption(), } } diff --git a/pkg/cmd/proxy/health/cmd.go b/pkg/cmd/proxy/health/cmd.go index 82c7b0031..41aece728 100644 --- a/pkg/cmd/proxy/health/cmd.go +++ b/pkg/cmd/proxy/health/cmd.go @@ -59,8 +59,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream "Konnectivity proxy server's entry hostname") cmd.Flags().IntVar(&o.proxyServerPort, "proxy-server-port", 8090, "Konnectivity proxy server's entry port") - cmd.Flags().StringArrayVarP(&o.clusters, "clusters", "c", nil, - "The names of the clusters to probe") + o.ClusterOption.AddFlags(cmd.Flags()) return cmd } diff --git a/pkg/cmd/proxy/health/exec.go b/pkg/cmd/proxy/health/exec.go index a10951bd4..f0ed0ddf5 100644 --- a/pkg/cmd/proxy/health/exec.go +++ b/pkg/cmd/proxy/health/exec.go @@ -23,7 +23,6 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8snet "k8s.io/apimachinery/pkg/util/net" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -61,6 +60,9 @@ func (o *Options) validate() error { return errors.New("--proxy-key must be set when in-cluster lookup is disabled") } } + if err := o.ClusterOption.Validate(); err != nil { + return err + } return nil } @@ -152,10 +154,10 @@ func (o *Options) run(streams genericclioptions.IOStreams) error { return errors.Wrapf(err, "failed starting konnectivity proxy") } - probingClusters := sets.NewString(o.clusters...) + probingClusters := o.ClusterOption.AllClusters() w := newWriter(streams) for _, cluster := range managedClusterList.Items { - if len(o.clusters) == 0 || probingClusters.Has(cluster.Name) { + if probingClusters.Len() == 0 || probingClusters.Has(cluster.Name) { if err := o.visit(&w, hubRestConfig, addonClient, tunnel.DialContext, cluster.Name); err != nil { klog.Errorf("An error occurred when requesting: %v", err) } diff --git a/pkg/cmd/proxy/health/options.go b/pkg/cmd/proxy/health/options.go index 45a99b6aa..d8b0c2c66 100644 --- a/pkg/cmd/proxy/health/options.go +++ b/pkg/cmd/proxy/health/options.go @@ -10,8 +10,8 @@ import ( type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags + ClusterOption *genericclioptionsclusteradm.ClusterOption - clusters []string inClusterProxyCertLookup bool proxyClientCACertPath string proxyClientCertPath string @@ -27,5 +27,6 @@ type Options struct { func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ ClusteradmFlags: clusteradmFlags, + ClusterOption: genericclioptionsclusteradm.NewClusterOption().AllowUnset(), } } diff --git a/pkg/cmd/proxy/kubectl/cmd.go b/pkg/cmd/proxy/kubectl/cmd.go index 5184ce98d..1aa1c3e09 100644 --- a/pkg/cmd/proxy/kubectl/cmd.go +++ b/pkg/cmd/proxy/kubectl/cmd.go @@ -69,13 +69,13 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream if err != nil { return err } - _, err = clusterClient.ManagedClusters().Get(context.TODO(), o.cluster, metav1.GetOptions{}) + _, err = clusterClient.ManagedClusters().Get(context.TODO(), o.ClusterOption.Cluster, metav1.GetOptions{}) if err != nil { return err } // Get managedServiceAccount - managedServiceAccountToken, err := getManagedServiceAccountToken(hubRestConfig, o.managedServiceAccount, o.cluster) + managedServiceAccountToken, err := getManagedServiceAccountToken(hubRestConfig, o.managedServiceAccount, o.ClusterOption.Cluster) if err != nil { return err } @@ -102,7 +102,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream // Run a http-proxy-server in goroutine hps, err := newHttpProxyServer( cmd.Context(), - o.cluster, + o.ClusterOption.Cluster, int32(8090), // TODO make it configurable or random later proxyCertificates, ) @@ -115,7 +115,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream } // Configure a customized kubeconfig amd write into /tmp dir with a random name - tmpKubeconfigFilePath, err := genTmpKubeconfig(o.cluster, managedServiceAccountToken) + tmpKubeconfigFilePath, err := genTmpKubeconfig(o.ClusterOption.Cluster, managedServiceAccountToken) if err != nil { return err } @@ -133,7 +133,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } - cmd.Flags().StringVar(&o.cluster, "cluster", "", "The name of the managed cluster") + o.ClusterOption.AddFlags(cmd.Flags()) cmd.Flags().StringVar(&o.managedServiceAccount, "sa", "", "The name of the managedServiceAccount") cmd.Flags().StringVar(&o.kubectlArgs, "args", "", "The arguments to pass to kubectl") diff --git a/pkg/cmd/proxy/kubectl/options.go b/pkg/cmd/proxy/kubectl/options.go index aa65f0c85..7cd09ead7 100644 --- a/pkg/cmd/proxy/kubectl/options.go +++ b/pkg/cmd/proxy/kubectl/options.go @@ -11,7 +11,7 @@ type Options struct { //ClusteradmFlags: The generic options from the clusteradm cli-runtime. ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags - cluster string + ClusterOption *genericclioptionsclusteradm.ClusterOption managedServiceAccount string kubectlArgs string } @@ -19,12 +19,13 @@ type Options struct { func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags) *Options { return &Options{ ClusteradmFlags: clusteradmFlags, + ClusterOption: genericclioptionsclusteradm.NewClusterOption(), } } func (o *Options) validate() error { - if o.cluster == "" { - return errors.Errorf("cluster is required") + if err := o.ClusterOption.Validate(); err != nil { + return err } if o.managedServiceAccount == "" { return errors.Errorf("managedServiceAccount is required") diff --git a/pkg/genericclioptions/cluster_flags.go b/pkg/genericclioptions/cluster_flags.go new file mode 100644 index 000000000..63308dba0 --- /dev/null +++ b/pkg/genericclioptions/cluster_flags.go @@ -0,0 +1,50 @@ +// Copyright Contributors to the Open Cluster Management project +package genericclioptions + +import ( + "fmt" + "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/util/sets" +) + +type ClusterOption struct { + Cluster string + Clusters []string + allowUnset bool +} + +func NewClusterOption() *ClusterOption { + return &ClusterOption{} +} + +func (c *ClusterOption) AllowUnset() *ClusterOption { + c.allowUnset = true + return c +} + +func (c *ClusterOption) AddFlags(flags *pflag.FlagSet) { + flags.StringVarP(&c.Cluster, "cluster", "c", "", "Name of the managed cluster") + flags.StringSliceVar(&c.Clusters, "clusters", []string{}, "A list of the managed clusters.") +} + +func (c *ClusterOption) AllClusters() sets.String { + output := sets.NewString(c.Clusters...) + if len(c.Cluster) != 0 { + output.Insert(c.Cluster) + } + + return output +} + +func (c *ClusterOption) Validate() error { + for _, cluster := range c.Clusters { + if len(cluster) == 0 { + return fmt.Errorf("--clusters cannot be set as an empty value") + } + } + if len(c.Cluster) == 0 && len(c.Clusters) == 0 && !c.allowUnset { + return fmt.Errorf("either --cluster or --clusters needs to be set") + } + + return nil +}