github.com/oam-dev/kubevela@v1.9.11/pkg/addon/addon_suite_test.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package addon
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"net/http/httptest"
    24  	"time"
    25  
    26  	. "github.com/onsi/ginkgo/v2"
    27  	. "github.com/onsi/gomega"
    28  	"github.com/pkg/errors"
    29  	yaml3 "gopkg.in/yaml.v3"
    30  	appsv1 "k8s.io/api/apps/v1"
    31  	v1 "k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    34  	"k8s.io/apimachinery/pkg/runtime"
    35  	types2 "k8s.io/apimachinery/pkg/types"
    36  	"sigs.k8s.io/controller-runtime/pkg/client"
    37  	"sigs.k8s.io/yaml"
    38  
    39  	"github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
    40  	clustercommon "github.com/oam-dev/cluster-gateway/pkg/common"
    41  
    42  	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    43  	v1alpha12 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
    44  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    45  	"github.com/oam-dev/kubevela/apis/types"
    46  	"github.com/oam-dev/kubevela/pkg/oam/util"
    47  	addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
    48  	"github.com/oam-dev/kubevela/pkg/utils/apply"
    49  	"github.com/oam-dev/kubevela/references/cli/top/model"
    50  )
    51  
    52  var _ = Describe("Addon test", func() {
    53  	ctx := context.Background()
    54  	var app v1beta1.Application
    55  	var s *httptest.Server
    56  
    57  	BeforeEach(func() {
    58  		s = setupMockServer()
    59  		// Prepare registry
    60  		reg := &Registry{
    61  			Name: "addon_helper_test",
    62  			Helm: &HelmSource{
    63  				URL: s.URL,
    64  			},
    65  		}
    66  		ds := NewRegistryDataStore(k8sClient)
    67  		Expect(ds.AddRegistry(context.Background(), *reg)).To(Succeed())
    68  	})
    69  
    70  	AfterEach(func() {
    71  		Expect(k8sClient.Delete(ctx, &app)).Should(BeNil())
    72  		// Clean up registry
    73  		ds := NewRegistryDataStore(k8sClient)
    74  		Expect(ds.DeleteRegistry(context.Background(), "addon_helper_test")).To(Succeed())
    75  		s.Close()
    76  	})
    77  
    78  	It("continueOrRestartWorkflow func test", func() {
    79  		app = v1beta1.Application{}
    80  		Expect(yaml.Unmarshal([]byte(appYaml), &app)).Should(BeNil())
    81  		app.SetNamespace(testns)
    82  		Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
    83  
    84  		Eventually(func() error {
    85  			checkApp := &v1beta1.Application{}
    86  			if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: app.Namespace, Name: app.Name}, checkApp); err != nil {
    87  				return err
    88  			}
    89  			appPatch := client.MergeFrom(checkApp.DeepCopy())
    90  			checkApp.Status.Workflow = &common.WorkflowStatus{
    91  				Suspend: true,
    92  			}
    93  			if err := k8sClient.Status().Patch(ctx, checkApp, appPatch); err != nil {
    94  				return err
    95  			}
    96  			return nil
    97  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
    98  
    99  		Eventually(func() error {
   100  			checkApp := &v1beta1.Application{}
   101  			if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: app.Namespace, Name: app.Name}, checkApp); err != nil {
   102  				return err
   103  			}
   104  			if !checkApp.Status.Workflow.Suspend {
   105  				return fmt.Errorf("app haven't not suspend")
   106  			}
   107  
   108  			h := Installer{ctx: ctx, cli: k8sClient, addon: &InstallPackage{Meta: Meta{Name: "test-app"}}}
   109  			if err := h.continueOrRestartWorkflow(); err != nil {
   110  				return err
   111  			}
   112  			return nil
   113  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   114  
   115  		Eventually(func() error {
   116  			checkApp := &v1beta1.Application{}
   117  			if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: app.Namespace, Name: app.Name}, checkApp); err != nil {
   118  				return err
   119  			}
   120  			if checkApp.Status.Workflow.Suspend {
   121  				return fmt.Errorf("app haven't not continue")
   122  			}
   123  			return nil
   124  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   125  	})
   126  
   127  	It("continueOrRestartWorkflow func test, test restart workflow", func() {
   128  		app = v1beta1.Application{}
   129  		Expect(yaml.Unmarshal([]byte(appYaml), &app)).Should(BeNil())
   130  		app.SetNamespace(testns)
   131  		Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
   132  
   133  		Eventually(func() error {
   134  			checkApp := &v1beta1.Application{}
   135  			if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: app.Namespace, Name: app.Name}, checkApp); err != nil {
   136  				return err
   137  			}
   138  			appPatch := client.MergeFrom(checkApp.DeepCopy())
   139  			checkApp.Status.Workflow = &common.WorkflowStatus{
   140  				Message:     "someMessage",
   141  				AppRevision: "test-revision",
   142  			}
   143  			checkApp.Status.Phase = common.ApplicationRunning
   144  			if err := k8sClient.Status().Patch(ctx, checkApp, appPatch); err != nil {
   145  				return err
   146  			}
   147  			return nil
   148  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   149  
   150  		Eventually(func() error {
   151  			checkApp := &v1beta1.Application{}
   152  			if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: app.Namespace, Name: app.Name}, checkApp); err != nil {
   153  				return err
   154  			}
   155  			if checkApp.Status.Phase != common.ApplicationRunning {
   156  				return fmt.Errorf("app haven't not running")
   157  			}
   158  
   159  			h := Installer{ctx: ctx, cli: k8sClient, addon: &InstallPackage{Meta: Meta{Name: "test-app"}}}
   160  			if err := h.continueOrRestartWorkflow(); err != nil {
   161  				return err
   162  			}
   163  			return nil
   164  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   165  
   166  		Eventually(func() error {
   167  			checkApp := &v1beta1.Application{}
   168  			if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: app.Namespace, Name: app.Name}, checkApp); err != nil {
   169  				return err
   170  			}
   171  			if checkApp.Status.Workflow != nil {
   172  				return fmt.Errorf("app workflow havenot been restart")
   173  			}
   174  			return nil
   175  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   176  	})
   177  
   178  	It(" FetchAddonRelatedApp func test", func() {
   179  		app = v1beta1.Application{}
   180  		Expect(yaml.Unmarshal([]byte(legacyAppYaml), &app)).Should(BeNil())
   181  		app.SetNamespace(testns)
   182  		Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
   183  
   184  		Eventually(func() error {
   185  			app, err := FetchAddonRelatedApp(ctx, k8sClient, "legacy-addon")
   186  			if err != nil {
   187  				return err
   188  			}
   189  			if app.Name != "legacy-addon" {
   190  				return fmt.Errorf("error addon app name")
   191  			}
   192  			return nil
   193  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   194  	})
   195  
   196  	It("checkDependencyNeedInstall func test", func() {
   197  		// case1: dependency addon not exist
   198  		depAddonName := "legacy-addon"
   199  		addonClusters := []string{"cluster1", "cluster2"}
   200  		needInstallAddonDep, err := checkDependencyNeedInstall(ctx, k8sClient, depAddonName, addonClusters)
   201  		Expect(needInstallAddonDep).Should(BeTrue())
   202  		Expect(err).Should(BeNil())
   203  
   204  		// case2: dependency addon is installed locally
   205  		app = v1beta1.Application{}
   206  		Expect(yaml.Unmarshal([]byte(legacyAppYamlLocal), &app)).Should(BeNil())
   207  		app.SetNamespace(testns)
   208  		Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
   209  		Eventually(func(g Gomega) {
   210  			needInstallAddonDep, err := checkDependencyNeedInstall(ctx, k8sClient, "legacy-addon-local", addonClusters)
   211  			Expect(err).Should(BeNil())
   212  			Expect(needInstallAddonDep).Should(BeFalse())
   213  		}, 30*time.Second).Should(Succeed())
   214  
   215  		// case3: dependency addon has no clusters arg
   216  		noClusterArgAddonName := "vela-workflow"
   217  		app = v1beta1.Application{}
   218  		Expect(yaml.Unmarshal([]byte(legacyAppYamlNoClustersArg), &app)).Should(BeNil())
   219  		app.SetNamespace(testns)
   220  		Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
   221  		Eventually(func(g Gomega) {
   222  			needInstallAddonDep, err := checkDependencyNeedInstall(ctx, k8sClient, noClusterArgAddonName, addonClusters)
   223  			Expect(err).Should(BeNil())
   224  			Expect(needInstallAddonDep).Should(BeFalse())
   225  		}, 30*time.Second).Should(Succeed())
   226  
   227  		// case3: dependency addon has clusters arg, but clusters value is nil
   228  		hasClusterArgAddonName := "has-clusters-arg"
   229  		app = v1beta1.Application{}
   230  		Expect(yaml.Unmarshal([]byte(legacyAppYamlHasClustersArg), &app)).Should(BeNil())
   231  		app.SetNamespace(testns)
   232  		Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
   233  		Eventually(func(g Gomega) {
   234  			needInstallAddonDep, err := checkDependencyNeedInstall(ctx, k8sClient, hasClusterArgAddonName, addonClusters)
   235  			Expect(err).Should(BeNil())
   236  			Expect(needInstallAddonDep).Should(BeFalse())
   237  		}, 30*time.Second).Should(Succeed())
   238  
   239  		// case4: dependency addon has clusters arg, clusters value is local, addonClusters is nil
   240  		secret := v1.Secret{}
   241  		Expect(yaml.Unmarshal([]byte(legacySecretYamlHasClustersArg), &secret)).Should(BeNil())
   242  		app.SetNamespace(testns)
   243  		Expect(k8sClient.Create(ctx, &secret)).Should(BeNil())
   244  		Eventually(func(g Gomega) {
   245  			needInstallAddonDep, err := checkDependencyNeedInstall(ctx, k8sClient, hasClusterArgAddonName, nil)
   246  			Expect(err).Should(BeNil())
   247  			Expect(needInstallAddonDep).Should(BeTrue())
   248  		}, 60*time.Second).Should(Succeed())
   249  
   250  		// case5: dependency addon has clusters arg, clusters value is ["local"], addonClusters is ["local"]
   251  		needInstallAddonDep1, err1 := checkDependencyNeedInstall(ctx, k8sClient, hasClusterArgAddonName, []string{"local"})
   252  		Expect(err1).Should(BeNil())
   253  		Expect(needInstallAddonDep1).Should(BeFalse())
   254  
   255  		// case6: dependency addon has clusters arg, clusters value is local, addonClusters is ["cluster1", "cluster2"]
   256  		needInstallAddonDep2, err2 := checkDependencyNeedInstall(ctx, k8sClient, hasClusterArgAddonName, addonClusters)
   257  		Expect(err2).Should(BeNil())
   258  		Expect(needInstallAddonDep2).Should(BeTrue())
   259  	})
   260  
   261  	It(" determineAddonAppName func test", func() {
   262  		app = v1beta1.Application{}
   263  		Expect(yaml.Unmarshal([]byte(legacyAppYaml), &app)).Should(BeNil())
   264  		app.SetNamespace(testns)
   265  		Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
   266  
   267  		Eventually(func() error {
   268  			appName, err := determineAddonAppName(ctx, k8sClient, "legacy-addon")
   269  			if err != nil {
   270  				return err
   271  			}
   272  			if appName != "legacy-addon" {
   273  				return fmt.Errorf("error addon app name")
   274  			}
   275  			return nil
   276  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   277  
   278  		notExsitAppName, err := determineAddonAppName(ctx, k8sClient, "not-exist")
   279  		Expect(err).Should(BeNil())
   280  		Expect(notExsitAppName).Should(BeEquivalentTo("addon-not-exist"))
   281  	})
   282  })
   283  
   284  var _ = Describe("Addon func test", func() {
   285  	var deploy appsv1.Deployment
   286  
   287  	AfterEach(func() {
   288  		Expect(k8sClient.Delete(ctx, &deploy))
   289  	})
   290  
   291  	It("fetchVelaCoreImageTag func test", func() {
   292  		deploy = appsv1.Deployment{}
   293  		tag, err := fetchVelaCoreImageTag(ctx, k8sClient)
   294  		Expect(err).ShouldNot(BeNil())
   295  		Expect(tag).Should(BeEquivalentTo(""))
   296  
   297  		Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
   298  		deploy.SetNamespace(types.DefaultKubeVelaNS)
   299  		Expect(k8sClient.Create(ctx, &deploy)).Should(BeNil())
   300  
   301  		Eventually(func() error {
   302  			tag, err := fetchVelaCoreImageTag(ctx, k8sClient)
   303  			if err != nil {
   304  				return err
   305  			}
   306  			if tag != "v1.2.3" {
   307  				return fmt.Errorf("tag mismatch want %s actual %s", "v1.2.3", tag)
   308  			}
   309  			return err
   310  		}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
   311  	})
   312  
   313  	It("checkAddonVersionMeetRequired func test", func() {
   314  		deploy = appsv1.Deployment{}
   315  		Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).ShouldNot(BeNil())
   316  		Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
   317  		deploy.SetNamespace(types.DefaultKubeVelaNS)
   318  		Expect(k8sClient.Create(ctx, &deploy)).Should(BeNil())
   319  
   320  		Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).Should(BeNil())
   321  		Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.4"}, k8sClient, dc)).ShouldNot(BeNil())
   322  	})
   323  })
   324  
   325  var _ = Describe("Test addon util func", func() {
   326  
   327  	It("test render and fetch args", func() {
   328  		i := InstallPackage{Meta: Meta{Name: "test-addon"}}
   329  		args := map[string]interface{}{
   330  			"imagePullSecrets": []string{
   331  				"myreg", "myreg1",
   332  			},
   333  		}
   334  		u := RenderArgsSecret(&i, args)
   335  		secName := u.GetName()
   336  		secNs := u.GetNamespace()
   337  		Expect(k8sClient.Create(ctx, u)).Should(BeNil())
   338  
   339  		sec := v1.Secret{}
   340  		Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: secNs, Name: secName}, &sec)).Should(BeNil())
   341  		res, err := FetchArgsFromSecret(&sec)
   342  		Expect(err).Should(BeNil())
   343  		Expect(res).Should(BeEquivalentTo(map[string]interface{}{"imagePullSecrets": []interface{}{"myreg", "myreg1"}}))
   344  	})
   345  
   346  	It("test render and fetch args backward compatibility", func() {
   347  		secArgs := v1.Secret{
   348  			TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"},
   349  			ObjectMeta: metav1.ObjectMeta{
   350  				Name:      addonutil.Addon2SecName("test-addon-old-args"),
   351  				Namespace: types.DefaultKubeVelaNS,
   352  			},
   353  			StringData: map[string]string{
   354  				"repo": "www.test.com",
   355  				"tag":  "v1.3.1",
   356  			},
   357  			Type: v1.SecretTypeOpaque,
   358  		}
   359  		secName := secArgs.GetName()
   360  		secNs := secArgs.GetNamespace()
   361  		Expect(k8sClient.Create(ctx, &secArgs)).Should(BeNil())
   362  
   363  		sec := v1.Secret{}
   364  		Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: secNs, Name: secName}, &sec)).Should(BeNil())
   365  		res, err := FetchArgsFromSecret(&sec)
   366  		Expect(err).Should(BeNil())
   367  		Expect(res).Should(BeEquivalentTo(map[string]interface{}{"repo": "www.test.com", "tag": "v1.3.1"}))
   368  	})
   369  
   370  })
   371  
   372  var _ = Describe("Test render addon with specified clusters", func() {
   373  	BeforeEach(func() {
   374  		Expect(k8sClient.Create(ctx, &v1.Secret{
   375  			ObjectMeta: metav1.ObjectMeta{
   376  				Name:      "add-c1",
   377  				Namespace: "vela-system",
   378  				Labels: map[string]string{
   379  					clustercommon.LabelKeyClusterCredentialType: string(v1alpha1.CredentialTypeX509Certificate),
   380  					clustercommon.LabelKeyClusterEndpointType:   string(v1alpha1.ClusterEndpointTypeConst),
   381  					"key": "value",
   382  				},
   383  			},
   384  		})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
   385  		Expect(k8sClient.Create(ctx, &v1.Secret{
   386  			ObjectMeta: metav1.ObjectMeta{
   387  				Name:      "add-c2",
   388  				Namespace: "vela-system",
   389  				Labels: map[string]string{
   390  					clustercommon.LabelKeyClusterCredentialType: string(v1alpha1.CredentialTypeX509Certificate),
   391  					clustercommon.LabelKeyClusterEndpointType:   string(v1alpha1.ClusterEndpointTypeConst),
   392  					"key": "value",
   393  				},
   394  			},
   395  		})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
   396  	})
   397  	It("test render not exits cluster", func() {
   398  		i := &baseAddon
   399  		i.Name = "test-cluster-addon"
   400  
   401  		args := map[string]interface{}{
   402  			"clusters": []string{"add-c1", "ne"},
   403  		}
   404  		_, _, err := RenderApp(ctx, i, k8sClient, args)
   405  		Expect(err.Error()).Should(BeEquivalentTo("cluster ne not exist"))
   406  	})
   407  	It("test render normal addon with specified clusters", func() {
   408  		i := &baseAddon
   409  		i.DeployTo = &DeployTo{RuntimeCluster: true}
   410  		i.Name = "test-cluster-addon-normal"
   411  		args := map[string]interface{}{
   412  			"clusters": []string{"add-c1", "add-c2"},
   413  		}
   414  		ap, _, err := RenderApp(ctx, i, k8sClient, args)
   415  		Expect(err).Should(BeNil())
   416  		Expect(ap.Spec.Policies).Should(BeEquivalentTo([]v1beta1.AppPolicy{{Name: specifyAddonClustersTopologyPolicy,
   417  			Type:       v1alpha12.TopologyPolicyType,
   418  			Properties: &runtime.RawExtension{Raw: []byte(`{"clusters":["add-c1","add-c2","local"]}`)}}}))
   419  	})
   420  })
   421  
   422  var _ = Describe("func addon update ", func() {
   423  	It("test update addon app label", func() {
   424  		app_test_update := v1beta1.Application{}
   425  		Expect(yaml.Unmarshal([]byte(addonUpdateAppYaml), &app_test_update)).Should(BeNil())
   426  		Expect(k8sClient.Create(ctx, &app_test_update)).Should(BeNil())
   427  
   428  		Eventually(func() error {
   429  			var err error
   430  			appCheck := v1beta1.Application{}
   431  			err = k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-test-update"}, &appCheck)
   432  			if err != nil {
   433  				return err
   434  			}
   435  			if appCheck.Labels["addons.oam.dev/version"] != "v1.2.0" {
   436  				return fmt.Errorf("label mismatch")
   437  			}
   438  			return nil
   439  		}, time.Millisecond*500, 30*time.Second).Should(BeNil())
   440  
   441  		pkg := &InstallPackage{Meta: Meta{Name: "test-update", Version: "1.3.0"}}
   442  		h := NewAddonInstaller(context.Background(), k8sClient, nil, nil, nil, &Registry{Name: "test"}, nil, nil, nil)
   443  		h.addon = pkg
   444  		Expect(h.dispatchAddonResource(pkg)).Should(BeNil())
   445  
   446  		Eventually(func() error {
   447  			var err error
   448  			appCheck := v1beta1.Application{}
   449  			err = k8sClient.Get(context.Background(), types2.NamespacedName{Namespace: "vela-system", Name: "addon-test-update"}, &appCheck)
   450  			if err != nil {
   451  				return err
   452  			}
   453  			if appCheck.Labels["addons.oam.dev/version"] != "1.3.0" {
   454  				return fmt.Errorf("label mismatch")
   455  			}
   456  			return nil
   457  		}, time.Second*3, 300*time.Second).Should(BeNil())
   458  	})
   459  })
   460  
   461  var _ = Describe("test enable addon in local dir", func() {
   462  	BeforeEach(func() {
   463  		app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-example"}}
   464  		Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   465  	})
   466  
   467  	It("test enable addon by local dir", func() {
   468  		ctx := context.Background()
   469  		_, err := EnableAddonByLocalDir(ctx, "example", "./testdata/example", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
   470  		Expect(err).Should(BeNil())
   471  		app := v1beta1.Application{}
   472  		Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-example"}, &app)).Should(BeNil())
   473  	})
   474  })
   475  
   476  var _ = Describe("test dry-run addon from local dir", func() {
   477  	BeforeEach(func() {
   478  		app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-example"}}
   479  		Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   480  		cd := v1beta1.ComponentDefinition{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "helm-example"}}
   481  		Expect(k8sClient.Delete(ctx, &cd)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   482  	})
   483  	AfterEach(func() {
   484  		app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-example"}}
   485  		Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   486  
   487  		cd := v1beta1.ComponentDefinition{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "helm-example"}}
   488  		Expect(k8sClient.Delete(ctx, &cd)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   489  	})
   490  
   491  	It("test dry-run enable addon from local dir", func() {
   492  		ctx := context.Background()
   493  
   494  		r := localReader{dir: "./testdata/example", name: "addon-example"}
   495  		metas, err := r.ListAddonMeta()
   496  		Expect(err).Should(BeNil())
   497  
   498  		meta := metas[r.name]
   499  		UIData, err := GetUIDataFromReader(r, &meta, UIMetaOptions)
   500  		Expect(err).Should(BeNil())
   501  
   502  		pkg, err := GetInstallPackageFromReader(r, &meta, UIData)
   503  		Expect(err).Should(BeNil())
   504  
   505  		h := NewAddonInstaller(ctx, k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, &Registry{Name: LocalAddonRegistryName}, map[string]interface{}{"example": "test-dry-run"}, nil, nil, DryRunAddon)
   506  
   507  		_, err = h.enableAddon(pkg)
   508  		Expect(err).Should(BeNil())
   509  
   510  		decoder := yaml3.NewDecoder(h.dryRunBuff)
   511  		for {
   512  			obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   513  			err := decoder.Decode(obj.Object)
   514  			if err != nil {
   515  				if errors.Is(err, io.EOF) {
   516  					break
   517  				}
   518  				Expect(err).Should(BeNil())
   519  			}
   520  			Expect(obj.GetNamespace()).Should(BeEquivalentTo(model.VelaSystemNS))
   521  			Expect(k8sClient.Create(ctx, obj)).Should(BeNil())
   522  		}
   523  
   524  		app := v1beta1.Application{}
   525  		Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-example"}, &app)).Should(BeNil())
   526  	})
   527  })
   528  
   529  var _ = Describe("test enable addon which applies the views independently", func() {
   530  	BeforeEach(func() {
   531  		app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-test-view"}}
   532  		Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   533  	})
   534  
   535  	It("test enable addon which applies the views independently", func() {
   536  		ctx := context.Background()
   537  		_, err := EnableAddonByLocalDir(ctx, "test-view", "./testdata/test-view", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
   538  		Expect(err).Should(BeNil())
   539  		app := v1beta1.Application{}
   540  		Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-test-view"}, &app)).Should(BeNil())
   541  		configMap := v1.ConfigMap{}
   542  		Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "pod-view"}, &configMap)).Should(BeNil())
   543  	})
   544  })
   545  
   546  var _ = Describe("test enable addon with notes", func() {
   547  	BeforeEach(func() {
   548  		app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-test-notes"}}
   549  		Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   550  		sec := v1.Secret{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-secret-test-notes"}}
   551  		Expect(k8sClient.Delete(ctx, &sec)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
   552  	})
   553  
   554  	It("test 'enable' addon which render notes output", func() {
   555  		ctx := context.Background()
   556  		addonInputArgs := map[string]interface{}{"example": "test"}
   557  		// inject runtime info
   558  		addonInputArgs[InstallerRuntimeOption] = map[string]interface{}{
   559  			"upgrade": false,
   560  		}
   561  		notes, err := EnableAddonByLocalDir(ctx, "test-notes", "./testdata/test-notes", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, addonInputArgs)
   562  		Expect(err).Should(BeNil())
   563  		app := v1beta1.Application{}
   564  		Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-test-notes"}, &app)).Should(BeNil())
   565  		Expect(notes).Should(ContainSubstring(`Thank you for your first installation!
   566  Please refer to URL.`))
   567  	})
   568  
   569  	It("test 'upgrade' addon which render notes output", func() {
   570  		ctx := context.Background()
   571  		addonInputArgs := map[string]interface{}{"example": "test"}
   572  		// inject runtime info
   573  		addonInputArgs[InstallerRuntimeOption] = map[string]interface{}{
   574  			"upgrade": true,
   575  		}
   576  		notes, err := EnableAddonByLocalDir(ctx, "test-notes-upgrade", "./testdata/test-notes", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, addonInputArgs)
   577  		Expect(err).Should(BeNil())
   578  		Expect(notes).Should(ContainSubstring(`Thank you for your upgrade!
   579  Please refer to URL.`))
   580  	})
   581  })
   582  
   583  var _ = Describe("test override defs of addon", func() {
   584  	It("test compDef exist", func() {
   585  		ctx := context.Background()
   586  		comp := v1beta1.ComponentDefinition{TypeMeta: metav1.TypeMeta{APIVersion: v1beta1.SchemeGroupVersion.String(), Kind: v1beta1.ComponentDefinitionKind}}
   587  		Expect(yaml.Unmarshal([]byte(helmCompDefYaml), &comp)).Should(BeNil())
   588  		Expect(k8sClient.Create(ctx, &comp)).Should(BeNil())
   589  
   590  		comp2 := v1beta1.ComponentDefinition{TypeMeta: metav1.TypeMeta{APIVersion: v1beta1.SchemeGroupVersion.String(), Kind: v1beta1.ComponentDefinitionKind}}
   591  		Expect(yaml.Unmarshal([]byte(kustomizeCompDefYaml), &comp2)).Should(BeNil())
   592  		Expect(k8sClient.Create(ctx, &comp2)).Should(BeNil())
   593  		app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Name: "addon-fluxcd"}}
   594  
   595  		comp3 := v1beta1.ComponentDefinition{TypeMeta: metav1.TypeMeta{APIVersion: v1beta1.SchemeGroupVersion.String(), Kind: v1beta1.ComponentDefinitionKind}}
   596  		Expect(yaml.Unmarshal([]byte(kustomizeCompDefYaml1), &comp3)).Should(BeNil())
   597  		Expect(k8sClient.Create(ctx, &comp3)).Should(BeNil())
   598  
   599  		compUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&comp)
   600  		Expect(err).Should(BeNil())
   601  		u := unstructured.Unstructured{Object: compUnstructured}
   602  		u.SetAPIVersion(v1beta1.SchemeGroupVersion.String())
   603  		u.SetKind(v1beta1.ComponentDefinitionKind)
   604  		u.SetLabels(map[string]string{"testUpdateLabel": "test"})
   605  		c, err := checkConflictDefs(ctx, k8sClient, []*unstructured.Unstructured{&u}, app.GetName())
   606  		Expect(err).Should(BeNil())
   607  		Expect(len(c)).Should(BeEquivalentTo(1))
   608  		// guarantee checkConflictDefs won't change source definition
   609  		Expect(u.GetLabels()["testUpdateLabel"]).Should(BeEquivalentTo("test"))
   610  
   611  		u.SetName("rollout")
   612  		c, err = checkConflictDefs(ctx, k8sClient, []*unstructured.Unstructured{&u}, app.GetName())
   613  		Expect(err).Should(BeNil())
   614  		Expect(len(c)).Should(BeEquivalentTo(0))
   615  
   616  		u.SetKind("NotExistKind")
   617  		_, err = checkConflictDefs(ctx, k8sClient, []*unstructured.Unstructured{&u}, app.GetName())
   618  		Expect(err).ShouldNot(BeNil())
   619  
   620  		compUnstructured2, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&comp2)
   621  		Expect(err).Should(BeNil())
   622  		u2 := &unstructured.Unstructured{Object: compUnstructured2}
   623  		u2.SetAPIVersion(v1beta1.SchemeGroupVersion.String())
   624  		u2.SetKind(v1beta1.ComponentDefinitionKind)
   625  		c, err = checkConflictDefs(ctx, k8sClient, []*unstructured.Unstructured{u2}, app.GetName())
   626  		Expect(err).Should(BeNil())
   627  		Expect(len(c)).Should(BeEquivalentTo(1))
   628  
   629  		compUnstructured3, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&comp3)
   630  		Expect(err).Should(BeNil())
   631  		u3 := &unstructured.Unstructured{Object: compUnstructured3}
   632  		u3.SetAPIVersion(v1beta1.SchemeGroupVersion.String())
   633  		u3.SetKind(v1beta1.ComponentDefinitionKind)
   634  		c, err = checkConflictDefs(ctx, k8sClient, []*unstructured.Unstructured{u3}, app.GetName())
   635  		Expect(err).Should(BeNil())
   636  		Expect(len(c)).Should(BeEquivalentTo(0))
   637  	})
   638  })
   639  
   640  const (
   641  	appYaml = `apiVersion: core.oam.dev/v1beta1
   642  kind: Application
   643  metadata:
   644    name: addon-test-app
   645  spec:
   646    components:
   647      - name: express-server
   648        type: webservice
   649        properties:
   650          image: crccheck/hello-world
   651          port: 8000
   652  `
   653  	legacyAppYaml = `apiVersion: core.oam.dev/v1beta1
   654  kind: Application
   655  metadata:
   656    name: legacy-addon
   657  spec:
   658    components:
   659      - name: express-server
   660        type: webservice
   661        properties:
   662          image: crccheck/hello-world
   663          port: 8000
   664  `
   665  	legacyAppYamlLocal = `apiVersion: core.oam.dev/v1beta1
   666  kind: Application
   667  metadata:
   668    name: legacy-addon-local
   669    labels:
   670      addons.oam.dev/registry: local
   671  spec:
   672    components:
   673      - name: express-server
   674        type: webservice
   675        properties:
   676          image: crccheck/hello-world
   677          port: 8000
   678  `
   679  	legacyAppYamlNoClustersArg = `apiVersion: core.oam.dev/v1beta1
   680  kind: Application
   681  metadata:
   682    name: addon-vela-workflow
   683  spec:
   684    components:
   685      - name: express-server
   686        type: webservice
   687        properties:
   688          image: crccheck/hello-world
   689          port: 8000
   690  `
   691  	legacyAppYamlHasClustersArg = `apiVersion: core.oam.dev/v1beta1
   692  kind: Application
   693  metadata:
   694    name: addon-has-clusters-arg
   695  spec:
   696    components:
   697      - name: express-server
   698        type: webservice
   699        properties:
   700          image: crccheck/hello-world
   701          port: 8000
   702  `
   703  	legacySecretYamlHasClustersArg = `apiVersion: v1
   704  data:
   705    addonParameterDataKey: eyJjbHVzdGVycyI6WyJsb2NhbCJdfQ==
   706  kind: Secret
   707  metadata:
   708    name: addon-secret-has-clusters-arg
   709    namespace: vela-system
   710  type: Opaque
   711  `
   712  	legacy2AppYaml = `apiVersion: core.oam.dev/v1beta1
   713  kind: Application
   714  metadata:
   715    name: legacy-addon2
   716  spec:
   717    components:
   718      - name: express-server
   719        type: webservice
   720        properties:
   721          image: crccheck/hello-world
   722          port: 8000
   723    policies:
   724      - name: target-default
   725        type: topology
   726        properties:
   727          clusters: ["local"]
   728          namespace: "default"
   729  `
   730  	legacy3AppYaml = `apiVersion: core.oam.dev/v1beta1
   731  kind: Application
   732  metadata:
   733    name: legacy-addon3
   734  spec:
   735    components:
   736      - name: express-server
   737        type: webservice
   738        properties:
   739          image: crccheck/hello-world
   740          port: 8000
   741    policies:
   742      - name: target-default
   743        type: topology
   744        properties:
   745          clusterLabelSelector: {}
   746          namespace: "default"
   747  `
   748  	secretYaml = `apiVersion: v1
   749  data:
   750    addonParameterDataKey: eyJjbHVzdGVycyI6WyJsb2NhbCIsInZlbGEtbTEiXX0K
   751  kind: Secret
   752  metadata:
   753    name: addon-secret-fluxcd
   754    namespace: vela-system
   755  type: Opaque
   756  `
   757  	secretErrorYaml = `apiVersion: v1
   758  data:
   759    addonParameterDataKey: eyJjbHVzdGVycyI6WyJsb2NhbCIsInZlbGEtbTEiXQo=
   760  kind: Secret
   761  metadata:
   762    name: addon-secret-fluxcd1
   763    namespace: vela-system
   764  type: Opaque
   765  `
   766  	deployYaml = `apiVersion: apps/v1
   767  kind: Deployment
   768  metadata:
   769    name: kubevela-vela-core
   770    namespace: vela-system
   771    labels:
   772       controller.oam.dev/name: vela-core
   773  spec:
   774    progressDeadlineSeconds: 600
   775    replicas: 1
   776    revisionHistoryLimit: 10
   777    selector:
   778      matchLabels:
   779        app.kubernetes.io/instance: kubevela
   780        app.kubernetes.io/name: vela-core
   781    strategy:
   782      rollingUpdate:
   783        maxSurge: 25%
   784        maxUnavailable: 25%
   785      type: RollingUpdate
   786    template:
   787      metadata:
   788        annotations:
   789          prometheus.io/path: /metrics
   790          prometheus.io/port: "8080"
   791          prometheus.io/scrape: "true"
   792        labels:
   793          app.kubernetes.io/instance: kubevela
   794          app.kubernetes.io/name: vela-core
   795      spec:
   796        containers:
   797        - args:
   798          image: oamdev/vela-core:v1.2.3
   799          imagePullPolicy: Always
   800          name: kubevela
   801          ports:
   802          - containerPort: 9443
   803            name: webhook-server
   804            protocol: TCP
   805          - containerPort: 9440
   806            name: healthz
   807            protocol: TCP
   808          resources:
   809            limits:
   810              cpu: 500m
   811              memory: 1Gi
   812            requests:
   813              cpu: 50m
   814              memory: 20Mi
   815        dnsPolicy: ClusterFirst
   816        restartPolicy: Always
   817        schedulerName: default-scheduler
   818        securityContext: {}
   819        terminationGracePeriodSeconds: 30`
   820  
   821  	addonUpdateAppYaml = `
   822  apiVersion: core.oam.dev/v1beta1
   823  kind: Application
   824  metadata:
   825    name: addon-test-update
   826    namespace: vela-system
   827    labels:
   828      addons.oam.dev/version: v1.2.0
   829  spec:
   830    components:
   831      - name: express-server
   832        type: webservice
   833        properties:
   834          image: crccheck/hello-world
   835          port: 8000
   836  `
   837  	helmCompDefYaml = `
   838  apiVersion: core.oam.dev/v1beta1
   839  kind: ComponentDefinition
   840  metadata:
   841    name: helm
   842    namespace: vela-system
   843    ownerReferences:
   844    - apiVersion: core.oam.dev/v1beta1
   845      blockOwnerDeletion: true
   846      controller: true
   847      kind: Application
   848      name: addon-fluxcd-helm
   849      uid: 73c47933-002e-4182-a673-6da6a9dcf080
   850  spec:
   851    schematic:
   852      cue:
   853  `
   854  	kustomizeCompDefYaml = `
   855  apiVersion: core.oam.dev/v1beta1
   856  kind: ComponentDefinition
   857  metadata:
   858    name: kustomize
   859    namespace: vela-system
   860  spec:
   861    schematic:
   862      cue:
   863  `
   864  	kustomizeCompDefYaml1 = `
   865  apiVersion: core.oam.dev/v1beta1
   866  kind: ComponentDefinition
   867  metadata:
   868    name: kustomize-another
   869    namespace: vela-system
   870    ownerReferences:
   871    - apiVersion: core.oam.dev/v1beta1
   872      blockOwnerDeletion: true
   873      controller: true
   874      kind: Application
   875      name: addon-fluxcd
   876      uid: 73c47933-002e-4182-a673-6da6a9dcf080
   877  spec:
   878    schematic:
   879      cue:
   880  `
   881  )