From 7fdf16a4a33b6fb3afaf519b2579f9f3d1af689f Mon Sep 17 00:00:00 2001 From: Lan Luo Date: Mon, 17 Jul 2023 16:16:52 +0800 Subject: [PATCH] Refine `antctl mc deploy` to support manifest update If the CRD is already existing, the command `antctl mc deploy` will do nothing even when the manifests are changed. Refine the code to apply changes if there is any manifest update. Signed-off-by: Lan Luo --- .../raw/multicluster/deploy/deploy_helper.go | 16 ++++- .../multicluster/deploy/deploy_helper_test.go | 70 +++++++++++++++++-- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/pkg/antctl/raw/multicluster/deploy/deploy_helper.go b/pkg/antctl/raw/multicluster/deploy/deploy_helper.go index 21261257ab7..dd430480578 100644 --- a/pkg/antctl/raw/multicluster/deploy/deploy_helper.go +++ b/pkg/antctl/raw/multicluster/deploy/deploy_helper.go @@ -121,7 +121,21 @@ func createResources(cmd *cobra.Command, apiGroupResources []*restmapper.APIGrou if !kerrors.IsAlreadyExists(err) { return err } - fmt.Fprintf(cmd.OutOrStdout(), "%s/%s already exists\n", unstructuredObj.GetKind(), unstructuredObj.GetName()) + existingRes, err := dri.Get(context.TODO(), unstructuredObj.GetName(), metav1.GetOptions{}) + if err != nil { + return err + } + existingVersion := existingRes.GetResourceVersion() + unstructuredObj.SetResourceVersion(existingVersion) + var updatedObj *unstructured.Unstructured + if updatedObj, err = dri.Update(context.TODO(), unstructuredObj, metav1.UpdateOptions{}); err != nil { + return err + } + if updatedObj.GetResourceVersion() != existingVersion { + fmt.Fprintf(cmd.OutOrStdout(), "%s/%s configured\n", unstructuredObj.GetKind(), unstructuredObj.GetName()) + } else { + fmt.Fprintf(cmd.OutOrStdout(), "%s/%s unchanged\n", unstructuredObj.GetKind(), unstructuredObj.GetName()) + } } else { fmt.Fprintf(cmd.OutOrStdout(), "%s/%s created\n", unstructuredObj.GetKind(), unstructuredObj.GetName()) } diff --git a/pkg/antctl/raw/multicluster/deploy/deploy_helper_test.go b/pkg/antctl/raw/multicluster/deploy/deploy_helper_test.go index ec2ecdc5064..85b5ff07a24 100644 --- a/pkg/antctl/raw/multicluster/deploy/deploy_helper_test.go +++ b/pkg/antctl/raw/multicluster/deploy/deploy_helper_test.go @@ -15,6 +15,7 @@ package deploy import ( + "bytes" "errors" "io" "log" @@ -27,11 +28,22 @@ import ( "github.com/spf13/cobra" "github.com/stretchr/testify/assert" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + fakedisco "k8s.io/client-go/discovery/fake" dynamicfake "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/restmapper" + // "k8s.io/apimachinery/pkg/api/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + + coretesting "k8s.io/client-go/testing" + mcscheme "antrea.io/antrea/pkg/antctl/raw/multicluster/scheme" ) @@ -99,18 +111,66 @@ func TestGenerateManifests(t *testing.T) { func TestCreateResources(t *testing.T) { fakeDynamicClient := dynamicfake.NewSimpleDynamicClient(mcscheme.Scheme) - fakeClient := fake.NewSimpleClientset() - apiGroupResources, _ := restmapper.GetAPIGroupResources(fakeClient.Discovery()) + fakeDynamicClient.PrependReactor("create", "customresourcedefinitions", func(action coretesting.Action) (bool, runtime.Object, error) { + crd := action.(coretesting.CreateAction).GetObject().(*unstructured.Unstructured) + metadata, ok := crd.UnstructuredContent()["metadata"].(map[string]interface{}) + if ok && metadata["name"] == "clustersets.multicluster.crd.antrea.io" { + return true, nil, k8serrors.NewAlreadyExists(schema.GroupResource{Resource: "customresourcedefinitions"}, "clustersets") + } + return true, nil, nil + }) + fakeDynamicClient.PrependReactor("get", "customresourcedefinitions", func(action coretesting.Action) (bool, runtime.Object, error) { + if action.(coretesting.GetAction).GetName() == "clustersets.multicluster.crd.antrea.io" { + return true, &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apiextensions.k8s.io/v1", + "kind": "customresourcedefinitions", + "metadata": map[string]interface{}{ + "name": "clustersets.multicluster.crd.antrea.io", + }, + }, + }, nil + } + return true, nil, nil + }) + fakeDynamicClient.PrependReactor("update", "customresourcedefinitions", func(action coretesting.Action) (bool, runtime.Object, error) { + crd := action.(coretesting.UpdateAction).GetObject().(*unstructured.Unstructured) + metadata, ok := crd.UnstructuredContent()["metadata"].(map[string]interface{}) + if ok && metadata["name"] == "clustersets.multicluster.crd.antrea.io" { + return true, &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apiextensions.k8s.io/v1", + "kind": "customresourcedefinitions", + "metadata": map[string]interface{}{ + "name": "clustersets.multicluster.crd.antrea.io", + "resourceVersion": "100", + }, + }, + }, nil + } + return true, nil, nil + }) + fakeDiscoveryClient := &fakedisco.FakeDiscovery{Fake: &coretesting.Fake{}} + fakeDiscoveryClient.Resources = []*metav1.APIResourceList{ + { + GroupVersion: apiextensionsv1.SchemeGroupVersion.String(), + APIResources: []metav1.APIResource{ + {Name: "customresourcedefinitions", Namespaced: false, Kind: "CustomResourceDefinition"}, + }, + }, + } + apiGroupResources, _ := restmapper.GetAPIGroupResources(fakeDiscoveryClient) cmd := &cobra.Command{} + buf := new(bytes.Buffer) + cmd.SetOutput(buf) file := filepath.Join("..", "..", "..", "..", "..", "multicluster", "build", "yamls", "antrea-multicluster-leader-global.yml") content, err := os.ReadFile(file) if err != nil { t.Errorf("Failed to open the file %s", file) } err = createResources(cmd, apiGroupResources, fakeDynamicClient, content) - if err != nil { - assert.Contains(t, err.Error(), "no matches for kind \"CustomResourceDefinition\"") - } + assert.NoError(t, err) + assert.Contains(t, buf.String(), "CustomResourceDefinition/clustersets.multicluster.crd.antrea.io configured\n") } func TestDeploy(t *testing.T) {