-
Notifications
You must be signed in to change notification settings - Fork 68
Feat: clusteradm addon disable command for disable specified addon #102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
openshift-merge-robot
merged 4 commits into
open-cluster-management-io:main
from
ycyaoxdu:add-command-addon-disable
Jan 25, 2022
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
6a2800d
addon enable: allow certain addon to be deployed
ycyaoxdu 4789d70
addon disable: allow certain addon to be undeployed
ycyaoxdu ec7df62
add integration test for clusteradm addon disable
ycyaoxdu 4df06b7
replace map by set in apimachinery, removed flag namespace
ycyaoxdu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // Copyright Contributors to the Open Cluster Management project | ||
| package disable | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" | ||
| clusteradmhelpers "open-cluster-management.io/clusteradm/pkg/helpers" | ||
|
|
||
| "github.com/spf13/cobra" | ||
| "k8s.io/cli-runtime/pkg/genericclioptions" | ||
| ) | ||
|
|
||
| var example = ` | ||
| # Disanable addon on a cluster in speccified a namespace | ||
| %[1]s addon disable --name application-manager --ns namespace --cluster cluster1,cluster2 | ||
| ` | ||
|
|
||
| // NewCmd... | ||
| func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *cobra.Command { | ||
| o := newOptions(clusteradmFlags, streams) | ||
|
|
||
| cmd := &cobra.Command{ | ||
| Use: "disable", | ||
| Short: "disable specified addon on specified managed cluster", | ||
| Example: fmt.Sprintf(example, clusteradmhelpers.GetExampleHeader()), | ||
| SilenceUsage: true, | ||
| PreRunE: func(c *cobra.Command, args []string) error { | ||
| clusteradmhelpers.DryRunMessage(clusteradmFlags.DryRun) | ||
|
|
||
| return nil | ||
| }, | ||
| RunE: func(c *cobra.Command, args []string) error { | ||
| if err := o.complete(c, args); err != nil { | ||
| return err | ||
| } | ||
| if err := o.validate(); err != nil { | ||
| return err | ||
| } | ||
| if err := o.run(); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| }, | ||
| } | ||
|
|
||
| cmd.Flags().StringSliceVar(&o.names, "name", []string{}, "Names of the add-on to deploy (comma separated)") | ||
| cmd.Flags().StringSliceVar(&o.clusters, "cluster", []string{}, "Names of the managed cluster to deploy the add-on to (comma separated)") | ||
|
|
||
| return cmd | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| // Copyright Contributors to the Open Cluster Management project | ||
| package disable | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| "k8s.io/apimachinery/pkg/api/errors" | ||
|
|
||
| "github.com/spf13/cobra" | ||
| apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/util/sets" | ||
| "k8s.io/client-go/dynamic" | ||
| "k8s.io/client-go/kubernetes" | ||
| "k8s.io/klog/v2" | ||
| addonclient "open-cluster-management.io/api/client/addon/clientset/versioned" | ||
| clusterclientset "open-cluster-management.io/api/client/cluster/clientset/versioned" | ||
| "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) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (o *Options) validate() (err error) { | ||
| if len(o.names) == 0 { | ||
| return fmt.Errorf("names is missing") | ||
| } | ||
|
|
||
| if len(o.clusters) == 0 { | ||
| return fmt.Errorf("clusters is misisng") | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func (o *Options) run() (err error) { | ||
| addons := sets.NewString(o.names...) | ||
| clusters := sets.NewString(o.clusters...) | ||
|
|
||
| klog.V(3).InfoS("values:", "addon", addons, "clusters", clusters) | ||
|
|
||
| restConfig, err := o.ClusteradmFlags.KubectlFactory.ToRESTConfig() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| clusterClient, err := clusterclientset.NewForConfig(restConfig) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| addonClient, err := addonclient.NewForConfig(restConfig) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| kubeClient, apiExtensionsClient, dynamicClient, err := helpers.GetClients(o.ClusteradmFlags.KubectlFactory) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return o.runWithClient(clusterClient, addonClient, kubeClient, apiExtensionsClient, dynamicClient, o.ClusteradmFlags.DryRun, addons.List(), clusters.List()) | ||
| } | ||
|
|
||
| func (o *Options) runWithClient(clusterClient clusterclientset.Interface, | ||
| addonClient addonclient.Interface, | ||
| kubeClient kubernetes.Interface, | ||
| apiExtensionsClient apiextensionsclient.Interface, | ||
| dynamicClient dynamic.Interface, | ||
| dryRun bool, | ||
| addons []string, | ||
| clusters []string) error { | ||
|
|
||
| for _, clusterName := range clusters { | ||
| _, err := clusterClient.ClusterV1().ManagedClusters().Get(context.TODO(), | ||
| clusterName, | ||
| metav1.GetOptions{}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| for _, addon := range addons { | ||
| for _, clusterName := range clusters { | ||
| err := addonClient.AddonV1alpha1().ManagedClusterAddOns(clusterName).Delete(context.TODO(), | ||
| addon, | ||
| metav1.DeleteOptions{}) | ||
| if err != nil { | ||
| if !errors.IsNotFound(err) { | ||
| return err | ||
| } | ||
| } | ||
| fmt.Fprintf(o.Streams.Out, "Undeploying %s add-on in managed cluster: %s.\n", addon, clusterName) | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| // Copyright Contributors to the Open Cluster Management project | ||
| package disable | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/onsi/ginkgo" | ||
| "github.com/onsi/gomega" | ||
| corev1 "k8s.io/api/core/v1" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/util/rand" | ||
| "k8s.io/cli-runtime/pkg/genericclioptions" | ||
|
|
||
| clusterapiv1 "open-cluster-management.io/api/cluster/v1" | ||
|
|
||
| "open-cluster-management.io/clusteradm/pkg/cmd/addon/enable" | ||
| "open-cluster-management.io/clusteradm/pkg/cmd/addon/enable/scenario" | ||
|
|
||
| "open-cluster-management.io/clusteradm/pkg/helpers/apply" | ||
| ) | ||
|
|
||
| var _ = ginkgo.Describe("addon disable", func() { | ||
| var cluster1Name string | ||
| var cluster2Name string | ||
| var err error | ||
|
|
||
| appMgrAddonName := "application-manager" | ||
|
|
||
| ginkgo.BeforeEach(func() { | ||
| cluster1Name = fmt.Sprintf("cluster-%s", rand.String(5)) | ||
| cluster2Name = fmt.Sprintf("cluster-%s", rand.String(5)) | ||
| }) | ||
|
|
||
| assertCreatingClusters := func(clusterName string) { | ||
| ginkgo.By(fmt.Sprintf("Create %s cluster", clusterName)) | ||
|
|
||
| cluster := &clusterapiv1.ManagedCluster{ | ||
| ObjectMeta: metav1.ObjectMeta{ | ||
| Name: clusterName, | ||
| }, | ||
| } | ||
|
|
||
| _, err = clusterClient.ClusterV1().ManagedClusters().Create(context.Background(), cluster, metav1.CreateOptions{}) | ||
| gomega.Expect(err).ToNot(gomega.HaveOccurred()) | ||
|
|
||
| ns := &corev1.Namespace{ | ||
| ObjectMeta: metav1.ObjectMeta{ | ||
| Name: clusterName, | ||
| }, | ||
| } | ||
| _, err := kubeClient.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) | ||
| gomega.Expect(err).ToNot(gomega.HaveOccurred(), "creat cluster error") | ||
| } | ||
|
|
||
| streams := genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} | ||
|
|
||
| assertEnableAddon := func(addons []string, clusters []string, ns string) { | ||
|
|
||
| reader := scenario.GetScenarioResourcesReader() | ||
| applierBuilder := &apply.ApplierBuilder{} | ||
| applier := applierBuilder.WithClient(kubeClient, apiExtensionsClient, dynamicClient).Build() | ||
|
|
||
| for _, addon := range addons { | ||
| for _, clus := range clusters { | ||
| ginkgo.By(fmt.Sprintf("Enableing %s addon on %s cluster in %s namespce", addon, clus, ns)) | ||
|
|
||
| cai := enable.NewClusterAddonInfo(clus, ns, addon) | ||
| _, err := applier.ApplyCustomResources(reader, cai, false, "", "addons/app/addon.yaml") | ||
| gomega.Expect(err).ToNot(gomega.HaveOccurred(), "enable addon error") | ||
| fmt.Fprintf(streams.Out, "Deploying %s add-on to namespaces %s of managed cluster: %s.\n", addon, ns, clus) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| ginkgo.Context("runWithClient", func() { | ||
|
|
||
| ginkgo.It("Should disable application-manager ManagedClusterAddOn in ManagedCluster namespace successfully", func() { | ||
| assertCreatingClusters(cluster1Name) | ||
|
|
||
| addons := []string{appMgrAddonName} | ||
| clusters := []string{cluster1Name} | ||
| assertEnableAddon([]string{appMgrAddonName}, []string{cluster1Name}, "default") | ||
|
|
||
| o := Options{ | ||
| Streams: streams, | ||
| } | ||
|
|
||
| err := o.runWithClient(clusterClient, addonClient, kubeClient, apiExtensionsClient, dynamicClient, false, addons, clusters) | ||
| gomega.Expect(err).ToNot(gomega.HaveOccurred()) | ||
| }) | ||
|
|
||
| ginkgo.It("Should disable application-manager ManagedClusterAddOns in each ManagedCluster namespace successfully", func() { | ||
| assertCreatingClusters(cluster1Name) | ||
| assertCreatingClusters(cluster2Name) | ||
|
|
||
| addons := []string{appMgrAddonName} | ||
| clusters := []string{cluster1Name, cluster2Name} | ||
| assertEnableAddon(addons, clusters, "default") | ||
|
|
||
| o := Options{ | ||
| Streams: streams, | ||
| } | ||
|
|
||
| err := o.runWithClient(clusterClient, addonClient, kubeClient, apiExtensionsClient, dynamicClient, false, addons, clusters) | ||
| gomega.Expect(err).ToNot(gomega.HaveOccurred()) | ||
| }) | ||
|
|
||
| ginkgo.It("Should not disable a ManagedClusterAddOn because ManagedCluster doesn't exist", func() { | ||
| assertCreatingClusters(cluster1Name) | ||
|
|
||
| addons := []string{appMgrAddonName} | ||
| clusters := []string{cluster1Name} | ||
| assertEnableAddon(addons, clusters, "default") | ||
|
|
||
| wrongCluster := "no-such-addon" | ||
| wrongClusters := []string{wrongCluster} | ||
| o := Options{ | ||
| Streams: streams, | ||
| } | ||
|
|
||
| err := o.runWithClient(clusterClient, addonClient, kubeClient, apiExtensionsClient, dynamicClient, false, addons, wrongClusters) | ||
| gomega.Expect(err).To(gomega.HaveOccurred()) | ||
| }) | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Copyright Contributors to the Open Cluster Management project | ||
| package disable | ||
|
|
||
| import ( | ||
| "k8s.io/cli-runtime/pkg/genericclioptions" | ||
| genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" | ||
| ) | ||
|
|
||
| type Options struct { | ||
| //ClusteradmFlags: The generic optiosn from the clusteradm cli-runtime. | ||
| ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags | ||
| //A list of comma separated addon names | ||
| names []string | ||
| //A list of comma separated cluster names | ||
| clusters []string | ||
|
|
||
| Streams genericclioptions.IOStreams | ||
| } | ||
|
|
||
| func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { | ||
| return &Options{ | ||
| ClusteradmFlags: clusteradmFlags, | ||
| Streams: streams, | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // Copyright Contributors to the Open Cluster Management project | ||
| package disable | ||
|
|
||
| import ( | ||
| "path/filepath" | ||
| "testing" | ||
|
|
||
| "github.com/onsi/ginkgo" | ||
| "github.com/onsi/gomega" | ||
|
|
||
| apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" | ||
| "k8s.io/client-go/dynamic" | ||
| "k8s.io/client-go/kubernetes" | ||
| "k8s.io/client-go/rest" | ||
| "sigs.k8s.io/controller-runtime/pkg/envtest" | ||
|
|
||
| addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned" | ||
| clusterv1client "open-cluster-management.io/api/client/cluster/clientset/versioned" | ||
| ) | ||
|
|
||
| var testEnv *envtest.Environment | ||
| var restConfig *rest.Config | ||
| var kubeClient kubernetes.Interface | ||
| var apiExtensionsClient apiextensionsclient.Interface | ||
| var dynamicClient dynamic.Interface | ||
| var clusterClient clusterv1client.Interface | ||
| var addonClient addonv1alpha1client.Interface | ||
|
|
||
| func TestIntegrationEnableAddons(t *testing.T) { | ||
| gomega.RegisterFailHandler(ginkgo.Fail) | ||
| ginkgo.RunSpecs(t, "Integration Disable Addons Suite") | ||
| } | ||
|
|
||
| var _ = ginkgo.BeforeSuite(func(done ginkgo.Done) { | ||
| ginkgo.By("bootstrapping test environment") | ||
|
|
||
| // start a kube-apiserver | ||
| testEnv = &envtest.Environment{ | ||
| ErrorIfCRDPathMissing: true, | ||
| CRDDirectoryPaths: []string{ | ||
| filepath.Join("..", "..", "..", "..", "vendor", "open-cluster-management.io", "api", "cluster", "v1"), | ||
| filepath.Join("..", "..", "..", "..", "vendor", "open-cluster-management.io", "api", "addon", "v1alpha1"), | ||
| }, | ||
| } | ||
|
|
||
| cfg, err := testEnv.Start() | ||
| gomega.Expect(err).ToNot(gomega.HaveOccurred()) | ||
| gomega.Expect(cfg).ToNot(gomega.BeNil()) | ||
|
|
||
| kubeClient, err = kubernetes.NewForConfig(cfg) | ||
| gomega.Expect(err).NotTo(gomega.HaveOccurred()) | ||
| apiExtensionsClient, err = apiextensionsclient.NewForConfig(cfg) | ||
| gomega.Expect(err).NotTo(gomega.HaveOccurred()) | ||
| dynamicClient, err = dynamic.NewForConfig(cfg) | ||
| gomega.Expect(err).NotTo(gomega.HaveOccurred()) | ||
| clusterClient, err = clusterv1client.NewForConfig(cfg) | ||
| gomega.Expect(err).NotTo(gomega.HaveOccurred()) | ||
| addonClient, err = addonv1alpha1client.NewForConfig(cfg) | ||
| gomega.Expect(err).NotTo(gomega.HaveOccurred()) | ||
|
|
||
| restConfig = cfg | ||
| close(done) | ||
| }, 60) | ||
|
|
||
| var _ = ginkgo.AfterSuite(func() { | ||
| ginkgo.By("tearing down the test environment") | ||
|
|
||
| err := testEnv.Stop() | ||
| gomega.Expect(err).ToNot(gomega.HaveOccurred()) | ||
| }) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.