diff --git a/go.mod b/go.mod index 15c4637..4b082e0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/kubevela/workflow go 1.19 require ( - cuelang.org/go v0.5.0-alpha.1 + cuelang.org/go v0.5.0-beta.5 github.com/agiledragon/gomonkey/v2 v2.4.0 github.com/aliyun/aliyun-log-go-sdk v0.1.38 github.com/crossplane/crossplane-runtime v0.14.1-0.20210722005935-0b469fcc77cd @@ -191,7 +191,7 @@ require ( golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/text v0.3.8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03 // indirect google.golang.org/grpc v1.48.0 // indirect diff --git a/go.sum b/go.sum index 613e757..d292396 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= -cuelang.org/go v0.5.0-alpha.1 h1:uftOYkiScCHPCQMF2dIwoyCIJsTAEONkFSA2GCm5xIc= -cuelang.org/go v0.5.0-alpha.1/go.mod h1:nxWFAPWKYvZJ+eYayxArWqKKjdBTeU1N52vJpML/c6w= +cuelang.org/go v0.5.0-beta.5 h1:TAV4ZjXw2M6xf6jI8XyAAXCqWJ82Y0oxhlf9w3l544A= +cuelang.org/go v0.5.0-beta.5/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI= github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= @@ -1898,8 +1898,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/providers/kube/handle.go b/pkg/providers/kube/handle.go index 0c29572..f4c4324 100644 --- a/pkg/providers/kube/handle.go +++ b/pkg/providers/kube/handle.go @@ -19,6 +19,8 @@ package kube import ( "context" + "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -31,7 +33,7 @@ import ( "github.com/kubevela/pkg/util/k8s/patch" wfContext "github.com/kubevela/workflow/pkg/context" - "github.com/kubevela/workflow/pkg/cue" + velacue "github.com/kubevela/workflow/pkg/cue" "github.com/kubevela/workflow/pkg/cue/model" "github.com/kubevela/workflow/pkg/cue/model/value" "github.com/kubevela/workflow/pkg/types" @@ -126,28 +128,64 @@ func (d *dispatcher) delete(ctx context.Context, cluster, owner string, manifest return d.cli.Delete(ctx, manifest) } -// Apply create or update CR in cluster. -func (h *provider) Apply(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error { +// Patch patch CR in cluster. +func (h *provider) Patch(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error { val, err := v.LookupValue("value") if err != nil { return err } - var workload = new(unstructured.Unstructured) - pv, err := v.Field("patch") - if pv.Exists() && err == nil { - base, err := model.NewBase(val.CueValue()) - if err != nil { - return err - } + obj := new(unstructured.Unstructured) + if err := val.UnmarshalTo(obj); err != nil { + return err + } + key := client.ObjectKeyFromObject(obj) + if key.Namespace == "" { + key.Namespace = "default" + } + cluster, err := v.GetString("cluster") + if err != nil { + return err + } + multiCtx := handleContext(ctx, cluster) + if err := h.cli.Get(multiCtx, key, obj); err != nil { + return err + } + baseVal := cuecontext.New().CompileString("").FillPath(cue.ParsePath(""), obj) + patcher, err := v.LookupValue("patch") + if err != nil { + return err + } - if err := base.Unify(pv); err != nil { - return err - } - workload, err = base.Unstructured() - if err != nil { + base, err := model.NewBase(baseVal) + if err != nil { + return err + } + if err := base.Unify(patcher.CueValue()); err != nil { + return err + } + workload, err := base.Unstructured() + if err != nil { + return err + } + for k, v := range h.labels { + if err := k8s.AddLabel(workload, k, v); err != nil { return err } - } else if err := val.UnmarshalTo(workload); err != nil { + } + if err := h.handlers.Apply(multiCtx, cluster, WorkflowResourceCreator, workload); err != nil { + return err + } + return velacue.FillUnstructuredObject(v, workload, "result") +} + +// Apply create or update CR in cluster. +func (h *provider) Apply(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error { + val, err := v.LookupValue("value") + if err != nil { + return err + } + var workload = new(unstructured.Unstructured) + if err := val.UnmarshalTo(workload); err != nil { return err } if workload.GetNamespace() == "" { @@ -166,7 +204,7 @@ func (h *provider) Apply(ctx monitorContext.Context, wfCtx wfContext.Context, v if err := h.handlers.Apply(deployCtx, cluster, WorkflowResourceCreator, workload); err != nil { return err } - return cue.SetUnstructuredObject(v, workload, "value") + return velacue.FillUnstructuredObject(v, workload, "value") } // ApplyInParallel create or update CRs in parallel. @@ -225,7 +263,7 @@ func (h *provider) Read(ctx monitorContext.Context, wfCtx wfContext.Context, v * if err := h.cli.Get(readCtx, key, obj); err != nil { return v.FillObject(err.Error(), "err") } - return cue.FillUnstructuredObject(v, obj, "value") + return velacue.FillUnstructuredObject(v, obj, "value") } // List lists CRs from cluster. @@ -263,7 +301,7 @@ func (h *provider) List(ctx monitorContext.Context, wfCtx wfContext.Context, v * if err := h.cli.List(readCtx, list, listOpts...); err != nil { return v.FillObject(err.Error(), "err") } - return cue.FillUnstructuredObject(v, list, "list") + return velacue.FillUnstructuredObject(v, list, "list") } // Delete deletes CR from cluster. @@ -326,5 +364,6 @@ func Install(p types.Providers, cli client.Client, labels map[string]string, han "read": prd.Read, "list": prd.List, "delete": prd.Delete, + "patch": prd.Patch, }) } diff --git a/pkg/providers/kube/handle_test.go b/pkg/providers/kube/handle_test.go index 6c78fec..5d428cd 100644 --- a/pkg/providers/kube/handle_test.go +++ b/pkg/providers/kube/handle_test.go @@ -173,25 +173,39 @@ cluster: "" Expect(err).ToNot(HaveOccurred()) v, err := value.NewValue(fmt.Sprintf(` -value: {%s} -cluster: "" -patch: { +value:{ + %s metadata: name: "test-app-1" - spec: containers: [{ - // +patchStrategy=replace - env: [{ - name: "APP" - value: "nginx-new" - }] - }] -}`, componentStr), nil, "") + metadata: labels: { + "test": "test" + } +} +cluster: "" +`, componentStr), nil, "") Expect(err).ToNot(HaveOccurred()) mCtx := monitorContext.NewTraceContext(context.Background(), "") err = p.Apply(mCtx, ctx, v, nil) Expect(err).ToNot(HaveOccurred()) - sub, err := v.LookupValue("value") + + v, err = value.NewValue(` +value: { + apiVersion: "v1" + kind: "Pod" + metadata: name: "test-app-1" +} +cluster: "" +patch: { + metadata: name: "test-app-1" + spec: { + containers: [{ + // +patchStrategy=retainKeys + image: "nginx:notfound" + }] + } +}`, nil, "") + Expect(err).ToNot(HaveOccurred()) + err = p.Patch(mCtx, ctx, v, nil) Expect(err).ToNot(HaveOccurred()) - Expect(sub.Error()).To(BeNil()) pod := &corev1.Pod{} Expect(err).ToNot(HaveOccurred()) @@ -202,7 +216,7 @@ patch: { }, pod) }, time.Second*2, time.Millisecond*300).Should(BeNil()) Expect(pod.Name).To(Equal("test-app-1")) - Expect(pod.Spec.Containers[0].Env[0].Value).To(Equal("nginx-new")) + Expect(pod.Spec.Containers[0].Image).To(Equal("nginx:notfound")) }) It("list", func() { @@ -254,7 +268,7 @@ cluster: "" expected := &metav1.PartialObjectMetadataList{} err = result.UnmarshalTo(expected) Expect(err).ToNot(HaveOccurred()) - Expect(len(expected.Items)).Should(Equal(3)) + Expect(len(expected.Items)).Should(Equal(4)) By("List pods with labels index=test-1") v, err = value.NewValue(` diff --git a/pkg/stdlib/actions/v1/op.cue b/pkg/stdlib/actions/v1/op.cue index f3b741e..cb20419 100644 --- a/pkg/stdlib/actions/v1/op.cue +++ b/pkg/stdlib/actions/v1/op.cue @@ -36,6 +36,8 @@ import ( #Apply: kube.#Apply +#Patch: kube.#Patch + #ApplyInParallel: kube.#ApplyInParallel #Read: kube.#Read diff --git a/pkg/stdlib/actions/v1/pkgs/kube.cue b/pkg/stdlib/actions/v1/pkgs/kube.cue index bff43cc..758a47c 100644 --- a/pkg/stdlib/actions/v1/pkgs/kube.cue +++ b/pkg/stdlib/actions/v1/pkgs/kube.cue @@ -11,6 +11,21 @@ ... } +#Patch: { + #do: "patch" + #provider: "kube" + + // +usage=The cluster to use + cluster: *"" | string + // +usage=The resource to patch, we'll first get the resource from the cluster, then apply the patcher to it + value: {...} + // +usage=The patcher that will be applied to the resource, you can define the strategy of list merge through comments. Reference doc here: https://kubevela.io/docs/platform-engineers/traits/patch-trait#patch-in-workflow-step + patch: {...} + // +usage=The resource after applied will be filled in this field after the action is executed + result?: {...} + ... +} + #ApplyInParallel: { #do: "apply-in-parallel" #provider: "kube"