github.com/oam-dev/kubevela@v1.9.11/pkg/appfile/appfile_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 appfile 18 19 import ( 20 "context" 21 "encoding/json" 22 "testing" 23 24 "cuelang.org/go/cue/cuecontext" 25 "github.com/crossplane/crossplane-runtime/pkg/test" 26 "github.com/google/go-cmp/cmp" 27 terraformtypes "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime" 28 terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2" 29 . "github.com/onsi/ginkgo/v2" 30 . "github.com/onsi/gomega" 31 "github.com/pkg/errors" 32 "github.com/stretchr/testify/assert" 33 appsv1 "k8s.io/api/apps/v1" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 36 "k8s.io/apimachinery/pkg/runtime" 37 "sigs.k8s.io/yaml" 38 39 "github.com/kubevela/workflow/pkg/cue/model" 40 41 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 42 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 43 oamtypes "github.com/oam-dev/kubevela/apis/types" 44 "github.com/oam-dev/kubevela/pkg/cue/definition" 45 "github.com/oam-dev/kubevela/pkg/cue/process" 46 "github.com/oam-dev/kubevela/pkg/oam" 47 "github.com/oam-dev/kubevela/pkg/oam/util" 48 ) 49 50 var _ = Describe("Test Workflow", func() { 51 It("generate workflow task runners", func() { 52 workflowStepDef := v1beta1.WorkflowStepDefinition{ 53 Spec: v1beta1.WorkflowStepDefinitionSpec{ 54 Schematic: &common.Schematic{ 55 CUE: &common.CUE{ 56 Template: ` 57 wait: op.#ConditionalWait & { 58 continue: true 59 } 60 `, 61 }, 62 }, 63 }, 64 } 65 workflowStepDef.Name = "test-wait" 66 workflowStepDef.Namespace = "default" 67 err := k8sClient.Create(context.Background(), &workflowStepDef) 68 Expect(err).To(BeNil()) 69 70 notCueStepDef := v1beta1.WorkflowStepDefinition{ 71 Spec: v1beta1.WorkflowStepDefinitionSpec{ 72 Schematic: &common.Schematic{}, 73 }, 74 } 75 76 notCueStepDef.Name = "not-cue" 77 notCueStepDef.Namespace = "default" 78 err = k8sClient.Create(context.Background(), ¬CueStepDef) 79 Expect(err).To(BeNil()) 80 }) 81 }) 82 83 var _ = Describe("Test Terraform schematic appfile", func() { 84 It("workload capability is Terraform", func() { 85 var ( 86 ns = "default" 87 compName = "sample-db" 88 appName = "webapp" 89 revision = "v1" 90 configuration = ` 91 module "rds" { 92 source = "terraform-alicloud-modules/rds/alicloud" 93 engine = "MySQL" 94 engine_version = "8.0" 95 instance_type = "rds.mysql.c1.large" 96 instance_storage = "20" 97 instance_name = var.instance_name 98 account_name = var.account_name 99 password = var.password 100 } 101 102 output "DB_NAME" { 103 value = module.rds.this_db_instance_name 104 } 105 output "DB_USER" { 106 value = module.rds.this_db_database_account 107 } 108 output "DB_PORT" { 109 value = module.rds.this_db_instance_port 110 } 111 output "DB_HOST" { 112 value = module.rds.this_db_instance_connection_string 113 } 114 output "DB_PASSWORD" { 115 value = module.rds.this_db_instance_port 116 } 117 118 variable "instance_name" { 119 description = "RDS instance name" 120 type = string 121 default = "poc" 122 } 123 124 variable "account_name" { 125 description = "RDS instance user account name" 126 type = "string" 127 default = "oam" 128 } 129 130 variable "password" { 131 description = "RDS instance account password" 132 type = "string" 133 default = "Xyfff83jfewGGfaked" 134 } 135 ` 136 ) 137 138 wl := &Component{ 139 Name: "sample-db", 140 FullTemplate: &Template{ 141 Terraform: &common.Terraform{ 142 Configuration: configuration, 143 Type: "hcl", 144 }, 145 ComponentDefinition: &v1beta1.ComponentDefinition{ 146 Spec: v1beta1.ComponentDefinitionSpec{ 147 Schematic: &common.Schematic{ 148 Terraform: &common.Terraform{}, 149 }, 150 }, 151 }, 152 }, 153 CapabilityCategory: oamtypes.TerraformCategory, 154 Params: map[string]interface{}{ 155 "account_name": "oamtest", 156 "writeConnectionSecretToRef": map[string]interface{}{ 157 "name": "db", 158 }, 159 process.OutputSecretName: "db-conn", 160 }, 161 } 162 163 af := &Appfile{ 164 ParsedComponents: []*Component{wl}, 165 Name: appName, 166 AppRevisionName: revision, 167 Namespace: ns, 168 } 169 170 variable := map[string]interface{}{"account_name": "oamtest"} 171 data, _ := json.Marshal(variable) 172 raw := &runtime.RawExtension{} 173 raw.Raw = data 174 175 workload := terraformapi.Configuration{ 176 ObjectMeta: metav1.ObjectMeta{ 177 Labels: map[string]string{ 178 "app.oam.dev/appRevision": "v1", 179 "app.oam.dev/component": "sample-db", 180 "app.oam.dev/name": "webapp", 181 "workload.oam.dev/type": "", 182 }, 183 Name: "sample-db", 184 Namespace: "default", 185 }, 186 187 Spec: terraformapi.ConfigurationSpec{ 188 HCL: configuration, 189 Variable: raw, 190 }, 191 Status: terraformapi.ConfigurationStatus{}, 192 } 193 workload.Spec.WriteConnectionSecretToReference = &terraformtypes.SecretReference{Name: "db", Namespace: "default"} 194 195 expectCompManifest := &oamtypes.ComponentManifest{ 196 Name: compName, 197 ComponentOutput: func() *unstructured.Unstructured { 198 r, _ := util.Object2Unstructured(workload) 199 return r 200 }(), 201 } 202 203 comps, err := af.GenerateComponentManifests() 204 diff := cmp.Diff(comps[0], expectCompManifest) 205 Expect(diff).ShouldNot(BeEmpty()) 206 Expect(err).Should(BeNil()) 207 }) 208 }) 209 210 func TestSetParameterValuesToKubeObj(t *testing.T) { 211 tests := map[string]struct { 212 reason string 213 obj unstructured.Unstructured 214 values paramValueSettings 215 wantObj unstructured.Unstructured 216 wantErr error 217 }{ 218 "InvalidStringType": { 219 reason: "An error should be returned", 220 values: paramValueSettings{ 221 "strParam": paramValueSetting{ 222 Value: int32(100), 223 ValueType: common.StringType, 224 FieldPaths: []string{"spec.test"}, 225 }, 226 }, 227 wantErr: errors.Errorf(errInvalidValueType, common.StringType), 228 }, 229 "InvalidNumberType": { 230 reason: "An error should be returned", 231 values: paramValueSettings{ 232 "intParam": paramValueSetting{ 233 Value: "test", 234 ValueType: common.NumberType, 235 FieldPaths: []string{"spec.test"}, 236 }, 237 }, 238 wantErr: errors.Errorf(errInvalidValueType, common.NumberType), 239 }, 240 "InvalidBoolType": { 241 reason: "An error should be returned", 242 values: paramValueSettings{ 243 "boolParam": paramValueSetting{ 244 Value: "test", 245 ValueType: common.BooleanType, 246 FieldPaths: []string{"spec.test"}, 247 }, 248 }, 249 wantErr: errors.Errorf(errInvalidValueType, common.BooleanType), 250 }, 251 "InvalidFieldPath": { 252 reason: "An error should be returned", 253 values: paramValueSettings{ 254 "strParam": paramValueSetting{ 255 Value: "test", 256 ValueType: common.StringType, 257 FieldPaths: []string{"spec[.test"}, // a invalid field path 258 }, 259 }, 260 wantErr: errors.Wrap(errors.New(`cannot parse path "spec[.test": unterminated '[' at position 4`), 261 `cannot set parameter "strParam" to field "spec[.test"`), 262 }, 263 "Succeed": { 264 reason: "No error should be returned", 265 obj: unstructured.Unstructured{Object: make(map[string]interface{})}, 266 values: paramValueSettings{ 267 "strParam": paramValueSetting{ 268 Value: "test", 269 ValueType: common.StringType, 270 FieldPaths: []string{"spec.strField"}, 271 }, 272 "intParam": paramValueSetting{ 273 Value: 10, 274 ValueType: common.NumberType, 275 FieldPaths: []string{"spec.intField"}, 276 }, 277 "floatParam": paramValueSetting{ 278 Value: float64(10.01), 279 ValueType: common.NumberType, 280 FieldPaths: []string{"spec.floatField"}, 281 }, 282 "boolParam": paramValueSetting{ 283 Value: true, 284 ValueType: common.BooleanType, 285 FieldPaths: []string{"spec.boolField"}, 286 }, 287 }, 288 wantObj: unstructured.Unstructured{Object: map[string]interface{}{ 289 "spec": map[string]interface{}{ 290 "strField": "test", 291 "intField": int64(10), 292 "floatField": float64(10.01), 293 "boolField": true, 294 }, 295 }}, 296 }, 297 } 298 299 for tcName, tc := range tests { 300 t.Run(tcName, func(t *testing.T) { 301 obj := tc.obj.DeepCopy() 302 err := setParameterValuesToKubeObj(obj, tc.values) 303 if diff := cmp.Diff(tc.wantObj, *obj); diff != "" { 304 t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff) 305 } 306 if diff := cmp.Diff(tc.wantErr, err, test.EquateErrors()); diff != "" { 307 t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff) 308 } 309 }) 310 } 311 312 } 313 314 var _ = Describe("Test evalWorkloadWithContext", func() { 315 It("workload capability is Terraform", func() { 316 var ( 317 ns = "default" 318 compName = "sample-db" 319 err error 320 ) 321 type appArgs struct { 322 wl *Component 323 appName string 324 revision string 325 } 326 327 args := appArgs{ 328 wl: &Component{ 329 Name: compName, 330 FullTemplate: &Template{ 331 Terraform: &common.Terraform{ 332 Configuration: ` 333 module "rds" { 334 source = "terraform-alicloud-modules/rds/alicloud" 335 engine = "MySQL" 336 engine_version = "8.0" 337 instance_type = "rds.mysql.c1.large" 338 instance_storage = "20" 339 instance_name = var.instance_name 340 account_name = var.account_name 341 password = var.password 342 } 343 344 output "DB_NAME" { 345 value = module.rds.this_db_instance_name 346 } 347 output "DB_USER" { 348 value = module.rds.this_db_database_account 349 } 350 output "DB_PORT" { 351 value = module.rds.this_db_instance_port 352 } 353 output "DB_HOST" { 354 value = module.rds.this_db_instance_connection_string 355 } 356 output "DB_PASSWORD" { 357 value = module.rds.this_db_instance_port 358 } 359 360 variable "instance_name" { 361 description = "RDS instance name" 362 type = string 363 default = "poc" 364 } 365 366 variable "account_name" { 367 description = "RDS instance user account name" 368 type = "string" 369 default = "oam" 370 } 371 372 variable "password" { 373 description = "RDS instance account password" 374 type = "string" 375 default = "Xyfff83jfewGGfaked" 376 } 377 `, 378 Type: "hcl", 379 }, 380 ComponentDefinition: &v1beta1.ComponentDefinition{ 381 Spec: v1beta1.ComponentDefinitionSpec{ 382 Schematic: &common.Schematic{ 383 Terraform: &common.Terraform{}, 384 }, 385 }, 386 }, 387 }, 388 CapabilityCategory: oamtypes.TerraformCategory, 389 engine: definition.NewWorkloadAbstractEngine(compName, pd), 390 Params: map[string]interface{}{ 391 "variable": map[string]interface{}{ 392 "account_name": "oamtest", 393 }, 394 }, 395 }, 396 appName: "webapp", 397 revision: "v1", 398 } 399 400 ctxData := GenerateContextDataFromAppFile(&Appfile{ 401 Name: args.appName, 402 Namespace: ns, 403 AppRevisionName: args.revision, 404 }, args.wl.Name) 405 pCtx := NewBasicContext(ctxData, args.wl.Params) 406 comp, err := evalWorkloadWithContext(pCtx, args.wl, ns, args.appName) 407 Expect(comp.ComponentOutput).ShouldNot(BeNil()) 408 Expect(comp.Name).Should(Equal("")) 409 Expect(err).Should(BeNil()) 410 }) 411 }) 412 413 func TestGenerateTerraformConfigurationWorkload(t *testing.T) { 414 var ( 415 name = "oss" 416 ns = "default" 417 variable = map[string]interface{}{"acl": "private"} 418 ) 419 420 ch := make(chan string) 421 badParam := map[string]interface{}{"abc": ch} 422 _, badParamMarshalError := json.Marshal(badParam) 423 424 type args struct { 425 writeConnectionSecretToRef *terraformtypes.SecretReference 426 hcl string 427 remote string 428 params map[string]interface{} 429 providerRef *terraformtypes.Reference 430 } 431 432 type want struct { 433 err error 434 } 435 436 testcases := map[string]struct { 437 args args 438 want want 439 }{ 440 "invalid ComponentDefinition": { 441 args: args{ 442 hcl: "abc", 443 params: map[string]interface{}{"acl": "private", 444 "writeConnectionSecretToRef": map[string]interface{}{"name": "oss", "namespace": "default"}}, 445 }, 446 want: want{err: errors.New("terraform component definition is not valid")}, 447 }, 448 "valid hcl workload": { 449 args: args{ 450 hcl: "abc", 451 params: map[string]interface{}{"acl": "private", 452 "writeConnectionSecretToRef": map[string]interface{}{"name": "oss", "namespace": "default"}}, 453 writeConnectionSecretToRef: &terraformtypes.SecretReference{Name: "oss", Namespace: "default"}, 454 }, 455 want: want{err: nil}}, 456 "valid hcl workload, and there are some custom params compared to ComponentDefinition": { 457 args: args{ 458 hcl: "def", 459 params: map[string]interface{}{"acl": "private", 460 "writeConnectionSecretToRef": map[string]interface{}{"name": "oss2", "namespace": "default2"}, 461 "providerRef": map[string]interface{}{"name": "aws2", "namespace": "default2"}}, 462 writeConnectionSecretToRef: &terraformtypes.SecretReference{Name: "oss", Namespace: "default"}, 463 providerRef: &terraformtypes.Reference{Name: "aws", Namespace: "default"}, 464 }, 465 want: want{err: nil}, 466 }, 467 "valid hcl workload, but the namespace of WriteConnectionSecretToReference is empty": { 468 args: args{ 469 hcl: "abc", 470 params: map[string]interface{}{"acl": "private", 471 "writeConnectionSecretToRef": map[string]interface{}{"name": "oss", "namespace": "default"}}, 472 writeConnectionSecretToRef: &terraformtypes.SecretReference{Name: "oss"}, 473 }, 474 want: want{err: nil}}, 475 476 "remote hcl workload": { 477 args: args{ 478 remote: "https://xxx/a.git", 479 params: map[string]interface{}{"acl": "private", 480 "writeConnectionSecretToRef": map[string]interface{}{"name": "oss", "namespace": "default"}}, 481 writeConnectionSecretToRef: &terraformtypes.SecretReference{Name: "oss", Namespace: "default"}, 482 }, 483 want: want{err: nil}}, 484 485 "workload's TF configuration is empty": { 486 args: args{ 487 params: variable, 488 }, 489 want: want{err: errors.New(errTerraformConfigurationIsNotSet)}, 490 }, 491 492 "workload's params is bad": { 493 args: args{ 494 params: badParam, 495 hcl: "abc", 496 }, 497 want: want{err: errors.Wrap(badParamMarshalError, errFailToConvertTerraformComponentProperties)}, 498 }, 499 500 "terraform workload has a provider reference, but parameters are bad": { 501 args: args{ 502 params: badParam, 503 hcl: "abc", 504 providerRef: &terraformtypes.Reference{Name: "azure", Namespace: "default"}, 505 }, 506 want: want{err: errors.Wrap(badParamMarshalError, errFailToConvertTerraformComponentProperties)}, 507 }, 508 "terraform workload has a provider reference": { 509 args: args{ 510 params: variable, 511 hcl: "variable \"name\" {\n description = \"Name to be used on all resources as prefix. Default to 'TF-Module-EIP'.\"\n default = \"TF-Module-EIP\"\n type = string\n }", 512 providerRef: &terraformtypes.Reference{Name: "aws", Namespace: "default"}, 513 }, 514 want: want{err: nil}, 515 }, 516 } 517 518 for tcName, tc := range testcases { 519 t.Run(tcName, func(t *testing.T) { 520 521 var ( 522 template *Template 523 configSpec terraformapi.ConfigurationSpec 524 ) 525 data, _ := json.Marshal(variable) 526 raw := &runtime.RawExtension{} 527 raw.Raw = data 528 if tc.args.hcl != "" { 529 template = &Template{ 530 Terraform: &common.Terraform{ 531 Configuration: tc.args.hcl, 532 Type: "hcl", 533 }, 534 } 535 configSpec = terraformapi.ConfigurationSpec{ 536 HCL: tc.args.hcl, 537 Variable: raw, 538 } 539 configSpec.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef 540 } 541 if tc.args.remote != "" { 542 template = &Template{ 543 Terraform: &common.Terraform{ 544 Configuration: tc.args.remote, 545 Type: "remote", 546 }, 547 } 548 configSpec = terraformapi.ConfigurationSpec{ 549 Remote: tc.args.remote, 550 Variable: raw, 551 } 552 configSpec.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef 553 } 554 if tc.args.hcl == "" && tc.args.remote == "" { 555 template = &Template{ 556 Terraform: &common.Terraform{}, 557 } 558 559 configSpec = terraformapi.ConfigurationSpec{ 560 Variable: raw, 561 } 562 configSpec.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef 563 } 564 tf := &common.Terraform{} 565 if tc.args.providerRef != nil { 566 tf.ProviderReference = tc.args.providerRef 567 configSpec.ProviderReference = tc.args.providerRef 568 } 569 if tc.args.writeConnectionSecretToRef != nil { 570 tf.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef 571 configSpec.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef 572 if tc.args.writeConnectionSecretToRef.Namespace == "" { 573 configSpec.WriteConnectionSecretToReference.Namespace = ns 574 } 575 } 576 577 if tc.args.providerRef != nil || tc.args.writeConnectionSecretToRef != nil { 578 template.ComponentDefinition = &v1beta1.ComponentDefinition{ 579 Spec: v1beta1.ComponentDefinitionSpec{ 580 Schematic: &common.Schematic{ 581 Terraform: tf, 582 }, 583 }, 584 } 585 } 586 587 if tc.args.hcl == "def" { 588 configSpec.WriteConnectionSecretToReference = &terraformtypes.SecretReference{ 589 Name: "oss2", 590 Namespace: "default2", 591 } 592 configSpec.ProviderReference = &terraformtypes.Reference{ 593 Name: "aws2", 594 Namespace: "default2", 595 } 596 } 597 598 wl := &Component{ 599 FullTemplate: template, 600 Name: name, 601 Params: tc.args.params, 602 } 603 604 got, err := generateTerraformConfigurationWorkload(wl, ns) 605 if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { 606 t.Errorf("\n%s\ngenerateTerraformConfigurationWorkload(...): -want error, +got error:\n%s\n", tcName, diff) 607 } 608 609 if err == nil { 610 tfConfiguration := terraformapi.Configuration{ 611 TypeMeta: metav1.TypeMeta{APIVersion: "terraform.core.oam.dev/v1beta2", Kind: "Configuration"}, 612 ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}, 613 Spec: configSpec, 614 } 615 rawConf := util.Object2RawExtension(tfConfiguration) 616 wantWL, _ := util.RawExtension2Unstructured(rawConf) 617 618 if diff := cmp.Diff(wantWL, got); diff != "" { 619 t.Errorf("\n%s\ngenerateTerraformConfigurationWorkload(...): -want, +got:\n%s\n", tcName, diff) 620 } 621 } 622 }) 623 } 624 } 625 626 func TestPrepareArtifactsData(t *testing.T) { 627 compManifests := []*oamtypes.ComponentManifest{ 628 { 629 Name: "readyComp", 630 Namespace: "ns", 631 RevisionName: "readyComp-v1", 632 ComponentOutput: &unstructured.Unstructured{Object: map[string]interface{}{ 633 "fake": "workload", 634 }}, 635 ComponentOutputsAndTraits: func() []*unstructured.Unstructured { 636 ingressYAML := `apiVersion: networking.k8s.io/v1 637 kind: Ingress 638 metadata: 639 labels: 640 trait.oam.dev/resource: ingress 641 trait.oam.dev/type: ingress 642 namespace: default 643 spec: 644 rules: 645 - host: testsvc.example.com` 646 ingress := &unstructured.Unstructured{} 647 _ = yaml.Unmarshal([]byte(ingressYAML), ingress) 648 svcYAML := `apiVersion: v1 649 kind: Service 650 metadata: 651 labels: 652 trait.oam.dev/resource: service 653 trait.oam.dev/type: ingress 654 namespace: default 655 spec: 656 clusterIP: 10.96.185.119 657 selector: 658 app.oam.dev/component: express-server 659 type: ClusterIP` 660 svc := &unstructured.Unstructured{} 661 _ = yaml.Unmarshal([]byte(svcYAML), svc) 662 return []*unstructured.Unstructured{ingress, svc} 663 }(), 664 }, 665 } 666 667 gotArtifacts := prepareArtifactsData(compManifests) 668 gotWorkload, _, err := unstructured.NestedMap(gotArtifacts, "readyComp", "workload") 669 assert.NoError(t, err) 670 diff := cmp.Diff(gotWorkload, map[string]interface{}{"fake": string("workload")}) 671 assert.Equal(t, diff, "") 672 673 _, gotIngress, err := unstructured.NestedMap(gotArtifacts, "readyComp", "traits", "ingress", "ingress") 674 assert.NoError(t, err) 675 if !gotIngress { 676 t.Fatalf("cannot get ingress trait") 677 } 678 _, gotSvc, err := unstructured.NestedMap(gotArtifacts, "readyComp", "traits", "ingress", "service") 679 assert.NoError(t, err) 680 if !gotSvc { 681 t.Fatalf("cannot get service trait") 682 } 683 684 } 685 686 func TestBaseGenerateComponent(t *testing.T) { 687 var appName = "test-app" 688 var ns = "test-ns" 689 var traitName = "mytrait" 690 var wlName = "my-wl-1" 691 var workflowName = "my-wf" 692 var publishVersion = "123" 693 ctxData := GenerateContextDataFromAppFile(&Appfile{ 694 Name: appName, 695 Namespace: ns, 696 AppAnnotations: map[string]string{ 697 oam.AnnotationWorkflowName: workflowName, 698 oam.AnnotationPublishVersion: publishVersion, 699 }, 700 }, wlName) 701 pContext := NewBasicContext(ctxData, nil) 702 base := ` 703 apiVersion: "apps/v1" 704 kind: "Deployment" 705 spec: { 706 template: { 707 spec: containers: [{ 708 image: "nginx" 709 }] 710 } 711 } 712 ` 713 inst := cuecontext.New().CompileString(base) 714 bs, _ := model.NewBase(inst.Value()) 715 err := pContext.SetBase(bs) 716 assert.NoError(t, err) 717 tr := &Trait{ 718 Name: traitName, 719 engine: definition.NewTraitAbstractEngine(traitName, nil), 720 Template: `outputs:mytrait:{ 721 if context.componentType == "stateless" { 722 kind: "Deployment" 723 } 724 if context.componentType == "stateful" { 725 kind: "StatefulSet" 726 } 727 name: context.name 728 envSourceContainerName: context.name 729 workflowName: context.workflowName 730 publishVersion: context.publishVersion 731 }`, 732 } 733 wl := &Component{Type: "stateful", Traits: []*Trait{tr}} 734 cm, err := baseGenerateComponent(pContext, wl, appName, ns) 735 assert.NoError(t, err) 736 assert.Equal(t, cm.ComponentOutputsAndTraits[0].Object["kind"], "StatefulSet") 737 assert.Equal(t, cm.ComponentOutputsAndTraits[0].Object["workflowName"], workflowName) 738 assert.Equal(t, cm.ComponentOutputsAndTraits[0].Object["publishVersion"], publishVersion) 739 } 740 741 var _ = Describe("Test use context.appLabels& context.appAnnotations in componentDefinition ", func() { 742 It("Test generate AppConfig resources from ", func() { 743 af := &Appfile{ 744 Name: "app", 745 Namespace: "ns", 746 AppLabels: map[string]string{ 747 "lk1": "lv1", 748 "lk2": "lv2", 749 }, 750 AppAnnotations: map[string]string{ 751 "ak1": "av1", 752 "ak2": "av2", 753 }, 754 ParsedComponents: []*Component{ 755 { 756 Name: "comp1", 757 Type: "deployment", 758 Params: map[string]interface{}{ 759 "image": "busybox", 760 "cmd": []interface{}{"sleep", "1000"}, 761 }, 762 engine: definition.NewWorkloadAbstractEngine("myweb", pd), 763 FullTemplate: &Template{ 764 TemplateStr: ` 765 output: { 766 apiVersion: "apps/v1" 767 kind: "Deployment" 768 spec: { 769 selector: matchLabels: { 770 "app.oam.dev/component": context.name 771 } 772 773 template: { 774 metadata: { 775 labels: { 776 if context.appLabels != _|_ { 777 context.appLabels 778 } 779 } 780 annotations: { 781 if context.appAnnotations != _|_ { 782 context.appAnnotations 783 } 784 } 785 } 786 787 spec: { 788 containers: [{ 789 name: context.name 790 image: parameter.image 791 792 if parameter["cmd"] != _|_ { 793 command: parameter.cmd 794 } 795 }] 796 } 797 } 798 799 selector: 800 matchLabels: 801 "app.oam.dev/component": context.name 802 } 803 } 804 805 parameter: { 806 // +usage=Which image would you like to use for your service 807 // +short=i 808 image: string 809 810 cmd?: [...string] 811 }`}, 812 }, 813 }, 814 } 815 By("Generate ComponentManifests") 816 componentManifests, err := af.GenerateComponentManifests() 817 Expect(err).To(BeNil()) 818 By("Verify expected ComponentManifest") 819 deployment := &appsv1.Deployment{} 820 runtime.DefaultUnstructuredConverter.FromUnstructured(componentManifests[0].ComponentOutput.Object, deployment) 821 labels := deployment.Spec.Template.Labels 822 annotations := deployment.Spec.Template.Annotations 823 Expect(cmp.Diff(len(labels), 2)).Should(BeEmpty()) 824 Expect(cmp.Diff(len(annotations), 2)).Should(BeEmpty()) 825 Expect(cmp.Diff(labels["lk1"], "lv1")).Should(BeEmpty()) 826 Expect(cmp.Diff(annotations["ak1"], "av1")).Should(BeEmpty()) 827 }) 828 829 })