From 3de9e391ee50e23884c491adc7047bbca2336ab6 Mon Sep 17 00:00:00 2001 From: Somefive Date: Sun, 23 Apr 2023 10:21:11 +0800 Subject: [PATCH] Feat: vela cuex eval (#5562) * Feat: vela cuex render Signed-off-by: Somefive * feat: CLI command `vela cuex eval ` Signed-off-by: Zhenghao Lou * responsive writer Signed-off-by: Somefive --------- Signed-off-by: Somefive Signed-off-by: Zhenghao Lou Co-authored-by: Zhenghao Lou --- go.mod | 2 +- go.sum | 4 +- references/cli/cli.go | 1 + references/cli/cuex.go | 116 ++++++++++++++++++++++ references/cli/cuex_test.go | 85 ++++++++++++++++ references/cli/test-data/cuex/foo.cue | 13 +++ references/cli/test-data/cuex/httpget.cue | 10 ++ 7 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 references/cli/cuex.go create mode 100644 references/cli/cuex_test.go create mode 100644 references/cli/test-data/cuex/foo.cue create mode 100644 references/cli/test-data/cuex/httpget.cue diff --git a/go.mod b/go.mod index b172f9c5b..b60a74f9c 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/hashicorp/hcl/v2 v2.16.2 github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 github.com/imdario/mergo v0.3.13 - github.com/kubevela/pkg v1.8.1-0.20230410075324-9f0ba3b09495 + github.com/kubevela/pkg v1.8.1-0.20230421100856-841accb6dd92 github.com/kubevela/workflow v0.5.1-0.20230412142834-be9e5a10baf0 github.com/kyokomi/emoji v2.2.4+incompatible github.com/magiconair/properties v1.8.7 diff --git a/go.sum b/go.sum index 3cc0b773b..2213cdac8 100644 --- a/go.sum +++ b/go.sum @@ -964,8 +964,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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/kubevela/pkg v1.8.1-0.20230410075324-9f0ba3b09495 h1:maT6E7JSrZgv4T1grgTBL69px7oUldc+tHIPxJ5SD9Y= -github.com/kubevela/pkg v1.8.1-0.20230410075324-9f0ba3b09495/go.mod h1:dGT23SGBw16frh5ReEYMFraxjppkX6jPCltBgTeLUhE= +github.com/kubevela/pkg v1.8.1-0.20230421100856-841accb6dd92 h1:wbsdJ6p6OhwUMRLpqCTFI1WxdjLxRrfWnNSHv/PVzsI= +github.com/kubevela/pkg v1.8.1-0.20230421100856-841accb6dd92/go.mod h1:dGT23SGBw16frh5ReEYMFraxjppkX6jPCltBgTeLUhE= github.com/kubevela/workflow v0.5.1-0.20230412142834-be9e5a10baf0 h1:/ZPmjKpd/+fpCjJfNfUnE7jdESuCcZeP+fyTUAU9an0= github.com/kubevela/workflow v0.5.1-0.20230412142834-be9e5a10baf0/go.mod h1:0GhIWFIPP+Zt31m4Aslx9mihoyNz3HrOvCV69ljMIBo= github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= diff --git a/references/cli/cli.go b/references/cli/cli.go index fb8bef0f5..1a4e81147 100644 --- a/references/cli/cli.go +++ b/references/cli/cli.go @@ -113,6 +113,7 @@ func NewCommandWithIOStreams(ioStream util.IOStreams) *cobra.Command { NewProviderCommand(commandArgs, "10", ioStream), AuthCommandGroup(f, ioStream), KubeCommandGroup(f, ioStream), + CueXCommandGroup(f), // Config management ConfigCommandGroup(f, ioStream), diff --git a/references/cli/cuex.go b/references/cli/cuex.go new file mode 100644 index 000000000..68fa803c1 --- /dev/null +++ b/references/cli/cuex.go @@ -0,0 +1,116 @@ +/* +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 cli + +import ( + "fmt" + "os" + + "github.com/kubevela/pkg/cue/cuex" + "github.com/kubevela/pkg/cue/util" + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" + + "github.com/oam-dev/kubevela/apis/types" + velacmd "github.com/oam-dev/kubevela/pkg/cmd" +) + +// CueXCommandGroup commands for cuex management +func CueXCommandGroup(f velacmd.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "cuex", + Short: i18n.T("Manage CueX engine for compile."), + Annotations: map[string]string{ + types.TagCommandType: types.TypeExtension, + }, + } + cmd.AddCommand(NewCueXEvalCommand(f)) + return cmd +} + +// CueXEvalOption option for compile cuex +type CueXEvalOption struct { + Format string + Path string + File string +} + +// Run compile cuex +func (in *CueXEvalOption) Run(cmd *cobra.Command) error { + if in.File == "" { + return fmt.Errorf("file must be provided for compile") + } + bs, err := os.ReadFile(in.File) + if err != nil { + return err + } + val, err := cuex.CompileString(cmd.Context(), string(bs)) + if err != nil { + return err + } + bs, err = util.Print(val, util.WithFormat(in.Format), util.WithPath(in.Path)) + if err != nil { + return err + } + cmd.Println(string(bs)) + return nil +} + +var ( + cuexEvalLong = templates.LongDesc(i18n.T(` + Eval cue file with CueX engine. + + Evaluate your cue file with the CueX engine. When your cue file does not + use KubeVela's extension, it will work similarly to the native CUE CLI. + When using KubeVela's extensions, this command will execute the extension + functions and resolve values, in addition to the native CUE compile process. + `)) + + cuexEvalExample = templates.Examples(i18n.T(` + # Evaluate a cue file + vela cuex eval -f my.cue + + # Evaluate a cue file into json format + vela cuex eval -f my.cue -o json + + # Evaluate a cue file and output the target path + vela cuex eval -f my.cue -p key.path + `)) +) + +// NewCueXEvalCommand `vela cuex eval` command +func NewCueXEvalCommand(f velacmd.Factory) *cobra.Command { + opt := &CueXEvalOption{ + Format: string(util.PrintFormatCue), + } + cmd := &cobra.Command{ + Use: "eval", + Short: i18n.T("Eval cue file with CueX engine."), + Long: cuexEvalLong, + Example: cuexEvalExample, + RunE: func(cmd *cobra.Command, args []string) error { + return opt.Run(cmd) + }, + } + cmd.Flags().StringVarP(&opt.Format, "format", "o", opt.Format, "format of the output") + cmd.Flags().StringVarP(&opt.File, "file", "f", opt.File, "file for eval") + cmd.Flags().StringVarP(&opt.Path, "path", "p", opt.Path, "path for eval") + return velacmd.NewCommandBuilder(f, cmd). + WithResponsiveWriter(). + Build() +} diff --git a/references/cli/cuex_test.go b/references/cli/cuex_test.go new file mode 100644 index 000000000..0b46f9f3b --- /dev/null +++ b/references/cli/cuex_test.go @@ -0,0 +1,85 @@ +/* +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 cli + +import ( + "bytes" + "path/filepath" + "testing" + + "github.com/kubevela/pkg/util/stringtools" + "github.com/stretchr/testify/require" +) + +const ( + testdataDir = "test-data/cuex" +) + +func TestCueXEval(t *testing.T) { + buffer := bytes.NewBuffer(nil) + cmd := NewCueXEvalCommand(nil) + cmd.SetOut(buffer) + + testCases := map[string]struct { + filepath string + path string + format string + expect string + }{ + "normal evaluate": { + filepath.Join(testdataDir, "foo.cue"), + "S.name", + "", + "\"Postgres\"", + }, + "json": { + filepath.Join(testdataDir, "foo.cue"), + "", + "json", + `{"label":"app","S":{"name":"Postgres","version":"13","label":"app","image":"docker.io/postgres:13"}}`, + }, + "yaml": { + filepath.Join(testdataDir, "foo.cue"), + "", + "yaml", + ` + S: + image: docker.io/postgres:13 + label: app + name: Postgres + version: "13" + label: app`, + }, + "http": { + filepath.Join(testdataDir, "httpget.cue"), + "statusCode", + "", + "200", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + buffer.Truncate(0) + r := require.New(t) + cmd.SetArgs([]string{"-f", tc.filepath, "-p", tc.path, "-o", tc.format}) + err := cmd.Execute() + r.NoError(err) + r.Equal(stringtools.TrimLeadingIndent(tc.expect), stringtools.TrimLeadingIndent(buffer.String())) + }) + } +} diff --git a/references/cli/test-data/cuex/foo.cue b/references/cli/test-data/cuex/foo.cue new file mode 100644 index 000000000..8a7a2f556 --- /dev/null +++ b/references/cli/test-data/cuex/foo.cue @@ -0,0 +1,13 @@ +import "strings" + +label: "app" +// alias for label +let L = label +S: { + name: "Postgres" + // intermediate value + let lower = strings.ToLower(name) + version: "13" + label: L + image: "docker.io/\(lower):\(version)" +} diff --git a/references/cli/test-data/cuex/httpget.cue b/references/cli/test-data/cuex/httpget.cue new file mode 100644 index 000000000..40184e223 --- /dev/null +++ b/references/cli/test-data/cuex/httpget.cue @@ -0,0 +1,10 @@ +import "vela/http" + +req: http.#Get & { + $params: { + url: "https://cuelang.org" + } +} + +body: req.$returns.body +statusCode: req.$returns.statusCode