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 )