From b0eff8285b6cf9a5f8f78c5b589d90b9e831806d Mon Sep 17 00:00:00 2001 From: Yin Da Date: Tue, 21 Feb 2023 12:10:50 +0800 Subject: [PATCH] Feat: cue encode provider Signed-off-by: Yin Da --- cue/cuex/default_compiler_test.go | 105 +++++++++++++++++++++++++ cue/cuex/example_test.go | 62 +++++++++++++++ cue/cuex/providers/cue/cue.cue | 23 ++++++ cue/cuex/providers/cue/cue.go | 63 +++++++++++++++ cue/cuex/providers/cue/cue_test.go | 120 +++++++++++++++++++++++++++++ 5 files changed, 373 insertions(+) create mode 100644 cue/cuex/default_compiler_test.go create mode 100644 cue/cuex/example_test.go create mode 100644 cue/cuex/providers/cue/cue.cue create mode 100644 cue/cuex/providers/cue/cue.go create mode 100644 cue/cuex/providers/cue/cue_test.go diff --git a/cue/cuex/default_compiler_test.go b/cue/cuex/default_compiler_test.go new file mode 100644 index 0000000..b669517 --- /dev/null +++ b/cue/cuex/default_compiler_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2023 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cuex_test + +import ( + "context" + "testing" + + "cuelang.org/go/cue" + "github.com/stretchr/testify/require" + "sigs.k8s.io/yaml" + + "github.com/kubevela/pkg/cue/cuex" + "github.com/kubevela/pkg/util/stringtools" +) + +func TestDefaultCompiler(t *testing.T) { + compiler := cuex.NewCompilerWithDefaultInternalPackages() + ctx := context.Background() + val, err := compiler.CompileString(ctx, ` + import ( + "vela/cue" + "strings" + ) + + scaler: { + description: "desc for scaler trait" + labels: {} + type: "trait" + } + + _enc: cue.Encode & { + $params: { + parameter: { + // +usage=Specify the number of workload + replicas: *1 | int + } + // +patchStrategy=retainKeys + patch: spec: replicas: parameter.replicas + } + } + + _template: _enc.$returns + + #EncodeDefinition: { + $params: { + name: string + meta: {...} + template: string + } + + $returns: { + apiVersion: "core.oam.dev/v1beta1" + kind: strings.ToTitle($params.meta.type) + "Definition" + metadata: name: $params.name + metadata: namespace: "vela-system" + metadata: annotations: "definition.oam.dev/description": $params.meta.description + spec: schematic: cue: template: $params.template + } + } + + _encode: #EncodeDefinition & { + $params: { + name: "scaler" + meta: scaler + template: _template + } + } + + $returns: _encode.$returns + `) + require.NoError(t, err) + bs, err := val.LookupPath(cue.ParsePath("$returns")).MarshalJSON() + require.NoError(t, err) + bs, err = yaml.JSONToYAML(bs) + require.NoError(t, err) + require.Equal(t, stringtools.TrimLeadingIndent(` + apiVersion: core.oam.dev/v1beta1 + kind: TraitDefinition + metadata: + annotations: + definition.oam.dev/description: desc for scaler trait + name: scaler + namespace: vela-system + spec: + schematic: + cue: + template: "parameter: {\n\t// +usage=Specify the number of workload\n\treplicas: + *1 | int\n}\n// +patchStrategy=retainKeys\npatch: spec: replicas: parameter.replicas\n" + `), string(bs)) +} diff --git a/cue/cuex/example_test.go b/cue/cuex/example_test.go new file mode 100644 index 0000000..a6eaaa0 --- /dev/null +++ b/cue/cuex/example_test.go @@ -0,0 +1,62 @@ +/* +Copyright 2023 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cuex + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/kubevela/pkg/cue/util" +) + +func TestCueXExample(t *testing.T) { + compiler := NewCompilerWithDefaultInternalPackages() + + src := ` + import ( + "vela/http" + "vela/kube" + ) + + getIP: http.#Get & { + $params: url: "https://api.ipify.org/" + } + + localIP: getIP.$returns.body + + apply: kube.#Apply & { + $params: resource: { + apiVersion: "v1" + kind: "Secret" + metadata: { + name: "ip" + namespace: "default" + } + stringData: ip: localIP + } + } + ` + + val, err := compiler.CompileString(context.Background(), src) + require.NoError(t, err) + s, err := util.ToString(val) + require.NoError(t, err) + fmt.Println(s) +} diff --git a/cue/cuex/providers/cue/cue.cue b/cue/cuex/providers/cue/cue.cue new file mode 100644 index 0000000..ece5d4e --- /dev/null +++ b/cue/cuex/providers/cue/cue.cue @@ -0,0 +1,23 @@ +package kube + +Encode: { + #do: "encode" + #provider: "cue" + + // +usage=The params of this action + $params: {...} + + // +usage=The result of this action + $returns: string +} + +Decode: { + #do: "decode" + #provider: "cue" + + // +usage=The params of this action + $params: string + + // +usage=The result of this action + $returns: {...} +} diff --git a/cue/cuex/providers/cue/cue.go b/cue/cuex/providers/cue/cue.go new file mode 100644 index 0000000..ae52dfd --- /dev/null +++ b/cue/cuex/providers/cue/cue.go @@ -0,0 +1,63 @@ +/* +Copyright 2023 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cue + +import ( + "context" + _ "embed" + + "cuelang.org/go/cue" + + "github.com/kubevela/pkg/cue/cuex/providers" + cuexruntime "github.com/kubevela/pkg/cue/cuex/runtime" + "github.com/kubevela/pkg/cue/util" + "github.com/kubevela/pkg/util/runtime" +) + +// ProviderName . +const ProviderName = "cue" + +//go:embed cue.cue +var template string + +// Encode given input cue.Value into cue string +func Encode(_ context.Context, in cue.Value) (cue.Value, error) { + s, _ := util.ToRawString(in) + _ = s + u, _ := util.ToString(in) + _ = u + str, err := util.ToRawString(in.LookupPath(cue.ParsePath(providers.ParamsKey))) + if err != nil { + return in, err + } + return in.FillPath(cue.ParsePath(providers.ReturnsKey), str), nil +} + +// Decode given cue string into cue.Value +func Decode(_ context.Context, in cue.Value) (cue.Value, error) { + str, err := in.LookupPath(cue.ParsePath(providers.ParamsKey)).String() + if err != nil { + return in, err + } + return in.FillPath(cue.ParsePath(providers.ReturnsKey), in.Context().CompileString(str)), nil +} + +// Package . +var Package = runtime.Must(cuexruntime.NewInternalPackage(ProviderName, template, map[string]cuexruntime.ProviderFn{ + "encode": cuexruntime.NativeProviderFn(Encode), + "decode": cuexruntime.NativeProviderFn(Decode), +})) diff --git a/cue/cuex/providers/cue/cue_test.go b/cue/cuex/providers/cue/cue_test.go new file mode 100644 index 0000000..9b82c3c --- /dev/null +++ b/cue/cuex/providers/cue/cue_test.go @@ -0,0 +1,120 @@ +/* +Copyright 2023 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cue_test + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "testing" + + "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/kubevela/pkg/cue/cuex/providers" + cueprovider "github.com/kubevela/pkg/cue/cuex/providers/cue" + "github.com/kubevela/pkg/cue/util" + "github.com/kubevela/pkg/util/stringtools" +) + +func TestEncode(t *testing.T) { + ctx := context.Background() + cctx := cuecontext.New() + val := cctx.CompileString(` + import "strconv" + { + $params: { + // test comment + key: *"key" | string + val: int & >= 0 + loop: *[1, 2, 3] | [...int] + if val > 1 { + loop: [2, 4, 6] + } + r: [for i in loop {strconv.FormatInt(i)}] + } + }`) + val, err := cueprovider.Encode(ctx, val) + require.NoError(t, err) + str, err := val.LookupPath(cue.ParsePath(providers.ReturnsKey)).String() + require.NoError(t, err) + require.Equal(t, stringtools.TrimLeadingIndent(` + import "strconv" + + // test comment + key: *"key" | string + val: int & >=0 + loop: *[1, 2, 3] | [...int] + if val > 1 { + loop: [2, 4, 6] + } + r: [ for i in loop { + strconv.FormatInt(i) + }]`), strings.TrimSpace(str)) +} + +func TestDecode(t *testing.T) { + ctx := context.Background() + cctx := cuecontext.New() + val := cctx.CompileString(` + { + $params: #""" + import "strconv" + + key: *"key" | string + val: int & >= 0 + loop: *[1, 2, 3] | [...int] + if val > 1 { + loop: [2, 4, 6] + } + r: [for i in loop {strconv.FormatInt(i)}] + """# + }`) + val, err := cueprovider.Decode(ctx, val) + require.NoError(t, err) + ret := val.LookupPath(cue.ParsePath(providers.ReturnsKey)) + str, err := util.ToRawString(ret) + require.NoError(t, err) + require.Equal(t, stringtools.TrimLeadingIndent(` + import "strconv" + + key: *"key" | string + val: int & >=0 + loop: *[1, 2, 3] | [...int] + if val > 1 { + loop: [2, 4, 6] + } + r: [ for i in loop { + strconv.FormatInt(i) + }]`), strings.TrimSpace(str)) +} + +type X struct { + A string `json:"a"` + B *runtime.RawExtension `json:"b"` +} + +func TestT(t *testing.T) { + x := &X{} + err := json.Unmarshal([]byte(`{"a":"x","b":{"ex":"y"}}`), x) + require.NoError(t, err) + fmt.Println(string(x.B.Raw)) + fmt.Println(x.B.Object) +}