From ed7a7c059b55dd9e439ad9df8ac4850b58f6b402 Mon Sep 17 00:00:00 2001 From: FogDong Date: Wed, 10 Aug 2022 22:42:00 +0800 Subject: [PATCH] Chore: update cue version to 0.4.3 Signed-off-by: FogDong --- api/v1alpha1/types.go | 4 +- .../crds/core.oam.dev_workflowruns.yaml | 6 +- .../crds/core.oam.dev_workflows.yaml | 3 +- charts/vela-workflow/values.yaml | 1 - controllers/workflow_test.go | 4 +- controllers/workflowrun_controller.go | 1 + go.mod | 6 +- go.sum | 36 +- pkg/context/context.go | 35 +- pkg/context/context_test.go | 30 +- pkg/cue/model/instance.go | 85 +--- pkg/cue/model/instance_test.go | 86 +--- pkg/cue/model/sets/operation.go | 105 ++--- pkg/cue/model/sets/operation_test.go | 157 +++++--- pkg/cue/model/sets/utils.go | 50 ++- pkg/cue/model/sets/utils_test.go | 63 +-- pkg/cue/model/sets/walk.go | 32 +- pkg/cue/model/sets/walk_test.go | 23 +- pkg/cue/model/value/value.go | 372 +++++++++++++----- pkg/cue/model/value/value_test.go | 135 +++++-- pkg/cue/package_suit_test.go | 2 + pkg/cue/packages/package.go | 42 +- pkg/cue/packages/package_test.go | 106 ++--- pkg/cue/process/handle.go | 16 +- pkg/cue/process/handle_test.go | 86 ++-- pkg/executor/interface.go | 2 +- pkg/hooks/data_passing.go | 6 +- pkg/providers/email/send_test.go | 8 +- pkg/providers/http/do.go | 2 +- pkg/providers/kube/handle.go | 6 +- pkg/providers/kube/handle_test.go | 12 +- pkg/providers/util/util.go | 6 +- pkg/providers/util/util_test.go | 2 +- pkg/providers/workspace/workspace.go | 14 +- pkg/providers/workspace/workspace_test.go | 19 +- pkg/stdlib/op.cue | 8 +- pkg/stdlib/packages.go | 13 +- pkg/stdlib/packages_test.go | 32 +- pkg/stdlib/pkgs/multicluster.cue | 2 +- pkg/tasks/custom/task.go | 25 +- pkg/tasks/custom/task_test.go | 29 +- pkg/tasks/template/load.go | 2 +- pkg/tasks/template/load_test.go | 2 +- pkg/types/types.go | 2 +- staticcheck.conf | 9 + 45 files changed, 1021 insertions(+), 666 deletions(-) create mode 100644 staticcheck.conf diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index 5909a30..239fda8 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -78,7 +78,7 @@ type WorkflowRunStatus struct { EndTime metav1.Time `json:"endTime,omitempty"` } -// Workflow defines workflow steps and other attributes +// WorkflowSpec defines workflow steps and other attributes type WorkflowSpec struct { Steps []WorkflowStep `json:"steps,omitempty"` } @@ -140,7 +140,7 @@ type WorkflowStepMeta struct { Alias string `json:"alias,omitempty"` } -// WorkflowSubStep defines how to execute a workflow subStep. +// WorkflowStepBase defines the workflow step base type WorkflowStepBase struct { // Name is the unique name of the workflow step. Name string `json:"name"` diff --git a/charts/vela-workflow/crds/core.oam.dev_workflowruns.yaml b/charts/vela-workflow/crds/core.oam.dev_workflowruns.yaml index afd98ef..383b84f 100644 --- a/charts/vela-workflow/crds/core.oam.dev_workflowruns.yaml +++ b/charts/vela-workflow/crds/core.oam.dev_workflowruns.yaml @@ -58,7 +58,7 @@ spec: workflowRef: type: string workflowSpec: - description: Workflow defines workflow steps and other attributes + description: WorkflowSpec defines workflow steps and other attributes properties: steps: items: @@ -114,8 +114,8 @@ spec: x-kubernetes-preserve-unknown-fields: true subSteps: items: - description: WorkflowSubStep defines how to execute a - workflow subStep. + description: WorkflowStepBase defines the workflow step + base properties: dependsOn: description: DependsOn is the dependency of the step diff --git a/charts/vela-workflow/crds/core.oam.dev_workflows.yaml b/charts/vela-workflow/crds/core.oam.dev_workflows.yaml index da503cc..2dcdfcc 100644 --- a/charts/vela-workflow/crds/core.oam.dev_workflows.yaml +++ b/charts/vela-workflow/crds/core.oam.dev_workflows.yaml @@ -87,8 +87,7 @@ spec: x-kubernetes-preserve-unknown-fields: true subSteps: items: - description: WorkflowSubStep defines how to execute a workflow - subStep. + description: WorkflowStepBase defines the workflow step base properties: dependsOn: description: DependsOn is the dependency of the step diff --git a/charts/vela-workflow/values.yaml b/charts/vela-workflow/values.yaml index 7c45dec..2bea443 100644 --- a/charts/vela-workflow/values.yaml +++ b/charts/vela-workflow/values.yaml @@ -139,7 +139,6 @@ admissionWebhooks: enabled: false certManager: enabled: false - revisionHistoryLimit: 3 ## @param kubeClient.qps The qps for reconcile clients, default is 50 ## @param kubeClient.burst The burst for reconcile clients, default is 100 diff --git a/controllers/workflow_test.go b/controllers/workflow_test.go index 595a994..bd7c2b6 100644 --- a/controllers/workflow_test.go +++ b/controllers/workflow_test.go @@ -1489,7 +1489,5 @@ metadata: spec: schematic: cue: - template: "import (\n\t\"vela/op1\"\n)\n\napply: op.#Apply & {\n\tvalue: parameter.value\n\tcluster: - parameter.cluster\n}\nparameter: {\n\t// +usage=Specify the value of the object\n\tvalue: - {...}\n\t// +usage=Specify the cluster of the object\n\tcluster: *\"\" | string\n}\n"` + template: ":"` ) diff --git a/controllers/workflowrun_controller.go b/controllers/workflowrun_controller.go index 15635b5..d593bc9 100644 --- a/controllers/workflowrun_controller.go +++ b/controllers/workflowrun_controller.go @@ -64,6 +64,7 @@ var ( ReconcileTimeout = time.Minute * 3 ) +// Reconcile reconciles the WorkflowRun object //+kubebuilder:rbac:groups=core.oam.dev,resources=workflowruns,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core.oam.dev,resources=workflowruns/status,verbs=get;update;patch //+kubebuilder:rbac:groups=core.oam.dev,resources=workflowruns/finalizers,verbs=update diff --git a/go.mod b/go.mod index e984b82..1337b5d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/kubevela/workflow go 1.17 require ( - cuelang.org/go v0.2.2 + cuelang.org/go v0.4.4-0.20220729051708-0a46a1624353 github.com/agiledragon/gomonkey/v2 v2.4.0 github.com/crossplane/crossplane-runtime v0.14.1-0.20210722005935-0b469fcc77cd github.com/evanphx/json-patch v4.12.0+incompatible @@ -16,7 +16,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.24.2 k8s.io/apiextensions-apiserver v0.24.2 k8s.io/apimachinery v0.24.2 @@ -66,7 +66,6 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect @@ -77,7 +76,6 @@ require ( golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a // indirect - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect diff --git a/go.sum b/go.sum index 59dce14..9c3271c 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,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= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cuelang.org/go v0.2.2 h1:i/wFo48WDibGHKQTRZ08nB8PqmGpVpQ2sRflZPj73nQ= -cuelang.org/go v0.2.2/go.mod h1:Dyjk8Y/B3CfFT1jQKJU0g5PpCeMiDe0yMOhk57oXwqo= +cuelang.org/go v0.4.4-0.20220729051708-0a46a1624353 h1:zKp5hMLvsOulekPnhK2HaXKeXBTTfSzy209Yc01DPD8= +cuelang.org/go v0.4.4-0.20220729051708-0a46a1624353/go.mod h1:LGl1HbaGIFxblk2o2nM53YSW5KN3jmjh4c5jpHMs7rc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= @@ -66,7 +66,6 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= @@ -97,17 +96,14 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE= github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -129,7 +125,7 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -186,6 +182,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -265,7 +262,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -335,11 +331,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -355,6 +348,7 @@ github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceT github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -399,7 +393,6 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -436,13 +429,12 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RNzIRkoGGMjE+YIsZ85CnDIz672JK2F3Zl4vux4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -462,14 +454,12 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -484,9 +474,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -560,7 +548,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -600,7 +587,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -795,7 +781,6 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200612220849-54c614fe050c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -818,8 +803,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -899,7 +882,6 @@ google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -966,11 +948,11 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/context/context.go b/pkg/context/context.go index 8e061d2..c0d77dc 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -24,7 +24,7 @@ import ( "sync" "time" - "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -276,11 +276,7 @@ type ComponentManifest struct { // Patch the ComponentManifest with value func (comp *ComponentManifest) Patch(patchValue *value.Value) error { - pInst, err := model.NewOther(patchValue.CueValue()) - if err != nil { - return err - } - return comp.Workload.Unify(pInst) + return comp.Workload.Unify(patchValue.CueValue()) } type componentMould struct { @@ -289,11 +285,19 @@ type componentMould struct { } func (comp *ComponentManifest) string() (string, error) { + workload, err := comp.Workload.String() + if err != nil { + return "", err + } cm := componentMould{ - StandardWorkload: comp.Workload.String(), + StandardWorkload: workload, } for _, aux := range comp.Auxiliaries { - cm.Traits = append(cm.Traits, aux.String()) + auxiliary, err := aux.String() + if err != nil { + return "", err + } + cm.Traits = append(cm.Traits, auxiliary) } js, err := json.Marshal(cm) return string(js), err @@ -305,23 +309,16 @@ func (comp *ComponentManifest) unmarshal(v string) error { if err := json.Unmarshal([]byte(v), &cm); err != nil { return err } - var r cue.Runtime - wlInst, err := r.Compile("workload", cm.StandardWorkload) - if err != nil { - return err - } - wl, err := model.NewBase(wlInst.Value()) + wlInst := cuecontext.New().CompileString(cm.StandardWorkload) + wl, err := model.NewBase(wlInst) if err != nil { return err } comp.Workload = wl for _, s := range cm.Traits { - auxInst, err := r.Compile("-", s) - if err != nil { - return err - } - aux, err := model.NewOther(auxInst.Value()) + auxInst := cuecontext.New().CompileString(s) + aux, err := model.NewOther(auxInst) if err != nil { return err } diff --git a/pkg/context/context_test.go b/pkg/context/context_test.go index 40a34d4..516b802 100644 --- a/pkg/context/context_test.go +++ b/pkg/context/context_test.go @@ -44,7 +44,9 @@ func TestComponent(t *testing.T) { _, ok := components["server"] r.Equal(ok, true) - r.Equal(cmf.Workload.String(), `apiVersion: "v1" + s, err := cmf.Workload.String() + r.NoError(err) + r.Equal(s, `apiVersion: "v1" kind: "Pod" metadata: { labels: { @@ -53,32 +55,34 @@ metadata: { } spec: { containers: [{ - name: "main" env: [{ name: "APP" value: "nginx" - }, ...] + }] image: "nginx:1.14.2" imagePullPolicy: "IfNotPresent" + name: "main" ports: [{ containerPort: 8080 protocol: "TCP" - }, ...] - }, ...] + }] + }] } `) r.Equal(len(cmf.Auxiliaries), 1) - r.Equal(cmf.Auxiliaries[0].String(), `apiVersion: "v1" + s, err = cmf.Auxiliaries[0].String() + r.NoError(err) + r.Equal(s, `apiVersion: "v1" kind: "Service" metadata: { name: "my-service" } spec: { ports: [{ - protocol: "TCP" port: 80 + protocol: "TCP" targetPort: 8080 - }, ...] + }] selector: { app: "nginx" } @@ -96,7 +100,9 @@ env:[{name: "ClusterIP",value: "1.1.1.1"}]}] cmf, err = wfCtx.GetComponent("server") r.NoError(err) - r.Equal(cmf.Workload.String(), `apiVersion: "v1" + s, err = cmf.Workload.String() + r.NoError(err) + r.Equal(s, `apiVersion: "v1" kind: "Pod" metadata: { labels: { @@ -105,7 +111,6 @@ metadata: { } spec: { containers: [{ - name: "main" // +patchKey=name env: [{ name: "APP" @@ -116,11 +121,12 @@ spec: { }, ...] image: "nginx:1.14.2" imagePullPolicy: "IfNotPresent" + name: "main" ports: [{ containerPort: 8080 protocol: "TCP" }, ...] - }, ...] + }] } `) @@ -201,7 +207,7 @@ result: 101 conflictV, err := value.NewValue(`score: 101`, nil, "") r.NoError(err) err = wfCtx.SetVar(conflictV, "football") - r.Equal(err.Error(), "football.result: conflicting values 100 and 101") + r.Equal(err.Error(), "football.score: conflicting values 101 and 100") } func TestRefObj(t *testing.T) { diff --git a/pkg/cue/model/instance.go b/pkg/cue/model/instance.go index be17f4c..5e33480 100644 --- a/pkg/cue/model/instance.go +++ b/pkg/cue/model/instance.go @@ -17,12 +17,7 @@ limitations under the License. package model import ( - "regexp" - "strings" - "cuelang.org/go/cue" - "cuelang.org/go/cue/build" - "cuelang.org/go/cue/format" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/klog/v2" @@ -32,20 +27,25 @@ import ( // Instance defines Model Interface type Instance interface { - String() string + String() (string, error) + Value() cue.Value Unstructured() (*unstructured.Unstructured, error) IsBase() bool - Unify(other Instance, options ...sets.UnifyOption) error + Unify(other cue.Value, options ...sets.UnifyOption) error Compile() ([]byte, error) } type instance struct { - v string + v cue.Value base bool } // String return instance's cue format string -func (inst *instance) String() string { +func (inst *instance) String() (string, error) { + return sets.ToString(inst.v) +} + +func (inst *instance) Value() cue.Value { return inst.v } @@ -55,21 +55,14 @@ func (inst *instance) IsBase() bool { } func (inst *instance) Compile() ([]byte, error) { - bi := build.NewContext().NewInstance("", nil) - err := bi.AddFile("-", inst.v) - if err != nil { - return nil, err - } - var r cue.Runtime - it, err := r.Build(bi) - if err != nil { + if err := inst.v.Err(); err != nil { return nil, err } // compiled object should be final and concrete value - if err := it.Value().Validate(cue.Concrete(true), cue.Final()); err != nil { + if err := inst.v.Validate(cue.Concrete(true), cue.Final()); err != nil { return nil, err } - return it.Value().MarshalJSON() + return inst.v.MarshalJSON() } // Unstructured convert cue values to unstructured.Unstructured @@ -77,7 +70,7 @@ func (inst *instance) Compile() ([]byte, error) { func (inst *instance) Unstructured() (*unstructured.Unstructured, error) { jsonv, err := inst.Compile() if err != nil { - klog.ErrorS(err, "failed to have the workload/trait unstructured", "Definition", inst.String()) + klog.ErrorS(err, "failed to have the workload/trait unstructured", "Definition", inst.v) return nil, errors.Wrap(err, "failed to have the workload/trait unstructured") } o := &unstructured.Unstructured{} @@ -88,8 +81,8 @@ func (inst *instance) Unstructured() (*unstructured.Unstructured, error) { } // Unify implement unity operations between instances -func (inst *instance) Unify(other Instance, options ...sets.UnifyOption) error { - pv, err := sets.StrategyUnify(inst.v, other.String(), options...) +func (inst *instance) Unify(other cue.Value, options ...sets.UnifyOption) error { + pv, err := sets.StrategyUnify(inst.v, other, options...) if err != nil { return err } @@ -99,59 +92,15 @@ func (inst *instance) Unify(other Instance, options ...sets.UnifyOption) error { // NewBase create a base instance func NewBase(v cue.Value) (Instance, error) { - vs, err := openPrint(v) - if err != nil { - return nil, err - } return &instance{ - v: vs, + v: v, base: true, }, nil } // NewOther create a non-base instance func NewOther(v cue.Value) (Instance, error) { - vs, err := openPrint(v) - if err != nil { - return nil, err - } return &instance{ - v: vs, + v: v, }, nil } - -func openPrint(v cue.Value) (string, error) { - sysopts := []cue.Option{cue.All(), cue.DisallowCycles(true), cue.ResolveReferences(true), cue.Docs(true)} - f, err := sets.ToFile(v.Syntax(sysopts...)) - if err != nil { - return "", err - } - for _, decl := range f.Decls { - sets.ListOpen(decl) - } - - ret, err := format.Node(f) - if err != nil { - return "", err - } - - errInfo, contain := IndexMatchLine(string(ret), "_|_") - if contain { - return "", errors.New(errInfo) - } - return string(ret), nil -} - -// IndexMatchLine will index and extract the line contains the pattern. -func IndexMatchLine(ret, target string) (string, bool) { - if strings.Contains(ret, target) { - if target == "_|_" { - r := regexp.MustCompile(`_\|_[\s]//.*`) - match := r.FindAllString(ret, -1) - if len(match) > 0 { - return strings.Join(match, ","), true - } - } - } - return "", false -} diff --git a/pkg/cue/model/instance_test.go b/pkg/cue/model/instance_test.go index 6981b17..6406195 100644 --- a/pkg/cue/model/instance_test.go +++ b/pkg/cue/model/instance_test.go @@ -20,52 +20,12 @@ import ( "fmt" "testing" - "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) -func TestGetCompileError(t *testing.T) { - testcases := []struct { - src string - wantErr bool - errInfo string - }{{ - src: ` env: [{ - name: "HELLO" - value: "_A_|_B_|_C_" -}]`, - wantErr: false, - errInfo: "", - }, { - src: ` env: [{ - name: conflicting - value: _|_ // conflicting values "ENV_LEVEL" and "JAVA_TOOL_OPTIONS" -}]`, - wantErr: true, - errInfo: "_|_ // conflicting values \"ENV_LEVEL\" and \"JAVA_TOOL_OPTIONS\"", - }, { - src: ` env: [{ - name: conflicting-1 - value: _|_ // conflicting values "ENV_LEVEL" and "JAVA_TOOL_OPTIONS" - },{ - name: conflicting-2 - value: _|_ // conflicting values "HELLO" and "WORLD" -}]`, - wantErr: true, - errInfo: "_|_ // conflicting values \"ENV_LEVEL\" and \"JAVA_TOOL_OPTIONS\"," + - "_|_ // conflicting values \"HELLO\" and \"WORLD\"", - }} - for _, tt := range testcases { - r := require.New(t) - errInfo, contains := IndexMatchLine(tt.src, "_|_") - r.Equal(tt.wantErr, contains) - r.Equal(tt.errInfo, errInfo) - } - -} - func TestInstance(t *testing.T) { testCases := []struct { @@ -84,12 +44,7 @@ metadata: name: "test" } for _, v := range testCases { - var r cue.Runtime - inst, err := r.Compile("-", v.src) - if err != nil { - t.Error(err) - return - } + inst := cuecontext.New().CompileString(v.src) base, err := NewBase(inst.Value()) if err != nil { t.Error(err) @@ -100,9 +55,9 @@ metadata: name: "test" t.Error(err) return } - re := require.New(t) - re.Equal(v.gvk, baseObj.GetObjectKind().GroupVersionKind()) - re.Equal(true, base.IsBase()) + r := require.New(t) + r.Equal(v.gvk, baseObj.GetObjectKind().GroupVersionKind()) + r.Equal(true, base.IsBase()) other, err := NewOther(inst.Value()) if err != nil { @@ -115,8 +70,8 @@ metadata: name: "test" return } - re.Equal(v.gvk, otherObj.GetObjectKind().GroupVersionKind()) - re.Equal(false, other.IsBase()) + r.Equal(v.gvk, otherObj.GetObjectKind().GroupVersionKind()) + r.Equal(false, other.IsBase()) } } @@ -170,40 +125,39 @@ output: { } ` - var r cue.Runtime - re := require.New(t) - inst, err := r.Compile("-", base) - re.NoError(err) + r := require.New(t) + inst := cuecontext.New().CompileString(base) newbase, err := NewBase(inst.Value()) - re.NoError(err) + r.NoError(err) data, err := newbase.Unstructured() - re.Error(err) + r.Error(err) var expnil *unstructured.Unstructured - re.Equal(expnil, data) + r.Equal(expnil, data) } func TestError(t *testing.T) { + ctx := cuecontext.New() ins := &instance{ - v: ``, + v: ctx.CompileString(``), } r := require.New(t) _, err := ins.Unstructured() r.Equal(err.Error(), "Object 'Kind' is missing in '{}'") ins = &instance{ - v: ` + v: ctx.CompileString(` apiVersion: "apps/v1" kind: "Deployment" metadata: name: parameter.name -`, +`), } _, err = ins.Unstructured() r.Equal(err.Error(), fmt.Sprintf(`failed to have the workload/trait unstructured: metadata.name: reference "%s" not found`, ParameterFieldName)) ins = &instance{ - v: ` + v: ctx.CompileString(` apiVersion: "apps/v1" kind: "Deployment" metadata: name: "abc" -`, +`), } obj, err := ins.Unstructured() r.Equal(err, nil) @@ -218,7 +172,7 @@ metadata: name: "abc" }) ins = &instance{ - v: ` + v: ctx.CompileString(` apiVersion: "source.toolkit.fluxcd.io/v1beta1" metadata: { name: "grafana" @@ -227,7 +181,7 @@ kind: "HelmRepository" spec: { url: string interval: *"5m" | string -}`, +}`), } o, err := ins.Unstructured() r.Nil(o) diff --git a/pkg/cue/model/sets/operation.go b/pkg/cue/model/sets/operation.go index 0c424bd..8d8d6ba 100644 --- a/pkg/cue/model/sets/operation.go +++ b/pkg/cue/model/sets/operation.go @@ -22,7 +22,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/ast" - "cuelang.org/go/cue/parser" + "cuelang.org/go/cue/cuecontext" jsonpatch "github.com/evanphx/json-patch" "github.com/pkg/errors" ) @@ -115,6 +115,9 @@ func listMergeProcess(field *ast.Field, key string, baseList, patchList *ast.Lis kmaps[fmt.Sprintf(key, blit.Value)] = patchList.Elts[i] } if !foundPatch { + if len(patchList.Elts) == 0 { + continue + } return } @@ -187,7 +190,10 @@ func strategyPatchHandle() interceptor { paths := append(ctx.Pos(), labelStr(field.Label)) baseSubNode, err := lookUp(baseNode, paths...) if err != nil { - return + if errors.Is(err, notFoundErr) { + return + } + baseSubNode = ast.NewList() } baselist, ok := baseSubNode.(*ast.ListLit) if !ok { @@ -253,69 +259,60 @@ func IsJSONPatch(patcher cue.Value) bool { } // StrategyUnify unify the objects by the strategy -func StrategyUnify(base, patch string, options ...UnifyOption) (ret string, err error) { +func StrategyUnify(base, patch cue.Value, options ...UnifyOption) (ret cue.Value, err error) { params := newUnifyParams(options...) var patchOpts []interceptor if params.PatchStrategy == StrategyJSONMergePatch || params.PatchStrategy == StrategyJSONPatch { - base, err = OpenBaiscLit(base) + _, err := OpenBaiscLit(base) if err != nil { return base, err } } else { patchOpts = []interceptor{strategyPatchHandle()} } - baseFile, err := parser.ParseFile("-", base, parser.ParseComments) - if err != nil { - return "", errors.WithMessage(err, "invalid base cue file") - } - patchFile, err := parser.ParseFile("-", patch, parser.ParseComments) - if err != nil { - return "", errors.WithMessage(err, "invalid patch cue file") - } - - return strategyUnify(baseFile, patchFile, params, patchOpts...) + return strategyUnify(base, patch, params, patchOpts...) } -func strategyUnify(baseFile *ast.File, patchFile *ast.File, params *UnifyParams, patchOpts ...interceptor) (string, error) { - for _, option := range patchOpts { - if err := option(baseFile, patchFile); err != nil { - return "", errors.WithMessage(err, "process patchOption") - } +// nolint:staticcheck +func strategyUnify(base cue.Value, patch cue.Value, params *UnifyParams, patchOpts ...interceptor) (val cue.Value, err error) { + if params.PatchStrategy == StrategyJSONMergePatch { + return jsonMergePatch(base, patch) + } else if params.PatchStrategy == StrategyJSONPatch { + return jsonPatch(base, patch.LookupPath(cue.ParsePath("operations"))) } - - var r cue.Runtime - - baseInst, err := r.CompileFile(baseFile) + openBase, err := openListLit(base) if err != nil { - return "", errors.WithMessage(err, "compile base file") + return cue.Value{}, errors.Wrapf(err, "failed to open list it for merge") } - patchInst, err := r.CompileFile(patchFile) + patchFile, err := ToFile(patch.Syntax(cue.Docs(true), cue.ResolveReferences(true))) if err != nil { - return "", errors.WithMessage(err, "compile patch file") + return cue.Value{}, err } - - if params.PatchStrategy == StrategyJSONMergePatch { - return jsonMergePatch(baseInst.Value(), patchInst.Value()) - } else if params.PatchStrategy == StrategyJSONPatch { - return jsonPatch(baseInst.Value(), patchInst.Lookup("operations")) + for _, option := range patchOpts { + if err := option(openBase, patchFile); err != nil { + return cue.Value{}, errors.WithMessage(err, "process patchOption") + } } - ret := baseInst.Value().Unify(patchInst.Value()) + baseInst := cuecontext.New().BuildFile(openBase) + patchInst := cuecontext.New().BuildFile(patchFile) + + ret := baseInst.Unify(patchInst) - rv, err := toString(ret, removeTmpVar) + _, err = toString(ret, removeTmpVar) if err != nil { - return rv, errors.WithMessage(err, " format result toString") + return ret, errors.WithMessage(err, " format result toString") } if err := ret.Err(); err != nil { - return rv, errors.WithMessage(err, "result check err") + return ret, errors.WithMessage(err, "result check err") } if err := ret.Validate(cue.All()); err != nil { - return rv, errors.WithMessage(err, "result validate") + return ret, errors.WithMessage(err, "result validate") } - return rv, nil + return ret, nil } func findCommentTag(commentGroup []*ast.CommentGroup) map[string]string { @@ -345,47 +342,51 @@ func findCommentTag(commentGroup []*ast.CommentGroup) map[string]string { return kval } -func jsonMergePatch(base cue.Value, patch cue.Value) (string, error) { +func jsonMergePatch(base cue.Value, patch cue.Value) (cue.Value, error) { + ctx := cuecontext.New() baseJSON, err := base.MarshalJSON() if err != nil { - return "", errors.Wrapf(err, "failed to marshal base value") + return cue.Value{}, errors.Wrapf(err, "failed to marshal base value") } patchJSON, err := patch.MarshalJSON() if err != nil { - return "", errors.Wrapf(err, "failed to marshal patch value") + return cue.Value{}, errors.Wrapf(err, "failed to marshal patch value") } merged, err := jsonpatch.MergePatch(baseJSON, patchJSON) if err != nil { - return "", errors.Wrapf(err, "failed to merge base value and patch value by JsonMergePatch") + return cue.Value{}, errors.Wrapf(err, "failed to merge base value and patch value by JsonMergePatch") } - output, err := OpenBaiscLit(string(merged)) + val := ctx.CompileBytes(merged) + output, err := OpenBaiscLit(val) if err != nil { - return "", errors.Wrapf(err, "failed to parse open basic lit for merged result") + return cue.Value{}, errors.Wrapf(err, "failed to parse open basic lit for merged result") } - return output, nil + return ctx.BuildFile(output), nil } -func jsonPatch(base cue.Value, patch cue.Value) (string, error) { +func jsonPatch(base cue.Value, patch cue.Value) (cue.Value, error) { + ctx := cuecontext.New() baseJSON, err := base.MarshalJSON() if err != nil { - return "", errors.Wrapf(err, "failed to marshal base value") + return cue.Value{}, errors.Wrapf(err, "failed to marshal base value") } patchJSON, err := patch.MarshalJSON() if err != nil { - return "", errors.Wrapf(err, "failed to marshal patch value") + return cue.Value{}, errors.Wrapf(err, "failed to marshal patch value") } decodedPatch, err := jsonpatch.DecodePatch(patchJSON) if err != nil { - return "", errors.Wrapf(err, "failed to decode patch") + return cue.Value{}, errors.Wrapf(err, "failed to decode patch") } merged, err := decodedPatch.Apply(baseJSON) if err != nil { - return "", errors.Wrapf(err, "failed to apply json patch") + return cue.Value{}, errors.Wrapf(err, "failed to apply json patch") } - output, err := OpenBaiscLit(string(merged)) + val := ctx.CompileBytes(merged) + output, err := OpenBaiscLit(val) if err != nil { - return "", errors.Wrapf(err, "failed to parse open basic lit for merged result") + return cue.Value{}, errors.Wrapf(err, "failed to parse open basic lit for merged result") } - return output, nil + return ctx.BuildFile(output), nil } diff --git a/pkg/cue/model/sets/operation_test.go b/pkg/cue/model/sets/operation_test.go index 97bbdcb..8571b67 100644 --- a/pkg/cue/model/sets/operation_test.go +++ b/pkg/cue/model/sets/operation_test.go @@ -21,15 +21,18 @@ import ( "testing" "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" + "cuelang.org/go/cue/parser" "github.com/stretchr/testify/require" ) func TestPatch(t *testing.T) { testCase := []struct { - base string - patch string - result string + base string + patch string + result string + expectedErr string }{ { base: `containers: [{name: "x1"},{name: "x2"},...]`, @@ -44,15 +47,27 @@ func TestPatch(t *testing.T) { }, { - base: `containers: [{name: "x1"},{name: "x2"},...]`, - patch: `containers: [{name: "x2"},{name: "x1"}]`, - result: "_|_\n", + base: `containers: [{name: "x1"},{name: "x2"},...]`, + patch: `containers: [{name: "x2"},{name: "x1"}]`, + result: `containers: [{ + name: _|_ // containers.0.name: conflicting values "x2" and "x1" +}, { + name: _|_ // containers.1.name: conflicting values "x1" and "x2" +}] +`, + expectedErr: `conflicting values "x2" and "x1"`, }, { - base: `containers: [{name: _|_},{name: "x2"},...]`, - patch: `containers: [{name: _|_},{name: "x2"}]`, - result: "_|_\n", + base: `containers: [{name: _|_},{name: "x2"},...]`, + patch: `containers: [{name: _|_},{name: "x2"}]`, + result: `containers: [{ + name: _|_ // explicit error (_|_ literal) in source (and 1 more errors) +}, { + name: "x2" +}] +`, + expectedErr: "explicit error (_|_ literal) in source", }, { @@ -70,12 +85,16 @@ containers: [{ }, { + // lose close here base: `containers: [close({namex: "x1"}),...]`, patch: ` // +patchKey=name containers: [{name: "x2"},{name: "x1"}]`, - result: ` // +patchKey=name -containers: [_|_, // field "name" not allowed in closed struct{ + result: `// +patchKey=name +containers: [{ + namex: "x1" + name: "x2" +}, { name: "x1" }, ...] `, @@ -103,13 +122,19 @@ containers: [{ base: `containers: [{name: "x1"},{name: "x2"},...]`, patch: ` // +patchKey=name -containers: [{noname: "x3"}]`, - result: "_|_\n", +containers: [{noname: "x3"},...]`, + result: `// +patchKey=name +containers: [{ + name: "x1" + noname: "x3" +}, { + name: "x2" +}, ...] +`, }, { base: `containers: [{name: "x1"},{name: "x2"},...]`, - patch: ` -// +patchKey=name + patch: `// +patchKey=name containers: [{noname: "x3"},{name: "x1"}]`, result: `// +patchKey=name containers: [{ @@ -141,16 +166,17 @@ containers: [{ patch: ` // +patchKey=name containers: [{name: "x2", envs: [close({name: "OPS", value: "OAM"})]}]`, + // TODO: fix losing close struct in cue result: `// +patchKey=name -containers: [close({ +containers: [{ name: "x1" -}), close({ +}, { name: "x2" - envs: [close({ + envs: [{ name: "OPS" value: "OAM" - }), ...] -}), ...] + }, ...] +}, ...] `, }, @@ -277,16 +303,17 @@ containers: [{ },...]`, result: `containers: [{ volumeMounts: [{ - path: "p1" name: "k1" + path: "p1" }, { - path: "p2" name: "k1" + path: "p2" }, { - path: "p3" name: "k2" + path: "p3" }] }, ...] + // +patchKey=name volumes: [{ name: "x1" @@ -356,12 +383,45 @@ containers: [{ }, ...] }, ...] `}, + { + base: `containers: [{name: "x1"}]`, + patch: ` +containers: [{ + // +patchKey=name + env: [{ + name: "k" + value: "v" + }] +}, ...]`, + result: `containers: [{ + name: "x1" + // +patchKey=name + env: [{ + name: "k" + value: "v" + }] +}, ...] +`, + }, } for i, tcase := range testCase { - r := require.New(t) - v, _ := StrategyUnify(tcase.base, tcase.patch) - r.Equal(v, tcase.result, fmt.Sprintf("testPatch for case(no:%d) %s", i, v)) + t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) { + r := require.New(t) + ctx := cuecontext.New() + base := ctx.CompileString(tcase.base) + patch := ctx.CompileString(tcase.patch) + v, err := StrategyUnify(base, patch) + if tcase.expectedErr != "" { + r.Error(err) + r.Contains(err.Error(), tcase.expectedErr) + return + } + r.NoError(err) + s, err := toString(v) + r.NoError(err) + r.Equal(s, tcase.result, fmt.Sprintf("testPatch for case(no:%d) %s", i, v)) + }) } } @@ -390,6 +450,9 @@ spec: { strategy: { // +patchStrategy=retainKeys type: "recreate" + rollingUpdate: { + maxSurge: "30%" + } } } `}, @@ -413,6 +476,9 @@ spec: { strategy: { // +patchStrategy=retainKeys type: "recreate" + rollingUpdate: { + maxSurge: "30%" + } } } `}, @@ -443,7 +509,7 @@ volumes: [{ configMap: { name: "conf-name" } -}] +}, ...] `}, { @@ -479,7 +545,7 @@ volumes: [{ configMap: { name: "conf-name" } -}] +}, ...] `}, { @@ -511,8 +577,8 @@ containers: [{ envs: [{ name: "e1" value: "v2" - }] -}] + }, ...] +}, ...] `}, { @@ -535,9 +601,9 @@ spec: { envs:[{name: "e1",value: "v2"}] }]} `, - result: `// +patchKey=name -// +patchStrategy=retainKeys -spec: { + result: `spec: { + // +patchKey=name + // +patchStrategy=retainKeys containers: [{ name: "c2" envs: [{ @@ -575,8 +641,14 @@ metadata: { for i, tcase := range testCase { r := require.New(t) - v, _ := StrategyUnify(tcase.base, tcase.patch) - r.Equal(v, tcase.result, fmt.Sprintf("testPatch for case(no:%d) %s", i, v)) + ctx := cuecontext.New() + base := ctx.CompileString(tcase.base) + patch := ctx.CompileString(tcase.patch) + v, err := StrategyUnify(base, patch) + r.NoError(err) + s, err := toString(v) + r.NoError(err) + r.Equal(s, tcase.result, fmt.Sprintf("testPatch for case(no:%d) %s", i, s)) } } @@ -593,15 +665,12 @@ func TestParseCommentTags(t *testing.T) { x: null ` - var r cue.Runtime - inst, err := r.Compile("-", temp) - if err != nil { - t.Error(err) - return - } - ms := findCommentTag(inst.Lookup("x").Doc()) - re := require.New(t) - re.Equal(ms, map[string]string{ + r := require.New(t) + file, err := parser.ParseFile("-", temp, parser.ParseComments) + r.NoError(err) + v := cuecontext.New().BuildFile(file) + ms := findCommentTag(v.LookupPath(cue.ParsePath("x")).Doc()) + r.Equal(ms, map[string]string{ "patchKey": "name", "testKey1": "testValue1", "testKey2": "testValue2", diff --git a/pkg/cue/model/sets/utils.go b/pkg/cue/model/sets/utils.go index 38676a5..db07cf4 100644 --- a/pkg/cue/model/sets/utils.go +++ b/pkg/cue/model/sets/utils.go @@ -27,7 +27,6 @@ import ( "cuelang.org/go/cue/ast" "cuelang.org/go/cue/format" "cuelang.org/go/cue/literal" - "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" "github.com/pkg/errors" ) @@ -153,7 +152,13 @@ func extractFuncName(expr ast.Expr) (string, []ast.Expr) { func getPaths(node ast.Expr) []string { switch v := node.(type) { case *ast.SelectorExpr: - return append(getPaths(v.X), v.Sel.Name) + var sel string + if l, ok := v.Sel.(*ast.Ident); ok { + sel = l.Name + } else { + sel = fmt.Sprint(v.Sel) + } + return append(getPaths(v.X), sel) case *ast.Ident: return []string{v.Name} case *ast.BasicLit: @@ -198,9 +203,9 @@ func labelStr(label ast.Label) string { return "" } +// nolint:staticcheck func toString(v cue.Value, opts ...func(node ast.Node) ast.Node) (string, error) { - v = v.Eval() - syopts := []cue.Option{cue.All(), cue.DisallowCycles(true), cue.ResolveReferences(true), cue.Docs(true)} + syopts := []cue.Option{cue.All(), cue.ResolveReferences(true), cue.DisallowCycles(true), cue.Docs(true), cue.Attributes(true)} var w bytes.Buffer useSep := false @@ -295,14 +300,41 @@ func OptBytesToString(node ast.Node) ast.Node { } // OpenBaiscLit make that the basicLit can be modified. -func OpenBaiscLit(s string) (string, error) { - f, err := parser.ParseFile("-", s, parser.ParseComments) +// nolint:staticcheck +func OpenBaiscLit(val cue.Value) (*ast.File, error) { + f, err := ToFile(val.Syntax(cue.Docs(true), cue.ResolveReferences(true))) if err != nil { - return "", err + return nil, err } openBaiscLit(f) - b, err := format.Node(f) - return string(b), err + return f, err +} + +// nolint:staticcheck +func openListLit(val cue.Value) (*ast.File, error) { + f, err := ToFile(val.Syntax(cue.Docs(true), cue.ResolveReferences(true))) + 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.ListLit: + if len(lit.Elts) > 0 { + if _, ok := lit.Elts[len(lit.Elts)-1].(*ast.Ellipsis); ok { + break + } + } + newList := lit.Elts + newList = append(newList, &ast.Ellipsis{}) + field.Value = ast.NewList(newList...) + } + } + return true + }, nil) + return f, nil } func openBaiscLit(root ast.Node) { diff --git a/pkg/cue/model/sets/utils_test.go b/pkg/cue/model/sets/utils_test.go index b7b8099..904707d 100644 --- a/pkg/cue/model/sets/utils_test.go +++ b/pkg/cue/model/sets/utils_test.go @@ -18,8 +18,8 @@ package sets import ( "testing" - "cuelang.org/go/cue" "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/format" "cuelang.org/go/cue/literal" "cuelang.org/go/cue/parser" @@ -55,8 +55,8 @@ if true { } lacy: string `, - expected: `foo: int -lacy: string + expected: `lacy: string +foo: int `}, { s: ` @@ -71,14 +71,12 @@ if foo > 5 { } `}, } - var r cue.Runtime for _, tcase := range testCases { - re := require.New(t) - inst, err := r.Compile("-", tcase.s) - re.NoError(err) - str, err := ToString(inst.Value()) - re.NoError(err) - re.Equal(str, tcase.expected) + r := require.New(t) + inst := cuecontext.New().CompileString(tcase.s) + str, err := ToString(inst) + r.NoError(err) + r.Equal(str, tcase.expected) } } @@ -122,20 +120,21 @@ abc `, expected: `foo: int lacy: """ - abc - 123 - """ + abc + 123 + """ `}, } - var r cue.Runtime + ctx := cuecontext.New() for _, tcase := range testCases { - re := require.New(t) - inst, err := r.Compile("-", tcase.s) - re.NoError(err) + r := require.New(t) + file, err := parser.ParseFile("-", tcase.s) + r.NoError(err) + inst := ctx.BuildFile(file) str, err := ToString(inst.Value(), OptBytesToString) - re.NoError(err) - re.Equal(str, tcase.expected) + r.NoError(err) + r.Equal(str, tcase.expected) } } @@ -228,22 +227,22 @@ wait: { }, } - var r cue.Runtime + ctx := cuecontext.New() for _, tCase := range testCases { - re := require.New(t) + r := require.New(t) f, err := parser.ParseFile("-", tCase.src) - re.NoError(err) + r.NoError(err) err = PreprocessBuiltinFunc(f, "script", doScript) - re.NoError(err) - inst, err := r.CompileFile(f) - re.NoError(err) + r.NoError(err) + inst := ctx.BuildFile(f) bt, _ := inst.Value().MarshalJSON() - re.Equal(string(bt), tCase.expectJson) + r.Equal(string(bt), tCase.expectJson) } } func TestOpenBasicLit(t *testing.T) { - s, err := OpenBaiscLit(` + r := require.New(t) + f, err := OpenBaiscLit(cuecontext.New().CompileString(` a: 10 a1: int b: "foo" @@ -253,8 +252,10 @@ c1: bool arr: [1,2] top: _ bottom: _|_ -`) - r := require.New(t) +`)) + r.NoError(err) + val := cuecontext.New().BuildFile(f) + s, err := toString(val) r.NoError(err) r.Equal(s, `a: *10 | _ a1: int @@ -264,17 +265,17 @@ c: *true | _ c1: bool arr: *[1, 2] | [...] top: _ -bottom: _|_ +bottom: _|_ // explicit error (_|_ literal) in source `) } func TestListOpen(t *testing.T) { + r := require.New(t) f, err := parser.ParseFile("-", ` x: ["a","b"] y: [...string] z: [] `) - r := require.New(t) r.NoError(err) ListOpen(f) diff --git a/pkg/cue/model/sets/walk.go b/pkg/cue/model/sets/walk.go index 535fb1a..82b5f3c 100644 --- a/pkg/cue/model/sets/walk.go +++ b/pkg/cue/model/sets/walk.go @@ -21,6 +21,7 @@ import ( "strings" "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/token" ) type nodewalker struct { @@ -83,6 +84,9 @@ func (nwk *nodewalker) walk(node ast.Node) { nwk.walk(n.X) nwk.walk(n.Y) + case *ast.UnaryExpr: + nwk.walk(n.X) + case *ast.EmbedDecl: nwk.walk(n.Expr) @@ -93,8 +97,10 @@ func (nwk *nodewalker) walk(node ast.Node) { case *ast.File: nwk.walkDeclList(n.Decls) - case *ast.ListComprehension: - nwk.walk(n.Expr) + case *ast.SliceExpr: + if list, ok := n.X.(*ast.ListLit); ok { + nwk.walkExprSlice(list.Elts, n.Low, n.High) + } case *ast.CallExpr: // close func need to be ignored @@ -119,6 +125,28 @@ func (nwk *nodewalker) walkExprList(list []ast.Expr) { } } +func (nwk *nodewalker) walkExprSlice(list []ast.Expr, low ast.Expr, high ast.Expr) { + var ( + lowIndex = 0 + highIndex = len(list) + ) + if v, ok := low.(*ast.BasicLit); ok && v.Kind == token.INT { + lowIndex, _ = strconv.Atoi(v.Value) + } + if v, ok := high.(*ast.BasicLit); ok && v.Kind == token.INT { + highIndex, _ = strconv.Atoi(v.Value) + } + for i, x := range list { + if i < lowIndex || i >= highIndex { + continue + } + origin := nwk.pos + nwk.pos = append(nwk.pos, strconv.Itoa(i-lowIndex)) + nwk.walk(x) + nwk.pos = origin + } +} + func (nwk *nodewalker) walkDeclList(list []ast.Decl) { for _, x := range list { nwk.walk(x) diff --git a/pkg/cue/model/sets/walk_test.go b/pkg/cue/model/sets/walk_test.go index f467510..32b2d2b 100644 --- a/pkg/cue/model/sets/walk_test.go +++ b/pkg/cue/model/sets/walk_test.go @@ -19,8 +19,8 @@ package sets import ( "testing" - "cuelang.org/go/cue" "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/parser" "github.com/stretchr/testify/require" ) @@ -86,17 +86,16 @@ func TestWalk(t *testing.T) { tags_str: strings.Compare(b,"c") } `, + `a: [1, 2, 3]`, } for _, src := range testCases { - var r cue.Runtime - re := require.New(t) - inst, err := r.Compile("-", src) - re.NoError(err) + r := require.New(t) + inst := cuecontext.New().CompileString(src) nsrc, err := toString(inst.Value()) - re.NoError(err) + r.NoError(err) f, err := parser.ParseFile("-", nsrc) - re.NoError(err) + r.NoError(err) newWalker(func(node ast.Node, ctx walkCtx) { if len(ctx.Pos()) == 0 { @@ -111,9 +110,9 @@ func TestWalk(t *testing.T) { } n, err := lookUp(f, ctx.Pos()...) - re.NoError(err) + r.NoError(err) - re.Equal(n, node, nsrc) + r.Equal(n, node, nsrc) }).walk(f) } @@ -133,10 +132,8 @@ func TestRemoveTmpVar(t *testing.T) { } ` r := require.New(t) - var runtime cue.Runtime - inst, err := runtime.Compile("-", src) - r.NoError(err) - s, err := toString(inst.Value(), removeTmpVar) + v := cuecontext.New().CompileString(src) + s, err := toString(v, removeTmpVar) r.NoError(err) r.Equal(`spec: { list: [{ diff --git a/pkg/cue/model/value/value.go b/pkg/cue/model/value/value.go index 30b2b90..eddfc60 100644 --- a/pkg/cue/model/value/value.go +++ b/pkg/cue/model/value/value.go @@ -26,6 +26,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/ast" "cuelang.org/go/cue/build" + "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/literal" "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" @@ -36,10 +37,13 @@ import ( "github.com/kubevela/workflow/pkg/stdlib" ) -// Value is an object with cue.runtime and vendors +// DefaultPackageHeader describes the default package header for CUE files. +const DefaultPackageHeader = "package main\n" + +// Value is an object with cue.context and vendors type Value struct { v cue.Value - r cue.Runtime + r *cue.Context addImports func(instance *build.Instance) error } @@ -55,6 +59,9 @@ func (val *Value) Error() error { if !v.Exists() { return errors.New("empty value") } + if err := val.v.Err(); err != nil { + return err + } var gerr error v.Walk(func(value cue.Value) bool { if err := value.Eval().Err(); err != nil { @@ -75,6 +82,50 @@ func (val *Value) UnmarshalTo(x interface{}) error { return json.Unmarshal(data, x) } +// NewValueWithMainAndFiles new a value from main and appendix files +func NewValueWithMainAndFiles(main string, slaveFiles []string, pd *packages.PackageDiscover, tagTempl string, opts ...func(*ast.File) error) (*Value, error) { + builder := &build.Instance{} + + mainFile, err := parser.ParseFile("main.cue", main, parser.ParseComments) + if err != nil { + return nil, errors.Wrap(err, "parse main file") + } + if mainFile.PackageName() == "" { + // add a default package main if not exist + mainFile, err = parser.ParseFile("main.cue", DefaultPackageHeader+main, parser.ParseComments) + if err != nil { + return nil, errors.Wrap(err, "parse main file with added package main header") + } + } + for _, opt := range opts { + if err := opt(mainFile); err != nil { + return nil, errors.Wrap(err, "run option func for main file") + } + } + if err := builder.AddSyntax(mainFile); err != nil { + return nil, errors.Wrap(err, "add main file to CUE builder") + } + + for idx, sf := range slaveFiles { + cueSF, err := parser.ParseFile("sf-"+strconv.Itoa(idx)+".cue", sf, parser.ParseComments) + if err != nil { + return nil, errors.Wrap(err, "parse added file "+strconv.Itoa(idx)+" \n"+sf) + } + if cueSF.PackageName() != mainFile.PackageName() { + continue + } + for _, opt := range opts { + if err := opt(cueSF); err != nil { + return nil, errors.Wrap(err, "run option func for files") + } + } + if err := builder.AddSyntax(cueSF); err != nil { + return nil, errors.Wrap(err, "add slave files to CUE builder") + } + } + return newValue(builder, pd, tagTempl) +} + // NewValue new a value func NewValue(s string, pd *packages.PackageDiscover, tagTempl string, opts ...func(*ast.File) error) (*Value, error) { builder := &build.Instance{} @@ -91,6 +142,10 @@ func NewValue(s string, pd *packages.PackageDiscover, tagTempl string, opts ...f if err := builder.AddSyntax(file); err != nil { return nil, err } + return newValue(builder, pd, tagTempl) +} + +func newValue(builder *build.Instance, pd *packages.PackageDiscover, tagTempl string) (*Value, error) { addImports := func(inst *build.Instance) error { if pd != nil { pd.ImportBuiltinPackagesFor(inst) @@ -105,18 +160,31 @@ func NewValue(s string, pd *packages.PackageDiscover, tagTempl string, opts ...f return nil, err } - var r cue.Runtime - inst, err := r.Build(builder) - if err != nil { - return nil, err - } + r := cuecontext.New() + inst := r.BuildInstance(builder) val := new(Value) val.r = r - val.v = inst.Value() + val.v = inst val.addImports = addImports + // do not check val.Err() error here, because the value may be filled later return val, nil } +// AddFile add file to the instance +func AddFile(bi *build.Instance, filename string, src interface{}) error { + if filename == "" { + filename = "-" + } + file, err := parser.ParseFile(filename, src, parser.ParseComments) + if err != nil { + return err + } + if err := bi.AddSyntax(file); err != nil { + return err + } + return nil +} + // TagFieldOrder add step tag. func TagFieldOrder(root *ast.File) error { i := 0 @@ -190,51 +258,66 @@ func (vs *visitor) addAttrForExpr(node ast.Node, index *int) { // MakeValue generate an value with same runtime func (val *Value) MakeValue(s string) (*Value, error) { builder := &build.Instance{} - if err := builder.AddFile("-", s); err != nil { + file, err := parser.ParseFile("-", s, parser.ParseComments) + if err != nil { return nil, err } - if err := val.addImports(builder); err != nil { + if err := builder.AddSyntax(file); err != nil { return nil, err } - inst, err := val.r.Build(builder) - if err != nil { + if err := val.addImports(builder); err != nil { return nil, err } + inst := val.r.BuildInstance(builder) v := new(Value) v.r = val.r - v.v = inst.Value() + v.v = inst v.addImports = val.addImports + if v.Error() != nil { + return nil, v.Error() + } return v, nil } func (val *Value) makeValueWithFile(files ...*ast.File) (*Value, error) { builder := &build.Instance{} + newFile := &ast.File{} + imports := map[string]*ast.ImportSpec{} for _, f := range files { - if err := builder.AddSyntax(f); err != nil { - return nil, err + for _, importSpec := range f.Imports { + if _, ok := imports[importSpec.Name.String()]; !ok { + imports[importSpec.Name.String()] = importSpec + } } + newFile.Decls = append(newFile.Decls, f.Decls...) } - if err := val.addImports(builder); err != nil { + + for _, imp := range imports { + newFile.Imports = append(newFile.Imports, imp) + } + + if err := builder.AddSyntax(newFile); err != nil { return nil, err } - inst, err := val.r.Build(builder) - if err != nil { + if err := val.addImports(builder); err != nil { return nil, err } + inst := val.r.BuildInstance(builder) v := new(Value) v.r = val.r - v.v = inst.Value() + v.v = inst v.addImports = val.addImports return v, nil } // FillRaw unify the value with the cue format string x at the given path. func (val *Value) FillRaw(x string, paths ...string) error { - xInst, err := val.r.Compile("-", x) + file, err := parser.ParseFile("-", x, parser.ParseComments) if err != nil { return err } - v := val.v.Fill(xInst.Value(), paths...) + xInst := val.r.BuildFile(file) + v := val.v.FillPath(FieldPath(paths...), xInst) if v.Err() != nil { return v.Err() } @@ -245,7 +328,12 @@ func (val *Value) FillRaw(x string, paths ...string) error { // FillValueByScript unify the value x at the given script path. func (val *Value) FillValueByScript(x *Value, path string) error { if !strings.Contains(path, "[") { - return val.FillObject(x, strings.Split(path, ".")...) + newV := val.v.FillPath(FieldPath(path), x.v) + if err := newV.Err(); err != nil { + return err + } + val.v = newV + return nil } s, err := x.String() if err != nil { @@ -292,19 +380,17 @@ func (val *Value) FillObject(x interface{}, paths ...string) error { } insert = v.v } - newV := val.v.Fill(insert, paths...) - if newV.Err() != nil { - return newV.Err() - } + newV := val.v.FillPath(FieldPath(paths...), insert) + // do not check newV.Err() error here, because the value may be filled later val.v = newV return nil } // LookupValue reports the value at a path starting from val func (val *Value) LookupValue(paths ...string) (*Value, error) { - v := val.v.Lookup(paths...) + v := val.v.LookupPath(FieldPath(paths...)) if !v.Exists() { - return nil, errors.Errorf("var(path=%s) not exist", strings.Join(paths, ".")) + return nil, errors.Errorf("failed to lookup value: var(path=%s) not exist", strings.Join(paths, ".")) } return &Value{ v: v, @@ -313,6 +399,33 @@ func (val *Value) LookupValue(paths ...string) (*Value, error) { }, nil } +func isScript(content string) (bool, error) { + content = strings.TrimSpace(content) + scriptFile, err := parser.ParseFile("-", content, parser.ParseComments) + if err != nil { + return false, errors.WithMessage(err, "parse script") + } + if len(scriptFile.Imports) != 0 { + return true, nil + } + if len(scriptFile.Decls) == 0 || len(scriptFile.Decls) > 1 { + return true, nil + } + + return !isSelector(scriptFile.Decls[0]), nil +} + +func isSelector(node ast.Node) bool { + switch v := node.(type) { + case *ast.EmbedDecl: + return isSelector(v.Expr) + case *ast.SelectorExpr, *ast.IndexExpr, *ast.Ident: + return true + default: + return false + } +} + // LookupByScript reports the value by cue script. func (val *Value) LookupByScript(script string) (*Value, error) { var outputKey = "zz_output__" @@ -321,6 +434,14 @@ func (val *Value) LookupByScript(script string) (*Value, error) { if err != nil { return nil, errors.WithMessage(err, "parse script") } + isScriptPath, err := isScript(script) + if err != nil { + return nil, err + } + + if !isScriptPath { + return val.LookupValue(script) + } raw, err := val.String() if err != nil { @@ -341,6 +462,7 @@ func (val *Value) LookupByScript(script string) (*Value, error) { return newV.LookupValue(outputKey) } + func behindKey(file *ast.File, key string) { var ( implDecls []ast.Decl @@ -405,68 +527,106 @@ func (val *Value) StepByList(handle func(name string, in *Value) (bool, error)) // StepByFields process the fields in order func (val *Value) StepByFields(handle func(name string, in *Value) (bool, error)) error { - for i := 0; ; i++ { - field, end, err := val.fieldIndex(i) - if err != nil { - return err - } + iter := steps(val) + for iter.next() { + iter.do(handle) + } + return iter.err +} - if end { - return nil - } - stop, err := handle(field.Name, field.Value) - if err != nil { - return errors.WithMessagef(err, "step %s", field.Name) - } - if !isDef(field.Name) { - if err := val.FillObject(field.Value, field.Name); err != nil { - return err - } - } - if stop { - return nil - } +type stepsIterator struct { + queue []*field + index int + target *Value + err error + stopped bool +} + +func steps(v *Value) *stepsIterator { + return &stepsIterator{ + target: v, } } -func (val *Value) fieldIndex(index int) (*field, bool, error) { - fields, err := val.fields() - if err != nil { - return nil, false, err +func (iter *stepsIterator) next() bool { + if iter.stopped { + return false + } + if iter.err != nil { + return false } - if index >= len(fields) { - return nil, true, nil + if iter.queue != nil { + iter.index++ } - return fields[index], false, nil + iter.assemble() + return iter.index <= len(iter.queue)-1 } -func (val *Value) fields() ([]*field, error) { - st, err := val.v.Struct() +func (iter *stepsIterator) assemble() { + st, err := iter.target.v.Struct() if err != nil { - return nil, err + iter.err = err + return + } + + filters := map[string]struct{}{} + for _, item := range iter.queue { + filters[item.Name] = struct{}{} } - var fields []*field + var addFields []*field for i := 0; i < st.Len(); i++ { - v := st.Field(i) - attr := v.Value.Attribute("step") + name := st.Field(i).Name + attr := st.Field(i).Value.Attribute("step") no, err := attr.Int(0) if err != nil { no = 100 - if v.Name == "#do" || v.Name == "#provider" { + if name == "#do" || name == "#provider" { no = 0 } } - fields = append(fields, &field{ - no: no, - Name: v.Name, - Value: &Value{ - r: val.r, - v: v.Value, - addImports: val.addImports, - }}) + if _, ok := filters[name]; !ok { + addFields = append(addFields, &field{ + Name: name, + no: no, + }) + } + } + + suffixItems := append(addFields, iter.queue[iter.index:]...) + sort.Sort(sortFields(suffixItems)) + iter.queue = append(iter.queue[:iter.index], suffixItems...) +} + +func (iter *stepsIterator) value() *Value { + v := iter.target.v.LookupPath(FieldPath(iter.name())) + return &Value{ + r: iter.target.r, + v: v, + addImports: iter.target.addImports, + } +} + +func (iter *stepsIterator) name() string { + return iter.queue[iter.index].Name +} + +func (iter *stepsIterator) do(handle func(name string, in *Value) (bool, error)) { + if iter.err != nil { + return + } + v := iter.value() + stopped, err := handle(iter.name(), v) + if err != nil { + iter.err = err + return + } + iter.stopped = stopped + if !isDef(iter.name()) { + if err := iter.target.FillObject(v, iter.name()); err != nil { + iter.err = err + return + } } - sort.Sort(sortFields(fields)) - return fields, nil } type sortFields []*field @@ -484,13 +644,7 @@ func (sf sortFields) Swap(i, j int) { // Field return the cue value corresponding to the specified field func (val *Value) Field(label string) (cue.Value, error) { - var v cue.Value - if isDef(label) { - v = val.v.LookupDef(label) - } else { - v = val.v.Lookup(label) - } - + v := val.v.LookupPath(cue.ParsePath(label)) if !v.Exists() { return v, errors.Errorf("label %s not found", label) } @@ -541,19 +695,13 @@ func (val *Value) GetBool(paths ...string) (bool, error) { // OpenCompleteValue make that the complete value can be modified. func (val *Value) OpenCompleteValue() error { - s, err := val.String() + newS, err := sets.OpenBaiscLit(val.CueValue()) if err != nil { return err } - newS, err := sets.OpenBaiscLit(s) - if err != nil { - return err - } - v, err := val.MakeValue(newS) - if err != nil { - return err - } - val.v = v.CueValue() + + v := cuecontext.New().BuildFile(newS) + val.v = v return nil } func isDef(s string) bool { @@ -594,12 +742,17 @@ func (a *assembler) installTo(expr ast.Expr) error { return err } case *ast.SelectorExpr: - if err := a.installTo(v.Sel); err != nil { - return err + if ident, ok := v.Sel.(*ast.Ident); ok { + if err := a.installTo(ident); err != nil { + return err + } + } else { + return errors.New("invalid sel type in selector") } if err := a.installTo(v.X); err != nil { return err } + case *ast.Ident: a.fill2Path(v.String()) case *ast.BasicLit: @@ -617,3 +770,40 @@ func (a *assembler) installTo(expr ast.Expr) error { } return nil } + +// makePath creates a Path from a sequence of string. +func makePath(paths ...string) string { + mergedPath := "" + if len(paths) == 0 { + return mergedPath + } + mergedPath = paths[0] + if mergedPath == "" || (len(paths) == 1 && (strings.Contains(mergedPath, ".") || strings.Contains(mergedPath, "["))) { + return paths[0] + } + if !strings.HasPrefix(mergedPath, "_") && !strings.HasPrefix(mergedPath, "#") { + mergedPath = fmt.Sprintf("\"%s\"", mergedPath) + } + for _, p := range paths[1:] { + if !strings.HasPrefix(p, "#") { + mergedPath += fmt.Sprintf("[\"%s\"]", p) + } else { + mergedPath += fmt.Sprintf(".%s", p) + } + } + return mergedPath +} + +func isNumber(s string) bool { + _, err := strconv.ParseInt(s, 10, 64) + return err == nil +} + +// FieldPath return the cue path of the given paths +func FieldPath(paths ...string) cue.Path { + s := makePath(paths...) + if isNumber(s) { + return cue.MakePath(cue.Str(s)) + } + return cue.ParsePath(s) +} diff --git a/pkg/cue/model/value/value_test.go b/pkg/cue/model/value/value_test.go index b09d86c..70b839e 100644 --- a/pkg/cue/model/value/value_test.go +++ b/pkg/cue/model/value/value_test.go @@ -18,8 +18,11 @@ package value import ( "encoding/json" + "fmt" "testing" + "github.com/kubevela/workflow/pkg/cue/model/sets" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -101,14 +104,14 @@ step3: { prefix: 101 value: 102 } -step4: { - prefix: 102 - value: 103 -} step5: { prefix: 103 value: 104 } +step4: { + prefix: 102 + value: 103 +} `}, {base: ` @@ -205,6 +208,8 @@ step3: "3" } func TestStepWithTag(t *testing.T) { + // TODO(@FogDong): add if condition test cases back. + // refer to issue: https://github.com/cue-lang/cue/issues/1826 testCases := []struct { base string expected string @@ -214,9 +219,6 @@ step1: {} step2: {prefix: step1.value} step3: {prefix: step2.value} step4: {prefix: step3.value} -if step4.value > 100 { - step5: {} -} step5: { value: *100|int } @@ -242,9 +244,7 @@ step5: { `}, {base: ` step1: {} step2: {prefix: step1.value} -if step2.value > 100 { - step2_3: {prefix: step2.value} -} +step2_3: {prefix: step2.value} step3: {prefix: step2.value} step4: {prefix: step3.value} `, @@ -271,9 +271,7 @@ step4: { step2: {prefix: step1.value} @step(2) step1: {} @step(1) step3: {prefix: step2.value} @step(4) -if step2.value > 100 { - step2_3: {prefix: step2.value} @step(3) -} +step2_3: {prefix: step2.value} @step(3) `, expected: `step2: { prefix: 100 @@ -295,9 +293,7 @@ step2_3: { {base: ` step2: {prefix: step1.value} step1: {} @step(-1) -if step2.value > 100 { - step2_3: {prefix: step2.value} -} +step2_3: {prefix: step2.value} step3: {prefix: step2.value} `, expected: `step2: { @@ -317,7 +313,7 @@ step3: { } @step(3) `}} - for _, tCase := range testCases { + for i, tCase := range testCases { r := require.New(t) val, err := NewValue(tCase.base, nil, "", TagFieldOrder) r.NoError(err) @@ -329,9 +325,9 @@ step3: { }) }) r.NoError(err) - str, err := val.String() + str, err := sets.ToString(val.CueValue()) r.NoError(err) - r.Equal(str, tCase.expected) + r.Equal(str, tCase.expected, fmt.Sprintf("testPatch for case(no:%d) %s", i, str)) } } @@ -370,14 +366,14 @@ do: string } func TestStepByList(t *testing.T) { + r := require.New(t) base := `[{step: 1},{step: 2}]` v, err := NewValue(base, nil, "") - r := require.New(t) r.NoError(err) var i int64 err = v.StepByList(func(name string, in *Value) (bool, error) { i++ - num, err := in.CueValue().Lookup("step").Int64() + num, err := in.CueValue().LookupPath(FieldPath("step")).Int64() r.NoError(err) r.Equal(num, i) return false, nil @@ -416,8 +412,8 @@ provider: xxx ` r := require.New(t) val, err := NewValue(caseError, nil, "") - r.Error(err) - r.Equal(val == nil, true) + r.NoError(err) + r.Error(val.Error()) val, err = NewValue(":", nil, "") r.Error(err) @@ -436,6 +432,8 @@ do: "apply" r.Error(err) _, err = val.MakeValue(":") r.Error(err) + _, err = val.MakeValue("test: _|_") + r.Error(err) err = val.FillRaw(caseError) r.Error(err) r.Equal(originCue, val.CueValue()) @@ -455,8 +453,8 @@ close({provider: int}) cv, err = val.MakeValue(caseClose) r.NoError(err) err = val.FillObject(cv) - r.Error(err) - r.Equal(originCue, val.CueValue()) + r.NoError(err) + r.Error(val.Error()) _, err = val.LookupValue("abc") r.Error(err) @@ -483,6 +481,84 @@ x: #X & { r.NoError(err) } +func TestLookupValue(t *testing.T) { + testCases := []struct { + name string + str string + paths []string + }{ + { + name: "def", + str: ` +#x: "v" +`, + paths: []string{"#x"}, + }, + { + name: "def in def", + str: ` +#x: { + #y: "v" +} +`, + paths: []string{"#x", "#y"}, + }, + { + name: "num", + str: ` +"1": { + "2": "v" +} +`, + paths: []string{"1", "2"}, + }, + { + name: "invalid", + str: ` +"a-b": { + "b-c": "v" +} +`, + paths: []string{"a-b", "b-c"}, + }, + { + name: "concrete path", + str: ` +a: { + "b-c": "v" +} +`, + paths: []string{`a["b-c"]`}, + }, + { + name: "concrete path with num", + str: ` +a: [ + { + key: "v" + } +] +`, + paths: []string{`a[0].key`}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + r := require.New(t) + v, err := NewValue(tc.str, nil, "") + r.NoError(err) + result, err := v.LookupValue(tc.paths...) + r.NoError(err) + r.NoError(result.Error()) + s, err := sets.ToString(result.v) + r.Equal(s, `"v" +`) + r.NoError(err) + }) + } +} + func TestValueError(t *testing.T) { caseOk := ` provider: "kube" @@ -493,8 +569,7 @@ do: "apply" r.NoError(err) err = val.FillRaw(` provider: "conflict"`) - r.NoError(err) - r.Error(val.Error()) + r.Error(err) val, err = NewValue(caseOk, nil, "") r.NoError(err) @@ -669,7 +744,7 @@ strings.Join(apply.arr,".")+"$"`, { src: ` op: string - op: 12 + op: "help" `, script: `op(1`, err: "parse script: expected ')', found 'EOF'", @@ -680,7 +755,7 @@ strings.Join(apply.arr,".")+"$"`, op: "help" `, script: `oss`, - err: "zz_output__: reference \"oss\" not found", + err: "failed to lookup value: var(path=oss) not exist", }, } @@ -885,7 +960,7 @@ func TestFillByScript(t *testing.T) { raw: `a: b: [{x: y:[{name: "key"}]}]`, path: "a.b[0].x.y[0].name", v: `"foo"`, - err: "a.b.0.x.y.0.name: conflicting values \"key\" and \"foo\"", + err: "remake value: a.b.0.x.y.0.name: conflicting values \"foo\" and \"key\"", }, { name: "filled value with wrong cue format", diff --git a/pkg/cue/package_suit_test.go b/pkg/cue/package_suit_test.go index 3527eac..c43842c 100644 --- a/pkg/cue/package_suit_test.go +++ b/pkg/cue/package_suit_test.go @@ -94,6 +94,7 @@ var _ = Describe("Package discovery resources for definition from K8s APIServer" } }) + // nolint:staticcheck PIt("discovery built-in k8s resource with kube prefix", func() { By("test ingress in kube package") @@ -384,6 +385,7 @@ output: { }) + // nolint:staticcheck PIt("discovery built-in k8s resource with third-party path", func() { By("test ingress in kube package") diff --git a/pkg/cue/packages/package.go b/pkg/cue/packages/package.go index 03a18ad..db19d7b 100644 --- a/pkg/cue/packages/package.go +++ b/pkg/cue/packages/package.go @@ -26,6 +26,8 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/ast" "cuelang.org/go/cue/build" + "cuelang.org/go/cue/cuecontext" + "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" "cuelang.org/go/encoding/jsonschema" "github.com/pkg/errors" @@ -106,6 +108,7 @@ func (pd *PackageDiscover) ImportBuiltinPackagesFor(bi *build.Instance) { } // ImportPackagesAndBuildInstance Combine import built-in packages and build cue template together to avoid data race +// nolint:staticcheck func (pd *PackageDiscover) ImportPackagesAndBuildInstance(bi *build.Instance) (inst *cue.Instance, err error) { var r cue.Runtime if pd == nil { @@ -120,6 +123,21 @@ func (pd *PackageDiscover) ImportPackagesAndBuildInstance(bi *build.Instance) (i return r.Build(bi) } +// ImportPackagesAndBuildValue Combine import built-in packages and build cue template together to avoid data race +func (pd *PackageDiscover) ImportPackagesAndBuildValue(bi *build.Instance) (val cue.Value, err error) { + cuectx := cuecontext.New() + if pd == nil { + return cuectx.BuildInstance(bi), nil + } + pd.ImportBuiltinPackagesFor(bi) + if err := stdlib.AddImportsFor(bi, ""); err != nil { + return cue.Value{}, err + } + pd.mutex.Lock() + defer pd.mutex.Unlock() + return cuectx.BuildInstance(bi), nil +} + // ListPackageKinds list packages and their kinds func (pd *PackageDiscover) ListPackageKinds() map[string][]VersionKind { pd.mutex.RLock() @@ -189,7 +207,11 @@ func (pd *PackageDiscover) pkgBuild(packages map[string]*pkgInstance, pkgName st DefinitionName: "#" + dGVK.Kind, }) - if err := pkg.AddFile(dGVK.reverseString(), def); err != nil { + file, err := parser.ParseFile(dGVK.reverseString(), def) + if err != nil { + return err + } + if err := pkg.AddSyntax(file); err != nil { return err } @@ -199,19 +221,21 @@ func (pd *PackageDiscover) pkgBuild(packages map[string]*pkgInstance, pkgName st } func (pd *PackageDiscover) addKubeCUEPackagesFromCluster(apiSchema string) error { - var r cue.Runtime - oaInst, err := r.Compile("-", apiSchema) + file, err := parser.ParseFile("-", apiSchema) + if err != nil { + return err + } + oaInst := cuecontext.New().BuildFile(file) if err != nil { return err } dgvkMapper := make(map[string]domainGroupVersionKind) - pathValue := oaInst.Value().Lookup("paths") + pathValue := oaInst.LookupPath(cue.ParsePath("paths")) if pathValue.Exists() { if st, err := pathValue.Struct(); err == nil { iter := st.Fields() for iter.Next() { - gvk := iter.Value().Lookup("post", - "x-kubernetes-group-version-kind") + gvk := iter.Value().LookupPath(cue.ParsePath("post[\"x-kubernetes-group-version-kind\"]")) if gvk.Exists() { if v, err := getDGVK(gvk); err == nil { dgvkMapper[v.reverseString()] = v @@ -398,16 +422,16 @@ func (pkg *pkgInstance) processOpenAPIFile(f *ast.File) { func getDGVK(v cue.Value) (ret domainGroupVersionKind, err error) { gvk := metav1.GroupVersionKind{} - gvk.Group, err = v.Lookup("group").String() + gvk.Group, err = v.LookupPath(cue.ParsePath("group")).String() if err != nil { return } - gvk.Version, err = v.Lookup("version").String() + gvk.Version, err = v.LookupPath(cue.ParsePath("version")).String() if err != nil { return } - gvk.Kind, err = v.Lookup("kind").String() + gvk.Kind, err = v.LookupPath(cue.ParsePath("kind")).String() if err != nil { return } diff --git a/pkg/cue/packages/package_test.go b/pkg/cue/packages/package_test.go index eea903f..8824244 100644 --- a/pkg/cue/packages/package_test.go +++ b/pkg/cue/packages/package_test.go @@ -22,6 +22,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/build" + "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" "github.com/stretchr/testify/require" @@ -348,12 +349,12 @@ func TestPackage(t *testing.T) { } r.Equal(mypd.ListPackageKinds(), expectPkgKinds) - exceptObj := `output: close({ - kind: "Bucket" + // TODO: fix losing close struct in cue + exceptObj := `output: { apiVersion: "apps.test.io/v1" - type: "alicloud_oss_bucket" - acl: "public-read-write" | "public-read" | *"private" - dataRedundancyType?: "ZRS" | *"LRS" + kind: "Bucket" + acl: *"private" | "public-read" | "public-read-write" + dataRedundancyType?: "LRS" | "ZRS" | *"LRS" dataSourceRef?: { dsPath: string } @@ -361,12 +362,6 @@ func TestPackage(t *testing.T) { importKey: string } output: { - {[!~"^(bucketName|extranetEndpoint|intranetEndpoint|masterUserId)$"]: { - outRef: string - } | { - // Example: demoVpc.vpcId - valueRef: string - }} bucketName: { outRef: "self.name" } @@ -404,32 +399,41 @@ func TestPackage(t *testing.T) { valueRef: string } } - storageClass?: "IA" | "Archive" | "ColdArchive" | *"Standard" -}) + storageClass?: "Standard" | "IA" | "Archive" | "ColdArchive" | *"Standard" + type: "alicloud_oss_bucket" +} ` bi := build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` + file, err := parser.ParseFile("-", ` import "test.io/apps/v1" output: v1.#Bucket `) r.NoError(err) - inst, err := mypd.ImportPackagesAndBuildInstance(bi) + err = bi.AddSyntax(file) r.NoError(err) - base, err := model.NewBase(inst.Value()) + inst, err := mypd.ImportPackagesAndBuildValue(bi) r.NoError(err) - r.Equal(base.String(), exceptObj) + base, err := model.NewBase(inst) + r.NoError(err) + s, err := base.String() + r.NoError(err) + r.Equal(s, exceptObj) bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` + file, err = parser.ParseFile("-", ` import "kube/apps.test.io/v1" output: v1.#Bucket `) r.NoError(err) - inst, err = mypd.ImportPackagesAndBuildInstance(bi) + err = bi.AddSyntax(file) + r.NoError(err) + inst, err = mypd.ImportPackagesAndBuildValue(bi) r.NoError(err) - base, err = model.NewBase(inst.Value()) + base, err = model.NewBase(inst) r.NoError(err) - r.Equal(base.String(), exceptObj) + s, err = base.String() + r.NoError(err) + r.Equal(s, exceptObj) } func TestProcessFile(t *testing.T) { @@ -444,30 +448,27 @@ func TestProcessFile(t *testing.T) { ... } ` - re := require.New(t) + r := require.New(t) file, err := parser.ParseFile("-", srcTmpl) - re.NoError(err) + r.NoError(err) testPkg := newPackage("foo") testPkg.processOpenAPIFile(file) + cuectx := cuecontext.New() + inst := cuectx.BuildFile(file) - var r cue.Runtime - inst, err := r.CompileFile(file) - re.NoError(err) - testCasesInst, err := r.Compile("-", ` - #Definition: {} - case1: #Definition & {additionalProperty: "test"} + testCasesInst := cuectx.CompileString(` +#Definition: {} +case1: #Definition & {additionalProperty: "test"} - case2: #Definition & { - metadata: { - additionalProperty: "test" - } +case2: #Definition & { + metadata: { + additionalProperty: "test" + } } `) - re.NoError(err) - retInst, err := inst.Fill(testCasesInst.Value()) - re.NoError(err) - re.Error(retInst.Lookup("case1").Err(), "case1: field \"additionalProperty\" not allowed in closed struct") - re.Error(retInst.Lookup("case2", "metadata").Err(), "case2.metadata: field \"additionalProperty\" not allowed in closed struct") + retInst := inst.FillPath(cue.ParsePath(""), testCasesInst.Value()) + r.Error(retInst.LookupPath(cue.ParsePath("case1")).Err(), "case1.additionalProperty: field not allowed") + r.Error(retInst.LookupPath(cue.ParsePath("case2.metadata")).Err(), "case2.metadata.additionalProperty: field not allowed") } func TestMount(t *testing.T) { @@ -491,13 +492,13 @@ func TestGetDGVK(t *testing.T) { } } ` - re := require.New(t) - var r cue.Runtime - inst, err := r.Compile("-", srcTmpl) - re.NoError(err) - gvk, err := getDGVK(inst.Value().Lookup("x-kubernetes-group-version-kind")) - re.NoError(err) - re.Equal(gvk, domainGroupVersionKind{ + r := require.New(t) + file, err := parser.ParseFile("-", srcTmpl) + r.NoError(err) + inst := cuecontext.New().BuildFile(file) + gvk, err := getDGVK(inst.Value().LookupPath(cue.ParsePath("\"x-kubernetes-group-version-kind\""))) + r.NoError(err) + r.Equal(gvk, domainGroupVersionKind{ Domain: "test.io", Group: "apps", Version: "v1", @@ -514,11 +515,10 @@ func TestGetDGVK(t *testing.T) { } } ` - inst, err = r.Compile("-", srcTmpl) - re.NoError(err) - gvk, err = getDGVK(inst.Value().Lookup("x-kubernetes-group-version-kind")) - re.NoError(err) - re.Equal(gvk, domainGroupVersionKind{ + inst = cuecontext.New().CompileString(srcTmpl) + gvk, err = getDGVK(inst.LookupPath(cue.ParsePath("\"x-kubernetes-group-version-kind\""))) + r.NoError(err) + r.Equal(gvk, domainGroupVersionKind{ Group: "test.io", Version: "v1", Kind: "Foo", @@ -597,7 +597,8 @@ func TestGeneratePkgName(t *testing.T) { } for _, tCase := range testCases { - require.Equal(t, genStandardPkgName(tCase.dgvk), tCase.sdPkgName) + r := require.New(t) + r.Equal(genStandardPkgName(tCase.dgvk), tCase.sdPkgName) } } @@ -632,6 +633,7 @@ func TestReverseString(t *testing.T) { } for _, tCase := range testCases { - require.Equal(t, convert2DGVK(tCase.gvr).reverseString(), tCase.reverseString) + r := require.New(t) + r.Equal(convert2DGVK(tCase.gvr).reverseString(), tCase.reverseString) } } diff --git a/pkg/cue/process/handle.go b/pkg/cue/process/handle.go index d89d25b..5c08ad9 100644 --- a/pkg/cue/process/handle.go +++ b/pkg/cue/process/handle.go @@ -156,13 +156,21 @@ func (ctx *templateContext) BaseContextFile() (string, error) { buff += fmt.Sprintf(model.ContextPublishVersion+": \"%s\"\n", ctx.publishVersion) if ctx.base != nil { - buff += fmt.Sprintf(model.OutputFieldName+": %s\n", structMarshal(ctx.base.String())) + base, err := ctx.base.String() + if err != nil { + return "", err + } + buff += fmt.Sprintf(model.OutputFieldName+": %s\n", structMarshal(base)) } if len(ctx.auxiliaries) > 0 { var auxLines []string for _, auxiliary := range ctx.auxiliaries { - auxLines = append(auxLines, fmt.Sprintf("\"%s\": %s", auxiliary.Name, structMarshal(auxiliary.Ins.String()))) + aux, err := auxiliary.Ins.String() + if err != nil { + return "", err + } + auxLines = append(auxLines, fmt.Sprintf("\"%s\": %s", auxiliary.Name, structMarshal(aux))) } if len(auxLines) > 0 { buff += fmt.Sprintf(model.OutputsFieldName+": {%s}\n", strings.Join(auxLines, "\n")) @@ -214,14 +222,14 @@ func (ctx *templateContext) BaseContextFile() (string, error) { func (ctx *templateContext) ExtendedContextFile() (string, error) { context, err := ctx.BaseContextFile() if err != nil { - return "", fmt.Errorf("failed to convert data to workflow with marshal err %w", err) + return "", fmt.Errorf("failed to convert data to application with marshal err %w", err) } var bareSecret string if len(ctx.requiredSecrets) > 0 { for _, s := range ctx.requiredSecrets { data, err := json.Marshal(s.Data) if err != nil { - return "", fmt.Errorf("failed to convert data %v to workflow with marshal err %w", data, err) + return "", fmt.Errorf("failed to convert data %v to application with marshal err %w", data, err) } bareSecret += s.ContextName + ":" + string(data) + "\n" } diff --git a/pkg/cue/process/handle_test.go b/pkg/cue/process/handle_test.go index 17c8414..519ca40 100644 --- a/pkg/cue/process/handle_test.go +++ b/pkg/cue/process/handle_test.go @@ -19,33 +19,32 @@ package process import ( "testing" - "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" "github.com/stretchr/testify/require" "github.com/kubevela/workflow/pkg/cue/model" + "github.com/kubevela/workflow/pkg/cue/model/value" ) func TestContext(t *testing.T) { baseTemplate := ` image: "myserver" ` - re := require.New(t) - var r cue.Runtime - inst, err := r.Compile("-", baseTemplate) - re.NoError(err) - base, err := model.NewBase(inst.Value()) - re.NoError(err) + + r := require.New(t) + inst := cuecontext.New().CompileString(baseTemplate) + base, err := model.NewBase(inst) + r.NoError(err) serviceTemplate := ` apiVersion: "v1" kind: "ConfigMap" ` - svcInst, err := r.Compile("-", serviceTemplate) - re.NoError(err) + svcInst := cuecontext.New().CompileString(serviceTemplate) - svcIns, err := model.NewOther(svcInst.Value()) - re.NoError(err) + svcIns, err := model.NewOther(svcInst) + r.NoError(err) svcAux := Auxiliary{ Ins: svcIns, @@ -84,52 +83,51 @@ image: "myserver" PublishVersion: "mypublishversion", }) err = ctx.SetBase(base) - re.NoError(err) + r.NoError(err) err = ctx.AppendAuxiliaries(svcAux) - re.NoError(err) + r.NoError(err) err = ctx.AppendAuxiliaries(svcAuxWithAbnormalName) - re.NoError(err) + r.NoError(err) ctx.SetParameters(targetParams) ctx.PushData("arbitraryData", targetArbitraryData) c, err := ctx.ExtendedContextFile() - re.NoError(err) - ctxInst, err := r.Compile("-", c) - re.NoError(err) + r.NoError(err) + ctxInst := cuecontext.New().CompileString(c) - gName, err := ctxInst.Lookup("context", model.ContextName).String() - re.NoError(err) - re.Equal("myrun", gName) + gName, err := ctxInst.LookupPath(value.FieldPath("context", model.ContextName)).String() + r.Equal(nil, err) + r.Equal("myrun", gName) - myWorkflowName, err := ctxInst.Lookup("context", model.ContextWorkflowName).String() - re.NoError(err) - re.Equal("myworkflow", myWorkflowName) + myWorkflowName, err := ctxInst.LookupPath(value.FieldPath("context", model.ContextWorkflowName)).String() + r.Equal(nil, err) + r.Equal("myworkflow", myWorkflowName) - myPublishVersion, err := ctxInst.Lookup("context", model.ContextPublishVersion).String() - re.NoError(err) - re.Equal("mypublishversion", myPublishVersion) + myPublishVersion, err := ctxInst.LookupPath(value.FieldPath("context", model.ContextPublishVersion)).String() + r.Equal(nil, err) + r.Equal("mypublishversion", myPublishVersion) - inputJs, err := ctxInst.Lookup("context", model.OutputFieldName).MarshalJSON() - re.NoError(err) - re.Equal(`{"image":"myserver"}`, string(inputJs)) + inputJs, err := ctxInst.LookupPath(value.FieldPath("context", model.OutputFieldName)).MarshalJSON() + r.Equal(nil, err) + r.Equal(`{"image":"myserver"}`, string(inputJs)) - outputsJs, err := ctxInst.Lookup("context", model.OutputsFieldName, "service").MarshalJSON() - re.NoError(err) - re.Equal("{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\"}", string(outputsJs)) + outputsJs, err := ctxInst.LookupPath(value.FieldPath("context", model.OutputsFieldName, "service")).MarshalJSON() + r.Equal(nil, err) + r.Equal("{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\"}", string(outputsJs)) - outputsJs, err = ctxInst.Lookup("context", model.OutputsFieldName, "service-1").MarshalJSON() - re.NoError(err) - re.Equal("{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\"}", string(outputsJs)) + outputsJs, err = ctxInst.LookupPath(value.FieldPath("context", model.OutputsFieldName, "service-1")).MarshalJSON() + r.Equal(nil, err) + r.Equal("{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\"}", string(outputsJs)) - ns, err := ctxInst.Lookup("context", model.ContextNamespace).String() - re.NoError(err) - re.Equal("myns", ns) + ns, err := ctxInst.LookupPath(value.FieldPath("context", model.ContextNamespace)).String() + r.Equal(nil, err) + r.Equal("myns", ns) - params, err := ctxInst.Lookup("context", model.ParameterFieldName).MarshalJSON() - re.NoError(err) - re.Equal("{\"parameter1\":\"string\",\"parameter2\":{\"key1\":\"value1\",\"key2\":\"value2\"},\"parameter3\":[\"item1\",\"item2\"]}", string(params)) + params, err := ctxInst.LookupPath(value.FieldPath("context", model.ParameterFieldName)).MarshalJSON() + r.Equal(nil, err) + r.Equal("{\"parameter1\":\"string\",\"parameter2\":{\"key1\":\"value1\",\"key2\":\"value2\"},\"parameter3\":[\"item1\",\"item2\"]}", string(params)) - arbitraryData, err := ctxInst.Lookup("context", "arbitraryData").MarshalJSON() - re.NoError(err) - re.Equal("{\"bool\":false,\"string\":\"mytxt\",\"int\":10,\"map\":{\"key\":\"value\"},\"slice\":[\"str1\",\"str2\",\"str3\"]}", string(arbitraryData)) + arbitraryData, err := ctxInst.LookupPath(value.FieldPath("context", "arbitraryData")).MarshalJSON() + r.Equal(nil, err) + r.Equal("{\"bool\":false,\"int\":10,\"map\":{\"key\":\"value\"},\"slice\":[\"str1\",\"str2\",\"str3\"],\"string\":\"mytxt\"}", string(arbitraryData)) } diff --git a/pkg/executor/interface.go b/pkg/executor/interface.go index bbecb53..39e5784 100644 --- a/pkg/executor/interface.go +++ b/pkg/executor/interface.go @@ -22,7 +22,7 @@ import ( "github.com/kubevela/workflow/pkg/types" ) -// Workflow is used to execute the workflow steps of Application. +// WorkflowExecutor is used to execute the workflow steps type WorkflowExecutor interface { // ExecuteSteps executes the steps of an Application with given steps of rendered resources. // It returns done=true only if all steps are executed and succeeded. diff --git a/pkg/hooks/data_passing.go b/pkg/hooks/data_passing.go index a7bdac8..5675ec5 100644 --- a/pkg/hooks/data_passing.go +++ b/pkg/hooks/data_passing.go @@ -37,8 +37,10 @@ func Input(ctx wfContext.Context, paramValue *value.Value, step v1alpha1.Workflo if err != nil { return errors.WithMessagef(err, "get input from [%s]", input.From) } - if err := paramValue.FillValueByScript(inputValue, input.ParameterKey); err != nil { - return err + if input.ParameterKey != "" { + if err := paramValue.FillValueByScript(inputValue, input.ParameterKey); err != nil { + return err + } } } return nil diff --git a/pkg/providers/email/send_test.go b/pkg/providers/email/send_test.go index 595c1cd..a747a34 100644 --- a/pkg/providers/email/send_test.go +++ b/pkg/providers/email/send_test.go @@ -58,11 +58,11 @@ stepID: "success" }, "no-step-id": { from: ``, - expectedErr: errors.New("var(path=stepID) not exist"), + expectedErr: errors.New("failed to lookup value: var(path=stepID) not exist"), }, "no-sender": { from: `stepID:"no-sender"`, - expectedErr: errors.New("var(path=from) not exist"), + expectedErr: errors.New("failed to lookup value: var(path=from) not exist"), }, "no-receiver": { from: ` @@ -75,7 +75,7 @@ port: 465 } stepID: "no-receiver" `, - expectedErr: errors.New("var(path=to) not exist"), + expectedErr: errors.New("failed to lookup value: var(path=to) not exist"), }, "no-content": { from: ` @@ -89,7 +89,7 @@ port: 465 to: ["user1@gmail.com", "user2@gmail.com"] stepID: "no-content" `, - expectedErr: errors.New("var(path=content) not exist"), + expectedErr: errors.New("failed to lookup value: var(path=content) not exist"), }, "send-fail": { from: ` diff --git a/pkg/providers/http/do.go b/pkg/providers/http/do.go index bd74eec..6c8e35d 100644 --- a/pkg/providers/http/do.go +++ b/pkg/providers/http/do.go @@ -182,7 +182,7 @@ func (h *provider) getTransport(ctx monitorContext.Context, v *value.Value) (htt } func parseHeaders(obj cue.Value, label string) (http.Header, error) { - m := obj.Lookup("request", label) + m := obj.LookupPath(value.FieldPath("request", label)) if !m.Exists() { return nil, nil } diff --git a/pkg/providers/kube/handle.go b/pkg/providers/kube/handle.go index e9cc47d..d81a5f9 100644 --- a/pkg/providers/kube/handle.go +++ b/pkg/providers/kube/handle.go @@ -123,11 +123,7 @@ func (h *provider) Apply(ctx monitorContext.Context, wfCtx wfContext.Context, v return err } - patcher, err := model.NewOther(pv) - if err != nil { - return err - } - if err := base.Unify(patcher); err != nil { + if err := base.Unify(pv); err != nil { return err } workload, err = base.Unstructured() diff --git a/pkg/providers/kube/handle_test.go b/pkg/providers/kube/handle_test.go index 7a069ad..6e3b4bb 100644 --- a/pkg/providers/kube/handle_test.go +++ b/pkg/providers/kube/handle_test.go @@ -114,13 +114,15 @@ var _ = Describe("Test Workflow Provider Kube", func() { component, err := ctx.GetComponent("server") Expect(err).ToNot(HaveOccurred()) + s, err := component.Workload.String() + Expect(err).ToNot(HaveOccurred()) v, err := value.NewValue(fmt.Sprintf(` value:{ %s metadata: name: "app" } cluster: "" -`, component.Workload.String()), nil, "") +`, s), nil, "") Expect(err).ToNot(HaveOccurred()) mCtx := monitorContext.NewTraceContext(context.Background(), "") err = p.Apply(mCtx, ctx, v, nil) @@ -134,13 +136,15 @@ cluster: "" }, workload) }, time.Second*2, time.Millisecond*300).Should(BeNil()) + s, err = component.Workload.String() + Expect(err).ToNot(HaveOccurred()) v, err = value.NewValue(fmt.Sprintf(` value: { %s metadata: name: "app" } cluster: "" -`, component.Workload.String()), nil, "") +`, s), nil, "") Expect(err).ToNot(HaveOccurred()) err = p.Read(mCtx, ctx, v, nil) Expect(err).ToNot(HaveOccurred()) @@ -163,10 +167,12 @@ cluster: "" component, err := ctx.GetComponent("server") Expect(err).ToNot(HaveOccurred()) + s, err := component.Workload.String() + Expect(err).ToNot(HaveOccurred()) v, err := value.NewValue(fmt.Sprintf(` value: {%s} cluster: "" -patch: metadata: name: "test-app-1"`, component.Workload.String()), nil, "") +patch: metadata: name: "test-app-1"`, s), nil, "") Expect(err).ToNot(HaveOccurred()) mCtx := monitorContext.NewTraceContext(context.Background(), "") err = p.Apply(mCtx, ctx, v, nil) diff --git a/pkg/providers/util/util.go b/pkg/providers/util/util.go index 94eb1e8..e4de8ce 100644 --- a/pkg/providers/util/util.go +++ b/pkg/providers/util/util.go @@ -46,11 +46,7 @@ func (p *provider) PatchK8sObject(ctx monitorContext.Context, wfCtx wfContext.Co if err != nil { return err } - patcher, err := model.NewOther(pv.CueValue()) - if err != nil { - return err - } - if err = base.Unify(patcher); err != nil { + if err = base.Unify(pv.CueValue()); err != nil { return v.FillObject(err, "err") } diff --git a/pkg/providers/util/util_test.go b/pkg/providers/util/util_test.go index 676a5cd..cb1f6c3 100644 --- a/pkg/providers/util/util_test.go +++ b/pkg/providers/util/util_test.go @@ -168,7 +168,7 @@ func TestConvertString(t *testing.T) { }, "fail": { from: `bt: 123`, - expectedErr: errors.New("bt: cannot use value 123 (type int) as string|bytes"), + expectedErr: errors.New("bt: cannot use value 123 (type int) as (string|bytes)"), }, } diff --git a/pkg/providers/workspace/workspace.go b/pkg/providers/workspace/workspace.go index 3aa0d54..a62f239 100644 --- a/pkg/providers/workspace/workspace.go +++ b/pkg/providers/workspace/workspace.go @@ -58,13 +58,21 @@ func (h *provider) Load(ctx monitorContext.Context, wfCtx wfContext.Context, v * } func fillComponent(v *value.Value, component *wfContext.ComponentManifest, paths ...string) error { - if err := v.FillRaw(component.Workload.String(), append(paths, "workload")...); err != nil { + workload, err := component.Workload.String() + if err != nil { + return err + } + if err := v.FillRaw(workload, append(paths, "workload")...); err != nil { return err } if len(component.Auxiliaries) > 0 { var auxiliaries []string for _, aux := range component.Auxiliaries { - auxiliaries = append(auxiliaries, "{"+aux.String()+"}") + auxiliary, err := aux.String() + if err != nil { + return err + } + auxiliaries = append(auxiliaries, "{"+auxiliary+"}") } if err := v.FillRaw(fmt.Sprintf("[%s]", strings.Join(auxiliaries, ",")), append(paths, "auxiliaries")...); err != nil { return err @@ -138,7 +146,7 @@ func (h *provider) Wait(ctx monitorContext.Context, wfCtx wfContext.Context, v * cv := v.CueValue() if cv.Exists() { - ret := cv.Lookup("continue") + ret := cv.LookupPath(value.FieldPath("continue")) if ret.Exists() { isContinue, err := ret.Bool() if err == nil && isContinue { diff --git a/pkg/providers/workspace/workspace_test.go b/pkg/providers/workspace/workspace_test.go index fe491e3..bd3876a 100644 --- a/pkg/providers/workspace/workspace_test.go +++ b/pkg/providers/workspace/workspace_test.go @@ -87,7 +87,8 @@ component: "server" r.NoError(err) component, err := wfCtx.GetComponent("server") r.NoError(err) - s := component.Workload.String() + s, err := component.Workload.String() + r.NoError(err) r.Equal(s, `apiVersion: "v1" kind: "Pod" metadata: { @@ -97,7 +98,6 @@ metadata: { } spec: { containers: [{ - name: "main" // +patchKey=name env: [{ name: "APP" @@ -108,11 +108,12 @@ spec: { }, ...] image: "nginx:1.14.2" imagePullPolicy: "IfNotPresent" + name: "main" ports: [{ containerPort: 8080 protocol: "TCP" }, ...] - }, ...] + }] } `) @@ -327,18 +328,18 @@ metadata: } spec: { containers: [{ - name: "main" env: [{ name: "APP" value: "nginx" - }, ...] + }] image: "nginx:1.14.2" imagePullPolicy: "IfNotPresent" + name: "main" ports: [{ containerPort: 8080 protocol: "TCP" - }, ...] - }, ...] + }] + }] } } auxiliaries: [{ @@ -349,10 +350,10 @@ auxiliaries: [{ } spec: { ports: [{ - protocol: "TCP" port: 80 + protocol: "TCP" targetPort: 8080 - }, ...] + }] selector: { app: "nginx" } diff --git a/pkg/stdlib/op.cue b/pkg/stdlib/op.cue index efef20f..9a2d28b 100644 --- a/pkg/stdlib/op.cue +++ b/pkg/stdlib/op.cue @@ -65,7 +65,7 @@ import ( #ApplyComponentRemaining: #Steps & { // exceptions specify the resources not to apply. exceptions: [...string] - _exceptions: {for c in exceptions {"\(c)": true}} + exceptions_: {for c in exceptions {"\(c)": true}} component: string load: oam.#LoadComponets @step(1) @@ -77,7 +77,7 @@ import ( value: rendered.output } for name, c in rendered.outputs { - if _exceptions[name] == _|_ { + if exceptions_[name] == _|_ { "\(name)": kube.#Apply & { value: c } @@ -89,12 +89,12 @@ import ( #ApplyRemaining: #Steps & { // exceptions specify the resources not to apply. exceptions: [...string] - _exceptions: {for c in exceptions {"\(c)": true}} + exceptions_: {for c in exceptions {"\(c)": true}} load: oam.#LoadComponets @step(1) components: #Steps & { for name, c in load.value { - if _exceptions[name] == _|_ { + if exceptions_[name] == _|_ { "\(name)": oam.#ApplyComponent & { value: c } diff --git a/pkg/stdlib/packages.go b/pkg/stdlib/packages.go index 2d97900..a98bff9 100644 --- a/pkg/stdlib/packages.go +++ b/pkg/stdlib/packages.go @@ -24,6 +24,7 @@ import ( "strings" "cuelang.org/go/cue/build" + "cuelang.org/go/cue/parser" "k8s.io/klog/v2" ) @@ -88,7 +89,11 @@ func AddImportsFor(inst *build.Instance, tagTempl string) error { PkgName: filepath.Base("vela/custom"), ImportPath: "vela/custom", } - if err := p.AddFile("-", tagTempl); err != nil { + file, err := parser.ParseFile("-", tagTempl, parser.ParseComments) + if err != nil { + return err + } + if err := p.AddSyntax(file); err != nil { return err } inst.Imports = append(inst.Imports, p) @@ -107,7 +112,11 @@ func initBuiltinImports() ([]*build.Instance, error) { PkgName: filepath.Base(path), ImportPath: path, } - if err := p.AddFile("-", content); err != nil { + file, err := parser.ParseFile("-", content, parser.ParseComments) + if err != nil { + return nil, err + } + if err := p.AddSyntax(file); err != nil { return nil, err } imports = append(imports, p) diff --git a/pkg/stdlib/packages_test.go b/pkg/stdlib/packages_test.go index 758ea7d..038f67f 100644 --- a/pkg/stdlib/packages_test.go +++ b/pkg/stdlib/packages_test.go @@ -21,30 +21,34 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/build" + "cuelang.org/go/cue/cuecontext" + "cuelang.org/go/cue/parser" "github.com/stretchr/testify/require" ) func TestGetPackages(t *testing.T) { - re := require.New(t) + r := require.New(t) pkgs, err := GetPackages() - re.NoError(err) - var r cue.Runtime + r.NoError(err) + cuectx := cuecontext.New() for path, content := range pkgs { - _, err := r.Compile(path, content) - re.NoError(err) + file, err := parser.ParseFile(path, content) + r.NoError(err) + _ = cuectx.BuildFile(file) } - builder := &build.Instance{} - err = builder.AddFile("-", ` + file, err := parser.ParseFile("-", ` import "vela/custom" out: custom.context`) - re.NoError(err) + r.NoError(err) + builder := &build.Instance{} + err = builder.AddSyntax(file) + r.NoError(err) err = AddImportsFor(builder, "context: id: \"xxx\"") - re.NoError(err) + r.NoError(err) - insts := cue.Build([]*build.Instance{builder}) - re.Equal(len(insts), 1) - str, err := insts[0].Lookup("out", "id").String() - re.NoError(err) - re.Equal(str, "xxx") + inst := cuectx.BuildInstance(builder) + str, err := inst.LookupPath(cue.ParsePath("out.id")).String() + r.NoError(err) + r.Equal(str, "xxx") } diff --git a/pkg/stdlib/pkgs/multicluster.cue b/pkg/stdlib/pkgs/multicluster.cue index d3f6102..a018435 100644 --- a/pkg/stdlib/pkgs/multicluster.cue +++ b/pkg/stdlib/pkgs/multicluster.cue @@ -78,7 +78,7 @@ loadPolicies: oam.#LoadPolicies @step(1) policy_: string - if inputs.policy == "" { + if inputs.policy == "" && loadPolicies.value != _|_ { envBindingPolicies: [ for k, v in loadPolicies.value if v.type == "env-binding" {k}] policy_: envBindingPolicies[0] } diff --git a/pkg/tasks/custom/task.go b/pkg/tasks/custom/task.go index 572e797..881f77f 100644 --- a/pkg/tasks/custom/task.go +++ b/pkg/tasks/custom/task.go @@ -22,7 +22,6 @@ import ( "fmt" "strings" - "cuelang.org/go/cue" "github.com/pkg/errors" "github.com/kubevela/workflow/api/v1alpha1" @@ -142,6 +141,12 @@ func (t *TaskLoader) makeTaskGenerator(templ string) (types.TaskGenerator, error var paramFile string defer func() { + if r := recover(); r != nil { + exec.err(ctx, false, fmt.Errorf("invalid cue task for evaluation: %v", r), types.StatusReasonRendering) + stepStatus = exec.status() + operations = exec.operation() + return + } if taskv == nil { taskv, err = convertTemplate(ctx, t.pd, strings.Join([]string{templ, paramFile}, "\n"), wfStep.Name, exec.wfStatus.ID, options.PCtx) if err != nil { @@ -287,7 +292,14 @@ func buildValueForStatus(ctx wfContext.Context, step v1alpha1.WorkflowStep, pd * statusTemplate += fmt.Sprintf("status: %s\n", status) statusTemplate += contextTempl statusTemplate += "\n" + inputsTempl - return value.NewValue(template+"\n"+statusTemplate, pd, "") + v, err := value.NewValue(template+"\n"+statusTemplate, pd, "") + if err != nil { + return nil, err + } + if v.Error() != nil { + return nil, v.Error() + } + return v, nil } func convertTemplate(ctx wfContext.Context, pd *packages.PackageDiscover, templ, step, id string, pCtx process.Context) (*value.Value, error) { @@ -465,13 +477,6 @@ func (exec *executor) doSteps(ctx monitorContext.Context, wfCtx wfContext.Contex return nil } return v.StepByFields(func(fieldName string, in *value.Value) (bool, error) { - if in.CueValue().IncompleteKind() == cue.BottomKind { - errInfo, err := sets.ToString(in.CueValue()) - if err != nil { - errInfo = "value is _|_" - } - return true, errors.New(errInfo + "(bottom kind)") - } if retErr := in.CueValue().Err(); retErr != nil { errInfo, err := sets.ToString(in.CueValue()) if err == nil { @@ -519,7 +524,7 @@ func isStepList(fieldName string) bool { } func debugLog(v *value.Value) bool { - debug, _ := v.CueValue().LookupDef("#debug").Bool() + debug, _ := v.CueValue().LookupPath(value.FieldPath("#debug")).Bool() return debug } diff --git a/pkg/tasks/custom/task_test.go b/pkg/tasks/custom/task_test.go index 96f32e5..051b8e4 100644 --- a/pkg/tasks/custom/task_test.go +++ b/pkg/tasks/custom/task_test.go @@ -115,8 +115,8 @@ myIP: value: "1.1.1.1" }, { WorkflowStepBase: v1alpha1.WorkflowStepBase{ - Name: "rendering", - Type: "renderFailed", + Name: "template", + Type: "templateError", }, }, { @@ -152,9 +152,9 @@ myIP: value: "1.1.1.1" r.Equal(status.Message, "I am terminated") continue } - if step.Name == "rendering" { + if step.Name == "template" { r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) - r.Equal(status.Reason, types.StatusReasonRendering) + r.Equal(status.Reason, types.StatusReasonExecute) continue } if step.Name == "execute" { @@ -207,7 +207,7 @@ close({ Name: "input-err", Type: "ok", Properties: &runtime.RawExtension{Raw: []byte(` - {"score": {"y": 101}} +{"score": {"x": 101}} `)}, Inputs: v1alpha1.StepInputs{{ From: "score", @@ -262,10 +262,9 @@ close({ status, operation, err := run.Run(wfCtx, &types.TaskRunOptions{}) switch step.Name { case "input-err": - r.Equal(operation.Waiting, false) - r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) + r.Equal(err.Error(), "do preStartHook: score.x: conflicting values 100 and 101") case "input": - r.Equal(err.Error(), "do preStartHook: get input from [podIP]: var(path=podIP) not exist") + r.Equal(err.Error(), "do preStartHook: get input from [podIP]: failed to lookup value: var(path=podIP) not exist") case "output-var-conflict": r.Contains(status.Message, "conflict") r.Equal(operation.Waiting, false) @@ -410,7 +409,7 @@ apply: { #up: [process,{}] `, - expected: "ok", + expected: "okokok", hasErr: true, }, } @@ -679,6 +678,16 @@ func TestValidateIfValue(t *testing.T) { }, expected: true, }, + { + name: "error if", + step: v1alpha1.WorkflowStep{ + WorkflowStepBase: v1alpha1.WorkflowStepBase{ + If: `test == true`, + }, + }, + expectedErr: "invalid if value", + expected: false, + }, } for _, tc := range testCases { @@ -747,7 +756,7 @@ name: context.name return fmt.Sprintf(templ, "wait"), nil case "terminate": return fmt.Sprintf(templ, "terminate"), nil - case "renderFailed": + case "templateError": return ` output: xx `, nil diff --git a/pkg/tasks/template/load.go b/pkg/tasks/template/load.go index 71556a8..2ba473f 100644 --- a/pkg/tasks/template/load.go +++ b/pkg/tasks/template/load.go @@ -57,7 +57,7 @@ type WorkflowStepLoader struct { loadDefinition func(ctx context.Context, capName string) (string, error) } -// LoadTaskTemplate gets the workflowStep definition. +// LoadTemplate gets the workflow step definition. func (loader *WorkflowStepLoader) LoadTemplate(ctx context.Context, name string) (string, error) { files, err := templateFS.ReadDir(templateDir) if err != nil { diff --git a/pkg/tasks/template/load_test.go b/pkg/tasks/template/load_test.go index e24f842..c74f584 100644 --- a/pkg/tasks/template/load_test.go +++ b/pkg/tasks/template/load_test.go @@ -44,7 +44,7 @@ func TestLoad(t *testing.T) { if err := json.Unmarshal(js, &d); err != nil { return err } - *&o.Object = d + o.Object = d return nil }, } diff --git a/pkg/types/types.go b/pkg/types/types.go index a659997..7e8076d 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -101,7 +101,7 @@ type Operation struct { // TaskGenerator will generate taskRunner. type TaskGenerator func(wfStep v1alpha1.WorkflowStep, options *TaskGeneratorOptions) (TaskRunner, error) -// GeneratorOptions is the options for generate task. +// TaskGeneratorOptions is the options for generate task. type TaskGeneratorOptions struct { ID string PrePhase v1alpha1.WorkflowStepPhase diff --git a/staticcheck.conf b/staticcheck.conf new file mode 100644 index 0000000..ae0cb66 --- /dev/null +++ b/staticcheck.conf @@ -0,0 +1,9 @@ +# This is config file for staticcheck. +# If you need to add ignored checks, pls also add explaination in comments. + +checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-SA1019"] + +# ST1000 - Incorrect or missing package comment (non-default) +# ST1003 – Poorly chosen identifier (non-default) +# ST1016 – Use consistent method receiver names (non-default) +# SA1019 – Using a deprecated function, variable, constant or field (non-default), ignore it for now for cue upgrades (TODO: remove it after CUE compatible) \ No newline at end of file