diff --git a/pkg/appfile/appfile.go b/pkg/appfile/appfile.go index 15d952219..c58dbefd2 100644 --- a/pkg/appfile/appfile.go +++ b/pkg/appfile/appfile.go @@ -96,7 +96,7 @@ func (wl *Workload) EvalContext(ctx process.Context) error { // EvalStatus eval workload status func (wl *Workload) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) { - return wl.engine.Status(ctx, cli, ns, wl.FullTemplate.CustomStatus) + return wl.engine.Status(ctx, cli, ns, wl.FullTemplate.CustomStatus, wl.Params) } // EvalHealth eval workload health check @@ -149,7 +149,7 @@ func (trait *Trait) EvalContext(ctx process.Context) error { // EvalStatus eval trait status func (trait *Trait) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) { - return trait.engine.Status(ctx, cli, ns, trait.CustomStatusFormat) + return trait.engine.Status(ctx, cli, ns, trait.CustomStatusFormat, trait.Params) } // EvalHealth eval trait health check diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/apply.go b/pkg/controller/core.oam.dev/v1alpha2/application/apply.go index 159387085..cd9863b1a 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/apply.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/apply.go @@ -285,33 +285,32 @@ func (h *appHandler) statusAggregate(appFile *appfile.Appfile) ([]common.Applica } } + var traitStatusList []common.ApplicationTraitStatus for _, tr := range wl.Traits { if err := tr.EvalContext(pCtx); err != nil { return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate context error", appFile.Name, wl.Name, tr.Name) } - } - var traitStatusList []common.ApplicationTraitStatus - for _, trait := range wl.Traits { var traitStatus = common.ApplicationTraitStatus{ - Type: trait.Name, + Type: tr.Name, Healthy: true, } - traitHealth, err := trait.EvalHealth(pCtx, h.r, h.app.Namespace) + traitHealth, err := tr.EvalHealth(pCtx, h.r, h.app.Namespace) if err != nil { - return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, check health error", appFile.Name, wl.Name, trait.Name) + return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, check health error", appFile.Name, wl.Name, tr.Name) } if !traitHealth { // TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough traitStatus.Healthy = false healthy = false } - traitStatus.Message, err = trait.EvalStatus(pCtx, h.r, h.app.Namespace) + traitStatus.Message, err = tr.EvalStatus(pCtx, h.r, h.app.Namespace) if err != nil { - return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate status message error", appFile.Name, wl.Name, trait.Name) + return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate status message error", appFile.Name, wl.Name, tr.Name) } traitStatusList = append(traitStatusList, traitStatus) } + status.Traits = traitStatusList status.Scopes = generateScopeReference(wl.Scopes) appStatus = append(appStatus, status) diff --git a/pkg/dsl/definition/template.go b/pkg/dsl/definition/template.go index 98b1bede2..5f8ba2e46 100644 --- a/pkg/dsl/definition/template.go +++ b/pkg/dsl/definition/template.go @@ -58,7 +58,7 @@ const ( type AbstractEngine interface { Complete(ctx process.Context, abstractTemplate string, params interface{}) error HealthCheck(ctx process.Context, cli client.Client, ns string, healthPolicyTemplate string) (bool, error) - Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string) (string, error) + Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error) } type def struct { @@ -223,7 +223,7 @@ func checkHealth(templateContext map[string]interface{}, healthPolicyTemplate st } // Status get workload status by customStatusTemplate -func (wd *workloadDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string) (string, error) { +func (wd *workloadDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error) { if customStatusTemplate == "" { return "", nil } @@ -231,15 +231,28 @@ func (wd *workloadDef) Status(ctx process.Context, cli client.Client, ns string, if err != nil { return "", errors.WithMessage(err, "get template context") } - return getStatusMessage(templateContext, customStatusTemplate) + return getStatusMessage(templateContext, customStatusTemplate, parameter) } -func getStatusMessage(templateContext map[string]interface{}, customStatusTemplate string) (string, error) { +func getStatusMessage(templateContext map[string]interface{}, customStatusTemplate string, parameter interface{}) (string, error) { + var ctxBuff string + var paramBuff = "parameter: {}\n" + bt, err := json.Marshal(templateContext) if err != nil { return "", errors.WithMessage(err, "json marshal template context") } - var buff = "context: " + string(bt) + "\n" + customStatusTemplate + ctxBuff = "context: " + string(bt) + "\n" + + bt, err = json.Marshal(parameter) + if err != nil { + return "", errors.WithMessage(err, "json marshal template parameters") + } + if string(bt) != "null" { + paramBuff = "parameter: " + string(bt) + "\n" + } + var buff = ctxBuff + paramBuff + customStatusTemplate + var r cue.Runtime inst, err := r.Compile("-", buff) if err != nil { @@ -405,7 +418,7 @@ func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, n } // Status get trait status by customStatusTemplate -func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string) (string, error) { +func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error) { if customStatusTemplate == "" { return "", nil } @@ -413,7 +426,7 @@ func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, cu if err != nil { return "", errors.WithMessage(err, "get template context") } - return getStatusMessage(templateContext, customStatusTemplate) + return getStatusMessage(templateContext, customStatusTemplate, parameter) } // HealthCheck address health check for trait diff --git a/pkg/dsl/definition/template_test.go b/pkg/dsl/definition/template_test.go index 889521411..6f997e7f2 100644 --- a/pkg/dsl/definition/template_test.go +++ b/pkg/dsl/definition/template_test.go @@ -1003,6 +1003,7 @@ func TestCheckHealth(t *testing.T) { func TestGetStatus(t *testing.T) { cases := map[string]struct { tpContext map[string]interface{} + parameter interface{} statusTemp string expMessage string }{ @@ -1063,9 +1064,33 @@ if len(context.outputs.ingress.status.loadBalancer.ingress) == 0 { }`, expMessage: "Visiting URL: example.com, IP: 10.0.0.1", }, + "status use parameter field": { + tpContext: map[string]interface{}{ + "outputs": map[string]interface{}{ + "test-name": map[string]interface{}{ + "spec": map[string]interface{}{ + "type": "NodePort", + "clusterIP": "10.0.0.1", + "ports": []interface{}{ + map[string]interface{}{ + "port": 80, + }, + }, + }, + }, + }, + }, + parameter: map[string]interface{}{ + "configInfo": map[string]string{ + "name": "test-name", + }, + }, + statusTemp: `message: parameter.configInfo.name + ".type: " + context.outputs["\(parameter.configInfo.name)"].spec.type`, + expMessage: "test-name.type: NodePort", + }, } for message, ca := range cases { - gotMessage, err := getStatusMessage(ca.tpContext, ca.statusTemp) + gotMessage, err := getStatusMessage(ca.tpContext, ca.statusTemp, ca.parameter) assert.NoError(t, err, message) assert.Equal(t, ca.expMessage, gotMessage, message) } diff --git a/test/e2e-test/application_test.go b/test/e2e-test/application_test.go index ebbe4126d..7ee845841 100644 --- a/test/e2e-test/application_test.go +++ b/test/e2e-test/application_test.go @@ -158,6 +158,37 @@ var _ = Describe("Application Normal tests", func() { verifyWorkloadRunningExpected("myweb", 1, "stefanprodan/podinfo:5.0.2") }) + It("Test app have component with multiple same type traits", func() { + traitDef := new(v1beta1.TraitDefinition) + Expect(common.ReadYamlToObject("testdata/app/trait_config.yaml", traitDef)).Should(BeNil()) + traitDef.Namespace = namespaceName + Expect(k8sClient.Create(ctx, traitDef)).Should(BeNil()) + + By("apply application") + applyApp("app7.yaml") + appName := "test-worker" + + By("check application status") + testApp := new(v1beta1.Application) + Eventually(func() error { + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: appName}, testApp) + if err != nil { + return err + } + if len(testApp.Status.Services) != 1 { + return fmt.Errorf("error ComponentStatus number wants %d, actually %d", 1, len(testApp.Status.Services)) + } + if len(testApp.Status.Services[0].Traits) != 2 { + return fmt.Errorf("error TraitStatus number wants %d, actually %d", 2, len(testApp.Status.Services[0].Traits)) + } + return nil + }, 5*time.Second).Should(BeNil()) + + By("check trait status") + Expect(testApp.Status.Services[0].Traits[0].Message).Should(Equal("configMap:app-file-html")) + Expect(testApp.Status.Services[0].Traits[1].Message).Should(Equal("secret:app-env-config")) + }) + It("Test app have rollout-template false annotation", func() { By("Apply an application") var newApp v1beta1.Application diff --git a/test/e2e-test/testdata/app/app7.yaml b/test/e2e-test/testdata/app/app7.yaml new file mode 100644 index 000000000..2abb59bcb --- /dev/null +++ b/test/e2e-test/testdata/app/app7.yaml @@ -0,0 +1,25 @@ +apiVersion: core.oam.dev/v1beta1 +kind: Application +metadata: + name: test-worker +spec: + components: + - name: myworker + type: worker + properties: + image: nginx + traits: + - type: config + properties: + kind: "configMap" + name: "app-file-html" + configname: "file" + data: + test: "demo-app" + - type: config + properties: + kind: "secret" + name: "app-env-config" + configname: "env" + data: + test: "TXlQQHNzMTIz" diff --git a/test/e2e-test/testdata/app/trait_config.yaml b/test/e2e-test/testdata/app/trait_config.yaml new file mode 100644 index 000000000..ae3be2b9c --- /dev/null +++ b/test/e2e-test/testdata/app/trait_config.yaml @@ -0,0 +1,45 @@ +# Code generated by KubeVela templates. DO NOT EDIT. +apiVersion: core.oam.dev/v1beta1 +kind: TraitDefinition +metadata: + name: config +spec: + status: + customStatus: |- + message: parameter.kind + ":" + context.outputs["\(parameter.configname)"].metadata.name + appliesToWorkloads: + - deployments.apps + podDisruptive: true + schematic: + cue: + template: |- + outputs: "\(parameter.configname)": { + if parameter.kind == "configMap" { + apiVersion: "v1" + kind: "ConfigMap" + metadata: name: parameter.name + data: { + for k, v in parameter.data { + "\(k)": v + } + } + } + + if parameter.kind == "secret" { + apiVersion: "v1" + kind: "Secret" + metadata: name: parameter.name + data: { + for k, v in parameter.data { + "\(k)": v + } + } + } + } + parameter: { + kind: string + name: string + configname: string + data: [string]: string + } +