From afb0466bcce1c9b8904f714ba27c2248d9af0980 Mon Sep 17 00:00:00 2001 From: Somefive Date: Fri, 19 Aug 2022 16:51:11 +0800 Subject: [PATCH] Fix: open basic lit fails lookup path (#4632) * Fix: open basic lit fails lookup path Signed-off-by: Somefive * Fix: test Signed-off-by: Somefive Signed-off-by: Somefive Signed-off-by: Somefive --- pkg/cue/model/sets/operation.go | 44 +++++++++++++++++-- pkg/cue/model/sets/utils.go | 3 +- .../multicluster_test.go | 18 ++++++++ .../app/app-with-env-labels-storage.yaml | 35 +++++++++++++++ 4 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 test/e2e-multicluster-test/testdata/app/app-with-env-labels-storage.yaml diff --git a/pkg/cue/model/sets/operation.go b/pkg/cue/model/sets/operation.go index 1e57e2f4f..554f649f2 100644 --- a/pkg/cue/model/sets/operation.go +++ b/pkg/cue/model/sets/operation.go @@ -23,6 +23,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/ast" "cuelang.org/go/cue/cuecontext" + "cuelang.org/go/cue/parser" jsonpatch "github.com/evanphx/json-patch" "github.com/pkg/errors" ) @@ -356,8 +357,7 @@ func jsonMergePatch(base cue.Value, patch cue.Value) (cue.Value, error) { if err != nil { return cue.Value{}, errors.Wrapf(err, "failed to merge base value and patch value by JsonMergePatch") } - val := ctx.CompileBytes(merged) - output, err := OpenBaiscLit(val) + output, err := openJSON(string(merged)) if err != nil { return cue.Value{}, errors.Wrapf(err, "failed to parse open basic lit for merged result") } @@ -383,10 +383,46 @@ func jsonPatch(base cue.Value, patch cue.Value) (cue.Value, error) { if err != nil { return cue.Value{}, errors.Wrapf(err, "failed to apply json patch") } - val := ctx.CompileBytes(merged) - output, err := OpenBaiscLit(val) + output, err := openJSON(string(merged)) if err != nil { return cue.Value{}, errors.Wrapf(err, "failed to parse open basic lit for merged result") } return ctx.BuildFile(output), nil } + +func isEllipsis(elt ast.Node) bool { + _, ok := elt.(*ast.Ellipsis) + return ok +} + +func openJSON(data string) (*ast.File, error) { + f, err := parser.ParseFile("-", data, parser.ParseComments) + if err != nil { + return nil, err + } + ast.Walk(f, func(node ast.Node) bool { + field, ok := node.(*ast.Field) + if ok { + v := field.Value + switch lit := v.(type) { + case *ast.StructLit: + if len(lit.Elts) == 0 || !isEllipsis(lit.Elts[len(lit.Elts)-1]) { + lit.Elts = append(lit.Elts, &ast.Ellipsis{}) + } + case *ast.ListLit: + if len(lit.Elts) == 0 || !isEllipsis(lit.Elts[len(lit.Elts)-1]) { + lit.Elts = append(lit.Elts, &ast.Ellipsis{}) + } + } + } + return true + }, nil) + if len(f.Decls) > 0 { + if emb, ok := f.Decls[0].(*ast.EmbedDecl); ok { + if s, _ok := emb.Expr.(*ast.StructLit); _ok { + f.Decls = s.Elts + } + } + } + return f, nil +} diff --git a/pkg/cue/model/sets/utils.go b/pkg/cue/model/sets/utils.go index 59d275c1c..5f6d332f7 100644 --- a/pkg/cue/model/sets/utils.go +++ b/pkg/cue/model/sets/utils.go @@ -186,7 +186,8 @@ func peelCloseExpr(node ast.Node) ast.Node { func lookField(node ast.Node, key string) ast.Node { if field, ok := node.(*ast.Field); ok { - if labelStr(field.Label) == key { + // Note: the trim here has side effect: "\(v)" will be trimmed to \(v), only used for comparing fields + if strings.Trim(labelStr(field.Label), `"`) == strings.Trim(key, `"`) { return field.Value } } diff --git a/test/e2e-multicluster-test/multicluster_test.go b/test/e2e-multicluster-test/multicluster_test.go index 2b000b8b4..9161dcee7 100644 --- a/test/e2e-multicluster-test/multicluster_test.go +++ b/test/e2e-multicluster-test/multicluster_test.go @@ -605,5 +605,23 @@ var _ = Describe("Test multicluster scenario", func() { g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: testNamespace, Name: "another"}, &corev1.ConfigMap{})).Should(Satisfy(kerrors.IsNotFound)) }) }) + + It("Test Application with env in webservice and labels & storage trait", func() { + bs, err := ioutil.ReadFile("./testdata/app/app-with-env-labels-storage.yaml") + Expect(err).Should(Succeed()) + app := &v1beta1.Application{} + Expect(yaml.Unmarshal(bs, app)).Should(Succeed()) + app.SetNamespace(namespace) + Expect(k8sClient.Create(hubCtx, app)).Should(Succeed()) + deploy := &appsv1.Deployment{} + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "test"}, deploy)).Should(Succeed()) + }, 15*time.Second).Should(Succeed()) + Expect(deploy.GetLabels()["key"]).Should(Equal("val")) + Expect(len(deploy.Spec.Template.Spec.Containers[0].Env)).Should(Equal(1)) + Expect(deploy.Spec.Template.Spec.Containers[0].Env[0].Name).Should(Equal("testKey")) + Expect(deploy.Spec.Template.Spec.Containers[0].Env[0].Value).Should(Equal("testValue")) + Expect(len(deploy.Spec.Template.Spec.Volumes)).Should(Equal(1)) + }) }) }) diff --git a/test/e2e-multicluster-test/testdata/app/app-with-env-labels-storage.yaml b/test/e2e-multicluster-test/testdata/app/app-with-env-labels-storage.yaml new file mode 100644 index 000000000..c748f7990 --- /dev/null +++ b/test/e2e-multicluster-test/testdata/app/app-with-env-labels-storage.yaml @@ -0,0 +1,35 @@ +apiVersion: core.oam.dev/v1beta1 +kind: Application +metadata: + name: test +spec: + components: + - name: test + properties: + cmd: + - sleep + - -c + - "86400" + env: + - name: testKey + value: testValue + image: busybox + imagePullPolicy: IfNotPresent + traits: + - properties: + key: val + type: labels + - properties: + pvc: + - accessModes: + - ReadWriteOnce + mountOnly: false + mountPath: /data + name: lvhyca + resources: + requests: + storage: 4096Mi + storageClassName: default + volumeMode: Filesystem + type: storage + type: webservice \ No newline at end of file