From e7320e8d6424932344a5181aef6097790d35d3f1 Mon Sep 17 00:00:00 2001 From: Dominique Vernier Date: Wed, 9 Jun 2021 10:59:47 -0400 Subject: [PATCH] Made dry-run persistenflag Signed-off-by: Dominique Vernier --- build/run-functional-tests.sh | 4 +- cmd/clusteradm.go | 16 +- go.mod | 1 + pkg/cmd/accept/cmd.go | 10 +- pkg/cmd/accept/exec.go | 10 +- pkg/cmd/accept/options.go | 15 +- pkg/cmd/init/cmd.go | 11 +- pkg/cmd/init/exec.go | 14 +- pkg/cmd/init/options.go | 17 +-- pkg/cmd/join/cmd.go | 10 +- pkg/cmd/join/exec.go | 14 +- pkg/cmd/join/options.go | 16 +- pkg/cmd/version/cmd.go | 8 +- pkg/cmd/version/exec.go | 3 +- pkg/cmd/version/options.go | 11 +- pkg/genericclioptions/clusteradm_flags.go | 28 ++++ pkg/helpers/apply/apply.go | 171 +++++++++++++--------- pkg/helpers/apply/apply_test.go | 2 +- pkg/helpers/cmd.go | 6 + 19 files changed, 218 insertions(+), 149 deletions(-) create mode 100644 pkg/genericclioptions/clusteradm_flags.go diff --git a/build/run-functional-tests.sh b/build/run-functional-tests.sh index 83c39f39a..70fd936b8 100755 --- a/build/run-functional-tests.sh +++ b/build/run-functional-tests.sh @@ -49,10 +49,10 @@ else echo "join command result: " $CMDJOINRESULT fi -echo "Sleep 2 min to stabilize" +echo "Sleep 4 min to stabilize" # we need to wait 2 min but once we will have watch status monitor # we will not need to sleep anymore -sleep 120 +sleep 240 CMDACCEPT=`echo $CMDJOINRESULT | cut -d ':' -f2` CMDACCEPT="$CMDACCEPT c1" diff --git a/cmd/clusteradm.go b/cmd/clusteradm.go index 9f93e2708..29f95eefb 100644 --- a/cmd/clusteradm.go +++ b/cmd/clusteradm.go @@ -3,6 +3,7 @@ package main import ( + "flag" "os" "github.com/spf13/cobra" @@ -19,6 +20,7 @@ import ( acceptclusters "open-cluster-management.io/clusteradm/pkg/cmd/accept" inithub "open-cluster-management.io/clusteradm/pkg/cmd/init" joinhub "open-cluster-management.io/clusteradm/pkg/cmd/join" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" ) func main() { @@ -26,6 +28,7 @@ func main() { configFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(configFlags) f := cmdutil.NewFactory(matchVersionKubeConfigFlags) + clusteradmFlags := genericclioptionsclusteradm.NewClusteradmFlags(f) root := &cobra.Command{ @@ -40,7 +43,10 @@ func main() { // From this point and forward we get warnings on flags that contain "_" separators root.SetGlobalNormalizationFunc(cliflag.WarnWordSepNormalizeFunc) - configFlags.AddFlags(flags) + configFlags.AddFlags(root.PersistentFlags()) + clusteradmFlags.AddFlags(root.PersistentFlags()) + flags.AddGoFlagSet(flag.CommandLine) + root.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), streams)) root.AddCommand(options.NewCmdOptions(streams.Out)) //enable plugin functionality: all `os.Args[0]-` in the $PATH will be available for plugin @@ -51,15 +57,15 @@ func main() { { Message: "General commands:", Commands: []*cobra.Command{ - version.NewCmd(f, streams), + version.NewCmd(clusteradmFlags, streams), }, }, { Message: "Registration commands:", Commands: []*cobra.Command{ - inithub.NewCmd(f, streams), - joinhub.NewCmd(f, streams), - acceptclusters.NewCmd(f, streams), + inithub.NewCmd(clusteradmFlags, streams), + joinhub.NewCmd(clusteradmFlags, streams), + acceptclusters.NewCmd(clusteradmFlags, streams), }, }, } diff --git a/go.mod b/go.mod index 009e8aae4..d4cccc448 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/onsi/gomega v1.10.2 // indirect github.com/openshift/library-go v0.0.0-20210521084623-7392ea9b02ca github.com/spf13/cobra v1.1.3 + github.com/spf13/pflag v1.0.5 google.golang.org/appengine v1.6.6 // indirect k8s.io/api v0.21.1 k8s.io/apiextensions-apiserver v0.21.1 diff --git a/pkg/cmd/accept/cmd.go b/pkg/cmd/accept/cmd.go index fdcf69227..0678b6b8d 100644 --- a/pkg/cmd/accept/cmd.go +++ b/pkg/cmd/accept/cmd.go @@ -4,11 +4,11 @@ package accept import ( "fmt" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" "open-cluster-management.io/clusteradm/pkg/helpers" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" ) var example = ` @@ -17,14 +17,17 @@ var example = ` ` // NewCmd ... -func NewCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - o := newOptions(f, streams) +func NewCmd(clusteradmFlages *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *cobra.Command { + o := newOptions(clusteradmFlages, streams) cmd := &cobra.Command{ Use: "accept", Short: "accept a list of clusters", Example: fmt.Sprintf(example, helpers.GetExampleHeader()), SilenceUsage: true, + PreRun: func(c *cobra.Command, args []string) { + helpers.DryRunMessage(o.ClusteradmFlags.DryRun) + }, RunE: func(c *cobra.Command, args []string) error { if err := o.complete(c, args); err != nil { return err @@ -41,7 +44,6 @@ func NewCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Comma } cmd.Flags().StringVar(&o.clusters, "clusters", "", "Names of the cluster to accept (comma separated)") - cmd.Flags().BoolVar(&o.dryRun, "dry-run", false, "If set the generated resources will be displayed but not applied") return cmd } diff --git a/pkg/cmd/accept/exec.go b/pkg/cmd/accept/exec.go index c5c1c29b6..296a4984b 100644 --- a/pkg/cmd/accept/exec.go +++ b/pkg/cmd/accept/exec.go @@ -46,11 +46,11 @@ func (o *Options) validate() error { } func (o *Options) run() error { - kubeClient, err := o.factory.KubernetesClientSet() + kubeClient, err := o.ClusteradmFlags.KubectlFactory.KubernetesClientSet() if err != nil { return err } - restConfig, err := o.factory.ToRESTConfig() + restConfig, err := o.ClusteradmFlags.KubectlFactory.ToRESTConfig() if err != nil { return err } @@ -105,7 +105,7 @@ func (o *Options) runWithClient(kubeClient *kubernetes.Clientset, clusterClient } if csr != nil { - if !o.dryRun { + if !o.ClusteradmFlags.DryRun { if csr.Status.Conditions == nil { csr.Status.Conditions = make([]certificatesv1.CertificateSigningRequestCondition, 0) } @@ -118,7 +118,7 @@ func (o *Options) runWithClient(kubeClient *kubernetes.Clientset, clusterClient LastUpdateTime: metav1.Now(), }) - kubeClient, err := o.factory.KubernetesClientSet() + kubeClient, err := o.ClusteradmFlags.KubectlFactory.KubernetesClientSet() if err != nil { return err } @@ -139,7 +139,7 @@ func (o *Options) runWithClient(kubeClient *kubernetes.Clientset, clusterClient return err } if !mc.Spec.HubAcceptsClient { - if !o.dryRun { + if !o.ClusteradmFlags.DryRun { mc.Spec.HubAcceptsClient = true _, err = clusterClient.ClusterV1().ManagedClusters().Update(context.TODO(), mc, metav1.UpdateOptions{}) if err != nil { diff --git a/pkg/cmd/accept/options.go b/pkg/cmd/accept/options.go index 1dd2d2cfa..693a6b483 100644 --- a/pkg/cmd/accept/options.go +++ b/pkg/cmd/accept/options.go @@ -2,20 +2,16 @@ package accept import ( - cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/cli-runtime/pkg/genericclioptions" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" ) type Options struct { - //ConfigFlags: The generic options from the kubernetes cli-runtime. - ConfigFlags *genericclioptions.ConfigFlags - factory cmdutil.Factory + //ClusteradmFlags: The generic optiosn from the clusteradm cli-runtime. + ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags //A list of comma separated cluster names clusters string values Values - //if set the resources will be sent to stdout instead of being applied - dryRun bool } //Values: The values used in the template @@ -23,9 +19,8 @@ type Values struct { clusters []string } -func newOptions(f cmdutil.Factory, streams genericclioptions.IOStreams) *Options { +func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ - ConfigFlags: genericclioptions.NewConfigFlags(true), - factory: f, + ClusteradmFlags: clusteradmFlags, } } diff --git a/pkg/cmd/init/cmd.go b/pkg/cmd/init/cmd.go index 5f6287dca..4cc8dbbe8 100644 --- a/pkg/cmd/init/cmd.go +++ b/pkg/cmd/init/cmd.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" ) var example = ` @@ -17,14 +17,17 @@ var example = ` ` // NewCmd ... -func NewCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - o := newOptions(f, streams) +func NewCmd(clusteradmFlages *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *cobra.Command { + o := newOptions(clusteradmFlages, streams) cmd := &cobra.Command{ Use: "init", Short: "init the hub", Example: fmt.Sprintf(example, helpers.GetExampleHeader()), SilenceUsage: true, + PreRun: func(c *cobra.Command, args []string) { + helpers.DryRunMessage(o.ClusteradmFlags.DryRun) + }, RunE: func(c *cobra.Command, args []string) error { if err := o.complete(c, args); err != nil { return err @@ -39,7 +42,7 @@ func NewCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Comma return nil }, } - cmd.Flags().BoolVar(&o.dryRun, "dry-run", false, "If set the generated resources will be displayed but not applied") + cmd.Flags().StringVar(&o.outputFile, "output-file", "", "The generated resources will be copied in the specified file") return cmd diff --git a/pkg/cmd/init/exec.go b/pkg/cmd/init/exec.go index f0a8c82fe..46b416b48 100644 --- a/pkg/cmd/init/exec.go +++ b/pkg/cmd/init/exec.go @@ -35,16 +35,16 @@ func (o *Options) run() error { output := make([]string, 0) reader := scenario.GetScenarioResourcesReader() - kubeClient, err := o.factory.KubernetesClientSet() + kubeClient, err := o.ClusteradmFlags.KubectlFactory.KubernetesClientSet() if err != nil { return err } - dynamicClient, err := o.factory.DynamicClient() + dynamicClient, err := o.ClusteradmFlags.KubectlFactory.DynamicClient() if err != nil { return err } - restConfig, err := o.factory.ToRESTConfig() + restConfig, err := o.ClusteradmFlags.KubectlFactory.ToRESTConfig() if err != nil { return err } @@ -70,19 +70,19 @@ func (o *Options) run() error { "init/service_account.yaml", } - out, err := apply.ApplyDirectly(clientHolder, reader, o.values, o.dryRun, "", files...) + out, err := apply.ApplyDirectly(clientHolder, reader, o.values, o.ClusteradmFlags.DryRun, "", files...) if err != nil { return err } output = append(output, out...) - out, err = apply.ApplyDeployment(kubeClient, reader, o.values, o.dryRun, "", "init/operator.yaml") + out, err = apply.ApplyDeployments(kubeClient, reader, o.values, o.ClusteradmFlags.DryRun, "", "init/operator.yaml") if err != nil { return err } output = append(output, out...) - if !o.dryRun { + if !o.ClusteradmFlags.DryRun { b := retry.DefaultBackoff b.Duration = 100 * time.Millisecond err = helpers.WaitCRDToBeReady(*apiExtensionsClient, "clustermanagers.operator.open-cluster-management.io", b) @@ -92,7 +92,7 @@ func (o *Options) run() error { } discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(restConfig) - out, err = apply.ApplyCustomResouces(dynamicClient, discoveryClient, reader, o.values, o.dryRun, "", "init/clustermanagers.cr.yaml") + out, err = apply.ApplyCustomResouces(dynamicClient, discoveryClient, reader, o.values, o.ClusteradmFlags.DryRun, "", "init/clustermanagers.cr.yaml") if err != nil { return err } diff --git a/pkg/cmd/init/options.go b/pkg/cmd/init/options.go index 9197099bf..dd00de360 100644 --- a/pkg/cmd/init/options.go +++ b/pkg/cmd/init/options.go @@ -3,17 +3,15 @@ package init import ( "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" ) //Options: The structure holding all the command-line options type Options struct { - //ConfigFlags: The generic options from the kubernetes cli-runtime. - ConfigFlags *genericclioptions.ConfigFlags - factory cmdutil.Factory - values Values - //if set the resources will be sent to stdout instead of being applied - dryRun bool + //ClusteradmFlags: The generic optiosn from the clusteradm cli-runtime. + ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags + // factory cmdutil.Factory + values Values //The file to output the resources will be sent to the file. outputFile string } @@ -32,9 +30,8 @@ type Hub struct { TokenSecret string `json:"tokenSecret"` } -func newOptions(f cmdutil.Factory, streams genericclioptions.IOStreams) *Options { +func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ - ConfigFlags: genericclioptions.NewConfigFlags(true), - factory: f, + ClusteradmFlags: clusteradmFlags, } } diff --git a/pkg/cmd/join/cmd.go b/pkg/cmd/join/cmd.go index 3c790efa4..cd6fad8ac 100644 --- a/pkg/cmd/join/cmd.go +++ b/pkg/cmd/join/cmd.go @@ -4,11 +4,11 @@ package join import ( "fmt" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" "open-cluster-management.io/clusteradm/pkg/helpers" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" ) var example = ` @@ -17,14 +17,17 @@ var example = ` ` // NewCmd ... -func NewCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - o := newOptions(f, streams) +func NewCmd(clusteradmFlages *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *cobra.Command { + o := newOptions(clusteradmFlages, streams) cmd := &cobra.Command{ Use: "join", Short: "join a hub", Example: fmt.Sprintf(example, helpers.GetExampleHeader()), SilenceUsage: true, + PreRun: func(c *cobra.Command, args []string) { + helpers.DryRunMessage(o.ClusteradmFlags.DryRun) + }, RunE: func(c *cobra.Command, args []string) error { if err := o.complete(c, args); err != nil { return err @@ -43,7 +46,6 @@ func NewCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Comma cmd.Flags().StringVar(&o.token, "hub-token", "", "The token to access the hub") cmd.Flags().StringVar(&o.hubAPIServer, "hub-apiserver", "", "The api server url to the hub") cmd.Flags().StringVar(&o.clusterName, "cluster-name", "", "The name of the joining cluster") - cmd.Flags().BoolVar(&o.dryRun, "dry-run", false, "If set the generated resources will be displayed but not applied") cmd.Flags().StringVar(&o.outputFile, "output-file", "", "The generated resources will be copied in the specified file") return cmd } diff --git a/pkg/cmd/join/exec.go b/pkg/cmd/join/exec.go index 584087003..7b025b7d5 100644 --- a/pkg/cmd/join/exec.go +++ b/pkg/cmd/join/exec.go @@ -67,16 +67,16 @@ func (o *Options) run() error { return err } - kubeClient, err := o.factory.KubernetesClientSet() + kubeClient, err := o.ClusteradmFlags.KubectlFactory.KubernetesClientSet() if err != nil { return err } - dynamicClient, err := o.factory.DynamicClient() + dynamicClient, err := o.ClusteradmFlags.KubectlFactory.DynamicClient() if err != nil { return err } - restConfig, err := o.factory.ToRESTConfig() + restConfig, err := o.ClusteradmFlags.KubectlFactory.ToRESTConfig() if err != nil { return err } @@ -101,19 +101,19 @@ func (o *Options) run() error { "join/service_account.yaml", } - out, err := apply.ApplyDirectly(clientHolder, reader, o.values, o.dryRun, "", files...) + out, err := apply.ApplyDirectly(clientHolder, reader, o.values, o.ClusteradmFlags.DryRun, "", files...) if err != nil { return err } output = append(output, out...) - out, err = apply.ApplyDeployment(kubeClient, reader, o.values, o.dryRun, "", "join/operator.yaml") + out, err = apply.ApplyDeployments(kubeClient, reader, o.values, o.ClusteradmFlags.DryRun, "", "join/operator.yaml") if err != nil { return err } output = append(output, out...) - if !o.dryRun { + if !o.ClusteradmFlags.DryRun { b := retry.DefaultBackoff b.Duration = 100 * time.Millisecond @@ -124,7 +124,7 @@ func (o *Options) run() error { } discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(restConfig) - out, err = apply.ApplyCustomResouces(dynamicClient, discoveryClient, reader, o.values, o.dryRun, "", "join/klusterlets.cr.yaml") + out, err = apply.ApplyCustomResouces(dynamicClient, discoveryClient, reader, o.values, o.ClusteradmFlags.DryRun, "", "join/klusterlets.cr.yaml") if err != nil { return err } diff --git a/pkg/cmd/join/options.go b/pkg/cmd/join/options.go index 22ddae1ac..f9a7cdd99 100644 --- a/pkg/cmd/join/options.go +++ b/pkg/cmd/join/options.go @@ -3,13 +3,13 @@ package join import ( "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" ) //Options: The structure holding all the command-line options type Options struct { - //ConfigFlags: The generic options from the kubernetes cli-runtime. - ConfigFlags *genericclioptions.ConfigFlags + //ClusteradmFlags: The generic optiosn from the clusteradm cli-runtime. + ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags //The token generated on the hub to access it from the cluster token string //The external hub apiserver url (https://:) @@ -17,10 +17,7 @@ type Options struct { //the name under the cluster must be imported clusterName string - factory cmdutil.Factory - values Values - //if set the resources will be sent to stdout instead of being applied - dryRun bool + values Values //The file to output the resources will be sent to the file. outputFile string } @@ -42,9 +39,8 @@ type Hub struct { KubeConfig string } -func newOptions(f cmdutil.Factory, streams genericclioptions.IOStreams) *Options { +func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ - ConfigFlags: genericclioptions.NewConfigFlags(true), - factory: f, + ClusteradmFlags: clusteradmFlags, } } diff --git a/pkg/cmd/version/cmd.go b/pkg/cmd/version/cmd.go index be47ba9c4..ab5874a41 100644 --- a/pkg/cmd/version/cmd.go +++ b/pkg/cmd/version/cmd.go @@ -4,11 +4,11 @@ package version import ( "fmt" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" "open-cluster-management.io/clusteradm/pkg/helpers" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" ) var example = ` @@ -16,9 +16,9 @@ var example = ` %[1]s version ` -// NewCmd provides a cobra command wrapping NewCmdImportCluster -func NewCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { - o := newOptions(f, streams) +// NewCmd... +func NewCmd(clusteradmFlages *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *cobra.Command { + o := newOptions(clusteradmFlages, streams) cmd := &cobra.Command{ Use: "version", Short: "get the versions of the different components", diff --git a/pkg/cmd/version/exec.go b/pkg/cmd/version/exec.go index 5c7067a4f..b37a429e2 100644 --- a/pkg/cmd/version/exec.go +++ b/pkg/cmd/version/exec.go @@ -15,9 +15,10 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { func (o *Options) validate() error { return nil } + func (o *Options) run() (err error) { fmt.Printf("client\t\tversion\t:%s\n", clusteradm.GetVersion()) - discoveryClient, err := o.factory.ToDiscoveryClient() + discoveryClient, err := o.ClusteradmFlags.KubectlFactory.ToDiscoveryClient() if err != nil { return err } diff --git a/pkg/cmd/version/options.go b/pkg/cmd/version/options.go index 12d40040e..03508eb3d 100644 --- a/pkg/cmd/version/options.go +++ b/pkg/cmd/version/options.go @@ -3,17 +3,16 @@ package version import ( "k8s.io/cli-runtime/pkg/genericclioptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" + genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" ) type Options struct { - ConfigFlags *genericclioptions.ConfigFlags - factory cmdutil.Factory + //ClusteradmFlags: The generic optiosn from the clusteradm cli-runtime. + ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags } -func newOptions(f cmdutil.Factory, streams genericclioptions.IOStreams) *Options { +func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ - ConfigFlags: genericclioptions.NewConfigFlags(true), - factory: f, + ClusteradmFlags: clusteradmFlags, } } diff --git a/pkg/genericclioptions/clusteradm_flags.go b/pkg/genericclioptions/clusteradm_flags.go new file mode 100644 index 000000000..e5f783da4 --- /dev/null +++ b/pkg/genericclioptions/clusteradm_flags.go @@ -0,0 +1,28 @@ +// Copyright Contributors to the Open Cluster Management project +package genericclioptions + +import ( + "github.com/spf13/pflag" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +type ClusteradmFlagsGetter interface { + GetClusteradmFlag() *ClusteradmFlags +} + +type ClusteradmFlags struct { + KubectlFactory cmdutil.Factory + //if set the resources will be sent to stdout instead of being applied + DryRun bool +} + +// NewClusteradmFlags returns ClusteradmFlags with default values set +func NewClusteradmFlags(f cmdutil.Factory) *ClusteradmFlags { + return &ClusteradmFlags{ + KubectlFactory: f, + } +} + +func (f *ClusteradmFlags) AddFlags(flags *pflag.FlagSet) { + flags.BoolVar(&f.DryRun, "dry-run", false, "If set the generated resources will be displayed but not applied") +} diff --git a/pkg/helpers/apply/apply.go b/pkg/helpers/apply/apply.go index 7d76fa6a9..0146fdd67 100644 --- a/pkg/helpers/apply/apply.go +++ b/pkg/helpers/apply/apply.go @@ -26,7 +26,6 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/restmapper" - "k8s.io/klog" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/resource/resourceapply" @@ -42,70 +41,74 @@ var ( genericCodec = genericCodecs.UniversalDeserializer() ) -//ApplyDeployment applies a appsv1.Deployment template -func ApplyDeployment( - client kubernetes.Interface, +//ApplyDeployments applies a appsv1.Deployment template +func ApplyDeployments( + kubeClient kubernetes.Interface, reader asset.ScenarioReader, values interface{}, dryRun bool, headerFile string, files ...string) ([]string, error) { genericScheme.AddKnownTypes(appsv1.SchemeGroupVersion, &appsv1.Deployment{}) - recorder := events.NewInMemoryRecorder(helpers.GetExampleHeader()) output := make([]string, 0) //Render each file for _, name := range files { - deploymentBytes, err := MustTempalteAsset(name, headerFile, reader, values) + deployment, err := ApplyDeployment(kubeClient, reader, values, dryRun, headerFile, name) if err != nil { if IsEmptyAsset(err) { continue } return output, err } - output = append(output, string(deploymentBytes)) - if dryRun { - continue - } - deployment, sch, err := genericCodec.Decode(deploymentBytes, nil, nil) - if err != nil { - return output, fmt.Errorf("%q: %v %v", name, sch, err) - } - _, _, err = resourceapply.ApplyDeployment( - client.AppsV1(), - recorder, - deployment.(*appsv1.Deployment), 0) - if err != nil { - return output, fmt.Errorf("%q (%T): %v", name, deployment, err) - } + output = append(output, deployment) } return output, nil } -//ApplyDirectly applies standard kubernetes resources. -func ApplyDirectly(clients *resourceapply.ClientHolder, +func ApplyDeployment(kubeClient kubernetes.Interface, reader asset.ScenarioReader, values interface{}, dryRun bool, headerFile string, - files ...string) ([]string, error) { + name string) (string, error) { recorder := events.NewInMemoryRecorder(helpers.GetExampleHeader()) - output := make([]string, 0) - for _, name := range files { - deploymentBytes, err := MustTempalteAsset(name, headerFile, reader, values) - if err != nil { - if IsEmptyAsset(err) { - continue - } - return output, err - } - output = append(output, string(deploymentBytes)) + deploymentBytes, err := MustTempalteAsset(reader, values, headerFile, name) + if err != nil { + return string(deploymentBytes), err } + output := string(deploymentBytes) if dryRun { return output, nil } + deployment, sch, err := genericCodec.Decode(deploymentBytes, nil, nil) + if err != nil { + return output, fmt.Errorf("%q: %v %v", name, sch, err) + } + _, _, err = resourceapply.ApplyDeployment( + kubeClient.AppsV1(), + recorder, + deployment.(*appsv1.Deployment), 0) + if err != nil { + return output, fmt.Errorf("%q (%T): %v", name, deployment, err) + } + return output, nil +} + +//ApplyDirectly applies standard kubernetes resources. +func ApplyDirectly(clients *resourceapply.ClientHolder, + reader asset.ScenarioReader, + values interface{}, + dryRun bool, + headerFile string, + files ...string) ([]string, error) { + if dryRun { + return MustTemplateAssets(reader, values, headerFile, files...) + } + recorder := events.NewInMemoryRecorder(helpers.GetExampleHeader()) + output := make([]string, 0) //Apply resources resourceResults := resourceapply.ApplyDirectly(clients, recorder, func(name string) ([]byte, error) { - return MustTempalteAsset(name, headerFile, reader, values) + return MustTempalteAsset(reader, values, headerFile, name) }, files...) //Check errors for _, result := range resourceResults { @@ -117,7 +120,7 @@ func ApplyDirectly(clients *resourceapply.ClientHolder, } //ApplyCustomResouces applies custom resources -func ApplyCustomResouces(client dynamic.Interface, +func ApplyCustomResouces(dynamicClient dynamic.Interface, discoveryClient discovery.DiscoveryInterface, reader asset.ScenarioReader, values interface{}, @@ -126,7 +129,7 @@ func ApplyCustomResouces(client dynamic.Interface, files ...string) ([]string, error) { output := make([]string, 0) for _, name := range files { - asset, err := MustTempalteAsset(name, headerFile, reader, values) + asset, err := ApplyCustomResouce(dynamicClient, discoveryClient, reader, values, dryRun, headerFile, name) if err != nil { if IsEmptyAsset(err) { continue @@ -134,38 +137,54 @@ func ApplyCustomResouces(client dynamic.Interface, return output, err } output = append(output, string(asset)) - if dryRun { - continue - } - u, err := bytesToUnstructured(reader, asset) - if err != nil { - return output, err - } - gvks, _, err := genericScheme.ObjectKinds(u) - if err != nil { - return output, err - } - gvk := gvks[0] - mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(discoveryClient)) - mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) - if err != nil { - return output, err - } - dr := client.Resource(mapping.Resource) - ug, err := dr.Namespace(u.GetNamespace()).Get(context.TODO(), u.GetName(), metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - _, err = dr.Namespace(u.GetNamespace()). - Create(context.TODO(), u, metav1.CreateOptions{}) - } - } else { - u.SetResourceVersion(ug.GetResourceVersion()) + } + return output, nil +} + +//ApplyCustomResouces applies custom resources +func ApplyCustomResouce(dynamicClient dynamic.Interface, + discoveryClient discovery.DiscoveryInterface, + reader asset.ScenarioReader, + values interface{}, + dryRun bool, + headerFile string, + name string) (string, error) { + asset, err := MustTempalteAsset(reader, values, headerFile, name) + output := string(asset) + if err != nil { + return output, err + } + if dryRun { + return output, nil + } + u, err := bytesToUnstructured(reader, asset) + if err != nil { + return output, err + } + gvks, _, err := genericScheme.ObjectKinds(u) + if err != nil { + return output, err + } + gvk := gvks[0] + mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(discoveryClient)) + mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil { + return output, err + } + dr := dynamicClient.Resource(mapping.Resource) + ug, err := dr.Namespace(u.GetNamespace()).Get(context.TODO(), u.GetName(), metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { _, err = dr.Namespace(u.GetNamespace()). - Update(context.TODO(), u, metav1.UpdateOptions{}) - } - if err != nil { - return output, err + Create(context.TODO(), u, metav1.CreateOptions{}) } + } else { + u.SetResourceVersion(ug.GetResourceVersion()) + _, err = dr.Namespace(u.GetNamespace()). + Update(context.TODO(), u, metav1.UpdateOptions{}) + } + if err != nil { + return output, err } return output, nil } @@ -179,7 +198,6 @@ func bytesToUnstructured(reader asset.ScenarioReader, asset []byte) (*unstructur u := &unstructured.Unstructured{} _, _, err = unstructured.UnstructuredJSONScheme.Decode(j, nil, u) if err != nil { - klog.V(5).Infof("Error: %s", err) //In case it is not a kube yaml if !runtime.IsMissingKind(err) { return nil, err @@ -198,12 +216,27 @@ func getTemplate(templateName string) *template.Template { return tmpl } +func MustTemplateAssets(reader asset.ScenarioReader, values interface{}, headerFile string, files ...string) ([]string, error) { + output := make([]string, 0) + for _, name := range files { + deploymentBytes, err := MustTempalteAsset(reader, values, headerFile, name) + if err != nil { + if IsEmptyAsset(err) { + continue + } + return output, err + } + output = append(output, string(deploymentBytes)) + } + return output, nil +} + //MustTempalteAsset generates textual output for a template file name. //The headerfile will be added to each file. //Usually it contains nested template definitions as described https://golang.org/pkg/text/template/#hdr-Nested_template_definitions //This allows to add functions which can be use in each file. //The values object will be used to render the template -func MustTempalteAsset(name, headerFile string, reader asset.ScenarioReader, values interface{}) ([]byte, error) { +func MustTempalteAsset(reader asset.ScenarioReader, values interface{}, headerFile, name string) ([]byte, error) { tmpl := getTemplate(name) h := []byte{} var err error diff --git a/pkg/helpers/apply/apply_test.go b/pkg/helpers/apply/apply_test.go index 35147f155..8abff3bfb 100644 --- a/pkg/helpers/apply/apply_test.go +++ b/pkg/helpers/apply/apply_test.go @@ -77,7 +77,7 @@ func TestMustTempalteAsset(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MustTempalteAsset(tt.args.name, tt.args.headerFile, tt.args.reader, tt.args.values) + got, err := MustTempalteAsset(tt.args.reader, tt.args.values, tt.args.headerFile, tt.args.name) if (err != nil) != tt.wantErr { t.Errorf("MustTempalteAsset() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/helpers/cmd.go b/pkg/helpers/cmd.go index ea797c3fd..86ab335b6 100644 --- a/pkg/helpers/cmd.go +++ b/pkg/helpers/cmd.go @@ -29,3 +29,9 @@ func UsageTempate(cmd *cobra.Command, reader asset.ScenarioReader, valuesTemplat } return fmt.Sprintf("%s\n\n Values template:\n%s", baseUsage, string(b)) } + +func DryRunMessage(dryRun bool) { + if dryRun { + fmt.Printf("%s is running in dry-run mode", GetExampleHeader()) + } +}