From 63f9400afbcdc3f4bd8e23d93052bd8f439b8d1a Mon Sep 17 00:00:00 2001 From: FogDong Date: Tue, 25 Apr 2023 11:26:27 +0800 Subject: [PATCH] use singelton for compiler Signed-off-by: FogDong --- go.mod | 1 - go.sum | 2 - pkg/cue/README.md | 1 - pkg/cue/model/value/error.go | 29 - pkg/cue/model/value/value.go | 619 +--------- pkg/cue/model/value/value_test.go | 1013 +---------------- pkg/cue/package_suit_test.go | 675 ----------- pkg/cue/packages/package.go | 465 -------- pkg/cue/packages/package_test.go | 639 ----------- pkg/cue/suit_test.go | 79 -- pkg/cue/utils.go | 93 -- pkg/cue/utils_test.go | 155 --- pkg/errors/errors.go | 10 + pkg/executor/workflow.go | 16 +- pkg/generator/generator.go | 1 - pkg/providers/compiler.go | 21 +- pkg/providers/legacy/email/email.go | 3 +- pkg/providers/legacy/email/email_test.go | 6 +- pkg/providers/legacy/http/http.go | 3 +- pkg/providers/legacy/http/http_test.go | 9 +- pkg/providers/legacy/kube/kube.go | 66 +- pkg/providers/legacy/kube/kube_test.go | 32 +- pkg/providers/legacy/legacy.go | 5 +- pkg/providers/legacy/metrics/prom_check.go | 3 +- .../legacy/metrics/prom_check_test.go | 4 +- pkg/providers/legacy/util/util.go | 6 +- pkg/providers/legacy/util/util_test.go | 6 +- pkg/providers/legacy/workspace/workspace.go | 9 +- .../legacy/workspace/workspace_test.go | 24 +- pkg/providers/types/types.go | 58 +- pkg/tasks/custom/task.go | 2 +- pkg/tasks/custom/task_test.go | 151 +-- pkg/types/types.go | 17 - pkg/utils/operation.go | 11 +- .../v1alpha1/workflowrun/suite_test.go | 10 +- .../workflowrun/validating_handler.go | 2 - 36 files changed, 204 insertions(+), 4042 deletions(-) delete mode 100644 pkg/cue/model/value/error.go delete mode 100644 pkg/cue/package_suit_test.go delete mode 100644 pkg/cue/packages/package.go delete mode 100644 pkg/cue/packages/package_test.go delete mode 100644 pkg/cue/suit_test.go delete mode 100644 pkg/cue/utils.go delete mode 100644 pkg/cue/utils_test.go diff --git a/go.mod b/go.mod index ab34f57..428df85 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/agiledragon/gomonkey/v2 v2.4.0 github.com/aliyun/aliyun-log-go-sdk v0.1.38 github.com/crossplane/crossplane-runtime v0.19.2 - github.com/cue-exp/kubevelafix v0.0.0-20220922150317-aead819d979d github.com/evanphx/json-patch v5.6.0+incompatible github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da github.com/google/go-cmp v0.5.9 diff --git a/go.sum b/go.sum index 62ce0f1..4a2a7e3 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crossplane/crossplane-runtime v0.19.2 h1:9qBnhpqKN4x6apF2siaQ6PvgxqBXbGcKmgeD8mSIDO8= github.com/crossplane/crossplane-runtime v0.19.2/go.mod h1:OJQ1NxtQK2ZTRmvtnQPoy8LsXsARTnVydRVDQEgIuz4= -github.com/cue-exp/kubevelafix v0.0.0-20220922150317-aead819d979d h1:VNJA1nSKA8Xna5wjUIMItHlWmEej8Bb9fZ3vCNtIAX0= -github.com/cue-exp/kubevelafix v0.0.0-20220922150317-aead819d979d/go.mod h1:SyTryzw/zYJIogw3H2IRcYdV5gsSoVMJiKGElcQK09I= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/pkg/cue/README.md b/pkg/cue/README.md index 9ede780..a888547 100644 --- a/pkg/cue/README.md +++ b/pkg/cue/README.md @@ -7,4 +7,3 @@ The following packages need to be tested without external/tool dependencies, So - github.com/kubevela/workflow/pkg/cue/model/sets - github.com/kubevela/workflow/pkg/cue/process - github.com/kubevela/workflow/pkg/cue/task -- github.com/kubevela/workflow/pkg/cue/packages diff --git a/pkg/cue/model/value/error.go b/pkg/cue/model/value/error.go deleted file mode 100644 index f55e9d0..0000000 --- a/pkg/cue/model/value/error.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2022 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 value - -import ( - "fmt" -) - -// LookUpNotFoundErr is the error type of lookup -type LookUpNotFoundErr string - -// Error . -func (e LookUpNotFoundErr) Error() string { - return fmt.Sprintf("failed to lookup value: var(path=%s) not exist", string(e)) -} diff --git a/pkg/cue/model/value/value.go b/pkg/cue/model/value/value.go index e0f50d1..dce0a7c 100644 --- a/pkg/cue/model/value/value.go +++ b/pkg/cue/model/value/value.go @@ -17,9 +17,7 @@ limitations under the License. package value import ( - "encoding/json" "fmt" - "sort" "strconv" "strings" @@ -29,260 +27,16 @@ import ( "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/format" "cuelang.org/go/cue/parser" - "github.com/cue-exp/kubevelafix" "github.com/pkg/errors" "github.com/kubevela/pkg/cue/util" "github.com/kubevela/workflow/pkg/cue/model/sets" - "github.com/kubevela/workflow/pkg/cue/packages" - "github.com/kubevela/workflow/pkg/stdlib" + workflowerrors "github.com/kubevela/workflow/pkg/errors" ) // 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.Context - field string - addImports func(instance *build.Instance) error -} - -// String return value's cue format string -func (val *Value) String(opts ...func(node ast.Node) ast.Node) (string, error) { - opts = append(opts, sets.OptBytesToString) - return sets.ToString(val.v, opts...) -} - -// Error return value's error information. -func (val *Value) Error() error { - v := val.CueValue() - 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 { - gerr = err - return false - } - return true - }, nil) - return gerr -} - -// UnmarshalTo unmarshal value into golang object -func (val *Value) UnmarshalTo(x interface{}) error { - data, err := val.v.MarshalJSON() - if err != nil { - return err - } - return json.Unmarshal(data, x) -} - -// SubstituteInStruct substitute expr in struct lit value -// nolint:staticcheck -func (val *Value) SubstituteInStruct(expr ast.Expr, key string) error { - node := val.CueValue().Syntax(cue.ResolveReferences(true)) - x, ok := node.(*ast.StructLit) - if !ok { - return errors.New("value is not a struct lit") - } - for i := range x.Elts { - if field, ok := x.Elts[i].(*ast.Field); ok { - if strings.Trim(sets.LabelStr(field.Label), `"`) == strings.Trim(key, `"`) { - x.Elts[i].(*ast.Field).Value = expr - b, err := format.Node(node) - if err != nil { - return err - } - val.v = val.r.CompileBytes(b) - return nil - } - } - } - return errors.New("key not found in struct") -} - -// FieldName return value's field name -func (val *Value) FieldName() string { - return val.field -} - -// NewValue new a value -func NewValue(s string, pd *packages.PackageDiscover, tagTempl string, opts ...func(*ast.File) error) (*Value, error) { - builder := &build.Instance{} - - file, err := parser.ParseFile("-", s, parser.ParseComments) - if err != nil { - return nil, err - } - file = kubevelafix.Fix(file).(*ast.File) - for _, opt := range opts { - if err := opt(file); err != nil { - return nil, err - } - } - if err := builder.AddSyntax(file); err != nil { - return nil, err - } - return newValue(builder, pd, tagTempl) -} - -// NewValueWithInstance new value with instance -func NewValueWithInstance(instance *build.Instance, pd *packages.PackageDiscover, tagTempl string) (*Value, error) { - return newValue(instance, 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) - } - if err := stdlib.AddImportsFor(inst, tagTempl); err != nil { - return err - } - return nil - } - - if err := addImports(builder); err != nil { - return nil, err - } - - r := cuecontext.New() - inst := r.BuildInstance(builder) - val := new(Value) - val.r = r - 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) - file = kubevelafix.Fix(file).(*ast.File) - 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 - vs := &visitor{ - r: map[string]struct{}{}, - } - for _, decl := range root.Decls { - vs.addAttrForExpr(decl, &i) - } - return nil -} - -type visitor struct { - r map[string]struct{} -} - -func (vs *visitor) done(name string) { - vs.r[name] = struct{}{} -} - -func (vs *visitor) shouldDo(name string) bool { - _, ok := vs.r[name] - return !ok -} -func (vs *visitor) addAttrForExpr(node ast.Node, index *int) { - switch v := node.(type) { - case *ast.Comprehension: - st := v.Value.(*ast.StructLit) - for _, elt := range st.Elts { - vs.addAttrForExpr(elt, index) - } - case *ast.Field: - basic, ok := v.Label.(*ast.Ident) - if !ok { - return - } - if !vs.shouldDo(basic.Name) { - return - } - if v.Attrs == nil { - *index++ - vs.done(basic.Name) - v.Attrs = []*ast.Attribute{ - {Text: fmt.Sprintf("@step(%d)", *index)}, - } - } - } -} - -// MakeValue generate an value with same runtime -func (val *Value) MakeValue(s string) (*Value, error) { - builder := &build.Instance{} - file, err := parser.ParseFile("-", s, parser.ParseComments) - if err != nil { - return nil, err - } - if err := builder.AddSyntax(file); err != nil { - return nil, err - } - 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 - 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 { - for _, importSpec := range f.Imports { - if _, ok := imports[importSpec.Name.String()]; !ok { - imports[importSpec.Name.String()] = importSpec - } - } - newFile.Decls = append(newFile.Decls, f.Decls...) - } - - for _, imp := range imports { - newFile.Imports = append(newFile.Imports, imp) - } - - if err := builder.AddSyntax(newFile); err != nil { - return nil, err - } - 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 - v.addImports = val.addImports - return v, nil -} - // FillRaw unify the value with the cue format string x at the given path. func FillRaw(val cue.Value, x string, paths ...string) (cue.Value, error) { file, err := parser.ParseFile("-", x, parser.ParseComments) @@ -297,36 +51,6 @@ func FillRaw(val cue.Value, x string, paths ...string) (cue.Value, error) { 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 { - file, err := parser.ParseFile("-", x, parser.ParseComments) - if err != nil { - return err - } - xInst := val.r.BuildFile(file) - v := val.v.FillPath(FieldPath(paths...), xInst) - if v.Err() != nil { - return v.Err() - } - val.v = v - return nil -} - -// FillValueByScript unify the value x at the given script path. -func (val *Value) FillValueByScript(x *Value, path string) error { - f, err := sets.OpenListLit(val.v) - if err != nil { - return err - } - v := val.r.BuildFile(f) - newV := v.FillPath(FieldPath(path), x.v) - if err := newV.Err(); err != nil { - return err - } - val.v = newV - return nil -} - func setValue(orig ast.Node, expr ast.Expr, selectors []cue.Selector) error { if len(selectors) == 0 { return nil @@ -399,85 +123,6 @@ func SetValueByScript(base, v cue.Value, path ...string) (cue.Value, error) { return base.Context().CompileBytes(b), nil } -// SetValueByScript set the value v at the given script path. -// nolint:staticcheck -func (val *Value) SetValueByScript(v *Value, path ...string) error { - cuepath := FieldPath(path...) - selectors := cuepath.Selectors() - node := val.CueValue().Syntax(cue.ResolveReferences(true)) - if err := setValue(node, v.CueValue().Syntax(cue.ResolveReferences(true)).(ast.Expr), selectors); err != nil { - return err - } - b, err := format.Node(node) - if err != nil { - return err - } - val.v = val.r.CompileBytes(b) - return nil -} - -// CueValue return cue.Value -func (val *Value) CueValue() cue.Value { - return val.v -} - -// FillObject unify the value with object x at the given path. -func (val *Value) FillObject(x interface{}, paths ...string) error { - insert := x - if v, ok := x.(*Value); ok { - if v.r != val.r { - return errors.New("filled value not created with same Runtime") - } - insert = v.v - } - 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 -} - -// SetObject set the value with object x at the given path. -func (val *Value) SetObject(x interface{}, paths ...string) error { - insert := &Value{ - r: val.r, - } - switch v := x.(type) { - case *Value: - if v.r != val.r { - return errors.New("filled value not created with same Runtime") - } - insert.v = v.v - case ast.Expr: - cueV := val.r.BuildExpr(v) - insert.v = cueV - default: - return fmt.Errorf("not support type %T", x) - } - return val.SetValueByScript(insert, paths...) -} - -// LookupValue reports the value at a path starting from val -func (val *Value) LookupValue(paths ...string) (*Value, error) { - v := val.v.LookupPath(FieldPath(paths...)) - if !v.Exists() { - return nil, errors.Errorf("failed to lookup value: var(path=%s) not exist", strings.Join(paths, ".")) - } - var field string - if len(paths) > 0 { - index := len(paths) - 1 - if index < 0 { - index = 0 - } - field = paths[index] - } - return &Value{ - v: v, - r: val.r, - field: field, - addImports: val.addImports, - }, nil -} - func isScript(content string) (bool, error) { content = strings.TrimSpace(content) scriptFile, err := parser.ParseFile("-", content, parser.ParseComments) @@ -521,7 +166,7 @@ func LookupValueByScript(val cue.Value, script string) (cue.Value, error) { if !isScriptPath { v := val.LookupPath(cue.ParsePath(script)) if !v.Exists() { - return cue.Value{}, LookUpNotFoundErr(script) + return cue.Value{}, workflowerrors.LookUpNotFoundErr(script) } } @@ -544,7 +189,7 @@ func LookupValueByScript(val cue.Value, script string) (cue.Value, error) { v := newV.LookupPath(cue.ParsePath(outputKey)) if !v.Exists() { - return cue.Value{}, LookUpNotFoundErr(outputKey) + return cue.Value{}, workflowerrors.LookUpNotFoundErr(outputKey) } return v, nil } @@ -578,46 +223,6 @@ func makeValueWithFiles(files ...*ast.File) (cue.Value, error) { return v, nil } -// LookupByScript reports the value by cue script. -func (val *Value) LookupByScript(script string) (*Value, error) { - var outputKey = "zz_output__" - script = strings.TrimSpace(script) - scriptFile, err := parser.ParseFile("-", script, parser.ParseComments) - 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 { - return nil, err - } - - rawFile, err := parser.ParseFile("-", raw, parser.ParseComments) - if err != nil { - return nil, errors.WithMessage(err, "parse script") - } - - behindKey(scriptFile, outputKey) - - newV, err := val.makeValueWithFile(rawFile, scriptFile) - if err != nil { - return nil, err - } - if newV.Error() != nil { - return nil, newV.Error() - } - - return newV.LookupValue(outputKey) -} - func behindKey(file *ast.File, key string) { var ( implDecls []ast.Decl @@ -652,224 +257,6 @@ func behindKey(file *ast.File, key string) { } -type field struct { - Name string - Value *Value - no int64 -} - -// StepByList process item in list. -func (val *Value) StepByList(handle func(name string, in *Value) (bool, error)) error { - iter, err := val.CueValue().List() - if err != nil { - return err - } - for iter.Next() { - stop, err := handle(iter.Label(), &Value{ - v: iter.Value(), - r: val.r, - field: iter.Label(), - addImports: val.addImports, - }) - if err != nil { - return err - } - if stop { - return nil - } - } - return nil -} - -// StepByFields process the fields in order -func (val *Value) StepByFields(handle func(name string, in *Value) (bool, error)) error { - iter := steps(val) - for iter.next() { - iter.do(handle) - } - return iter.err -} - -type stepsIterator struct { - queue []*field - index int - target *Value - err error - stopped bool -} - -func steps(v *Value) *stepsIterator { - return &stepsIterator{ - target: v, - } -} - -func (iter *stepsIterator) next() bool { - if iter.stopped { - return false - } - if iter.err != nil { - return false - } - if iter.queue != nil { - iter.index++ - } - iter.assemble() - return iter.index <= len(iter.queue)-1 -} - -func (iter *stepsIterator) assemble() { - filters := map[string]struct{}{} - for _, item := range iter.queue { - filters[item.Name] = struct{}{} - } - cueIter, err := iter.target.v.Fields(cue.Definitions(true), cue.Hidden(true), cue.All()) - if err != nil { - iter.err = err - return - } - var addFields []*field - for cueIter.Next() { - val := cueIter.Value() - name := cueIter.Label() - if val.IncompleteKind() == cue.TopKind { - continue - } - attr := val.Attribute("step") - no, err := attr.Int(0) - if err != nil { - no = 100 - if name == "#do" || name == "#provider" { - no = 0 - } - } - if _, ok := filters[name]; !ok { - addFields = append(addFields, &field{ - Name: name, - no: no, - }) - } - } - - suffixItems := addFields - suffixItems = append(suffixItems, 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, - field: iter.name(), - 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() - v.field = iter.name() - 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 - } - } -} - -type sortFields []*field - -func (sf sortFields) Len() int { - return len(sf) -} -func (sf sortFields) Less(i, j int) bool { - return sf[i].no < sf[j].no -} - -func (sf sortFields) Swap(i, j int) { - sf[i], sf[j] = sf[j], sf[i] -} - -// Field return the cue value corresponding to the specified field -func (val *Value) Field(label string) (cue.Value, error) { - v := val.v.LookupPath(cue.ParsePath(label)) - if !v.Exists() { - return v, errors.Errorf("label %s not found", label) - } - - if v.IncompleteKind() == cue.BottomKind { - return v, errors.Errorf("label %s's value not computed", label) - } - return v, nil -} - -// GetString get the string value at a path starting from v. -func (val *Value) GetString(paths ...string) (string, error) { - v, err := val.LookupValue(paths...) - if err != nil { - return "", err - } - return v.CueValue().String() -} - -// GetStringSlice get string slice from val -func (val *Value) GetStringSlice(paths ...string) ([]string, error) { - v, err := val.LookupValue(paths...) - if err != nil { - return nil, err - } - var s []string - err = v.UnmarshalTo(&s) - return s, err -} - -// GetInt64 get the int value at a path starting from v. -func (val *Value) GetInt64(paths ...string) (int64, error) { - v, err := val.LookupValue(paths...) - if err != nil { - return 0, err - } - return v.CueValue().Int64() -} - -// GetBool get the int value at a path starting from v. -func (val *Value) GetBool(paths ...string) (bool, error) { - v, err := val.LookupValue(paths...) - if err != nil { - return false, err - } - return v.CueValue().Bool() -} - -// OpenCompleteValue make that the complete value can be modified. -func (val *Value) OpenCompleteValue() error { - newS, err := sets.OpenBaiscLit(val.CueValue()) - if err != nil { - return err - } - - v := cuecontext.New().BuildFile(newS) - val.v = v - return nil -} -func isDef(s string) bool { - return strings.HasPrefix(s, "#") -} - // makePath creates a Path from a sequence of string. func makePath(paths ...string) string { mergedPath := "" diff --git a/pkg/cue/model/value/value_test.go b/pkg/cue/model/value/value_test.go index 07c138e..f4d24ee 100644 --- a/pkg/cue/model/value/value_test.go +++ b/pkg/cue/model/value/value_test.go @@ -17,16 +17,13 @@ limitations under the License. package value import ( - "encoding/json" "fmt" "testing" "cuelang.org/go/cue" - "cuelang.org/go/cue/format" - cuejson "cuelang.org/go/pkg/encoding/json" - "github.com/kubevela/workflow/pkg/cue/model/sets" + "cuelang.org/go/cue/cuecontext" + "github.com/kubevela/pkg/cue/util" - "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -42,25 +39,8 @@ object: { } } ` - testVal1, err := NewValue(src, nil, "") - r.NoError(err) - err = testVal1.FillObject(12, "object", "x") - r.NoError(err) - err = testVal1.FillObject("y_string", "object", "y") - r.NoError(err) - z, err := testVal1.MakeValue(` -z: { - provider: string - do: "apply" -} -`) - r.NoError(err) - err = z.FillObject("kube", "z", "provider") - r.NoError(err) - err = testVal1.FillObject(z, "object") - r.NoError(err) - val1String, err := testVal1.String() - r.NoError(err) + cuectx := cuecontext.New() + testVal1 := cuectx.CompileString(src) expectedValString := `object: { x: 12 @@ -69,351 +49,14 @@ z: { provider: "kube" do: "apply" } -} -` - r.Equal(val1String, expectedValString) - - testVal2, err := NewValue(src, nil, "") - r.NoError(err) - err = testVal2.FillRaw(expectedValString) +}` + res, err := FillRaw(testVal1, expectedValString, "") r.NoError(err) - val2String, err := testVal1.String() + val2String, err := util.ToString(res) r.NoError(err) r.Equal(val2String, expectedValString) } -func TestStepByFields(t *testing.T) { - testCases := []struct { - base string - expected string - }{ - {base: ` -step1: {} -step2: {prefix: step1.value} -step3: {prefix: step2.value} -step4: {prefix: step3.value} -if step4.value > 100 { - step5: {prefix: step4.value} -} -`, - expected: `step1: { - value: 100 -} -step2: { - prefix: 100 - value: 101 -} -step3: { - prefix: 101 - value: 102 -} -step5: { - prefix: 103 - value: 104 -} -step4: { - prefix: 102 - value: 103 -} -`}, - - {base: ` -step1: {} -step2: {prefix: step1.value} -if step2.value > 100 { - step2_3: {prefix: step2.value} -} -step3: {prefix: step2.value} -`, - expected: `step1: { - value: 100 -} -step2: { - prefix: 100 - value: 101 -} -step2_3: { - prefix: 101 - value: 102 -} -step3: { - prefix: 101 - value: 103 -} -`}, - } - - for _, tCase := range testCases { - r := require.New(t) - val, err := NewValue(tCase.base, nil, "") - r.NoError(err) - number := 99 - err = val.StepByFields(func(_ string, in *Value) (bool, error) { - number++ - return false, in.FillObject(map[string]interface{}{ - "value": number, - }) - }) - r.NoError(err) - str, err := val.String() - r.NoError(err) - r.Equal(str, tCase.expected) - } - - r := require.New(t) - caseSkip := ` -step1: "1" -step2: "2" -step3: "3" -` - val, err := NewValue(caseSkip, nil, "") - r.NoError(err) - inc := 0 - err = val.StepByFields(func(_ string, in *Value) (bool, error) { - inc++ - s, err := in.CueValue().String() - r.NoError(err) - if s == "2" { - return true, nil - } - return false, nil - }) - r.NoError(err) - r.Equal(inc, 2) - - inc = 0 - err = val.StepByFields(func(_ string, in *Value) (bool, error) { - inc++ - s, err := in.CueValue().String() - r.NoError(err) - if s == "2" { - return false, errors.New("mock error") - } - return false, nil - }) - r.Error(err) - r.Equal(inc, 2) - - inc = 0 - err = val.StepByFields(func(_ string, in *Value) (bool, error) { - inc++ - s, err := in.CueValue().String() - r.NoError(err) - if s == "2" { - v, err := NewValue("v: 33", nil, "") - r.NoError(err) - *in = *v - } - return false, nil - }) - r.Error(err) - r.Equal(inc, 2) -} - -func TestStepWithTag(t *testing.T) { - testCases := []struct { - base string - expected string - }{ - {base: ` -step1: {} -step2: {prefix: step1.value} -step3: {prefix: step2.value} -step4: {prefix: step3.value} -if step4.value > 100 { - step5: {} -} -step5: { - value: *100|int -} -`, - expected: `step1: { - value: 100 -} @step(1) -step2: { - prefix: 100 - value: 101 -} @step(2) -step3: { - prefix: 101 - value: 102 -} @step(3) -step4: { - prefix: 102 - value: 103 -} @step(4) -step5: { - value: 104 -} @step(5) -`}, {base: ` -step1: {} -step2: {prefix: step1.value} -if step2.value > 100 { - step2_3: {prefix: step2.value} -} -step3: {prefix: step2.value} -step4: {prefix: step3.value} -`, - expected: `step1: { - value: 100 -} @step(1) -step2: { - prefix: 100 - value: 101 -} @step(2) -step3: { - prefix: 101 - value: 103 -} @step(4) -step2_3: { - prefix: 101 - value: 102 -} @step(3) -step4: { - prefix: 103 - value: 104 -} @step(5) -`}, {base: ` -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) -} -`, - expected: `step2: { - prefix: 100 - value: 101 -} @step(2) -step1: { - value: 100 -} @step(1) -step2_3: { - prefix: 101 - value: 102 -} @step(3) -step3: { - prefix: 101 - value: 103 -} @step(4) -`}, - - {base: ` -step2: {prefix: step1.value} -step1: {} @step(-1) -if step2.value > 100 { - step2_3: {prefix: step2.value} -} -step3: {prefix: step2.value} -`, - expected: `step2: { - prefix: 100 - value: 101 -} @step(1) -step1: { - value: 100 -} @step(-1) -step2_3: { - prefix: 101 - value: 102 -} @step(2) -step3: { - prefix: 101 - value: 103 -} @step(3) -`}} - - for i, tCase := range testCases { - r := require.New(t) - val, err := NewValue(tCase.base, nil, "", TagFieldOrder) - r.NoError(err) - number := 99 - err = val.StepByFields(func(name string, in *Value) (bool, error) { - number++ - return false, in.FillObject(map[string]interface{}{ - "value": number, - }) - }) - r.NoError(err) - str, err := sets.ToString(val.CueValue()) - r.NoError(err) - r.Equal(str, tCase.expected, fmt.Sprintf("testPatch for case(no:%d) %s", i, str)) - } -} - -func TestUnmarshal(t *testing.T) { - case1 := ` -provider: "kube" -do: "apply" -` - out := struct { - Provider string `json:"provider"` - Do string `json:"do"` - }{} - - r := require.New(t) - val, err := NewValue(case1, nil, "") - r.NoError(err) - err = val.UnmarshalTo(&out) - r.NoError(err) - r.Equal(out.Provider, "kube") - r.Equal(out.Do, "apply") - - bt, err := val.CueValue().MarshalJSON() - r.NoError(err) - expectedJson, err := json.Marshal(out) - r.NoError(err) - r.Equal(string(bt), string(expectedJson)) - - caseIncomplete := ` -provider: string -do: string -` - val, err = NewValue(caseIncomplete, nil, "") - r.NoError(err) - err = val.UnmarshalTo(&out) - r.Error(err) -} - -func TestStepByList(t *testing.T) { - r := require.New(t) - base := `[{step: 1},{step: 2}]` - v, err := NewValue(base, nil, "") - r.NoError(err) - var i int64 - err = v.StepByList(func(name string, in *Value) (bool, error) { - i++ - num, err := in.CueValue().LookupPath(FieldPath("step")).Int64() - r.NoError(err) - r.Equal(num, i) - return false, nil - }) - r.NoError(err) - - i = 0 - err = v.StepByList(func(_ string, _ *Value) (bool, error) { - i++ - return true, nil - }) - r.NoError(err) - r.Equal(i, int64(1)) - - i = 0 - err = v.StepByList(func(_ string, _ *Value) (bool, error) { - i++ - return false, errors.New("mock error") - }) - r.Equal(err.Error(), "mock error") - r.Equal(i, int64(1)) - - notListV, err := NewValue(`{}`, nil, "") - r.NoError(err) - err = notListV.StepByList(func(_ string, _ *Value) (bool, error) { - return false, nil - }) - r.Error(err) -} - func TestFieldPath(t *testing.T) { testCases := []struct { paths []string @@ -465,570 +108,44 @@ func TestFieldPath(t *testing.T) { } } -func TestValueFix(t *testing.T) { - testCases := []struct { - original string - expected string - }{ - { - original: ` -parameter: test: _ -// comment -y: { - for k, v in parameter.test.p { - "\(k)": v - } -}`, - expected: `{ - parameter: { - test: _ - } - // comment - y: { - for k, v in *parameter.test.p | {} { - "\(k)": v - } - } -}`, - }, - } - for i, tc := range testCases { - t.Run(fmt.Sprint(i), func(t *testing.T) { - r := require.New(t) - v, err := NewValue(tc.original, nil, "") - r.NoError(err) - b, err := format.Node(v.CueValue().Syntax(cue.Docs(true))) - r.NoError(err) - r.Equal(tc.expected, string(b)) - }) - } -} - -func TestValue(t *testing.T) { - - // Test NewValue with wrong cue format. - caseError := ` -provider: xxx -` - r := require.New(t) - val, err := NewValue(caseError, nil, "") - r.NoError(err) - r.Error(val.Error()) - - val, err = NewValue(":", nil, "") - r.Error(err) - r.Equal(val == nil, true) - - // Test make error by Fill with wrong cue format. - caseOk := ` -provider: "kube" -do: "apply" -` - val, err = NewValue(caseOk, nil, "") - r.NoError(err) - originCue := val.CueValue() - - _, err = val.MakeValue(caseError) - 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()) - cv, err := NewValue(caseOk, nil, "") - r.NoError(err) - err = val.FillObject(cv) - r.Error(err) - r.Equal(originCue, val.CueValue()) - - // Test make error by Fill with cue eval error. - caseClose := ` -close({provider: int}) -` - err = val.FillRaw(caseClose) - r.Error(err) - r.Equal(originCue, val.CueValue()) - cv, err = val.MakeValue(caseClose) - r.NoError(err) - err = val.FillObject(cv) - r.NoError(err) - r.Error(val.Error()) - - _, err = val.LookupValue("abc") - r.Error(err) - - providerValue, err := val.LookupValue("provider") - r.NoError(err) - err = providerValue.StepByFields(func(_ string, in *Value) (bool, error) { - return false, nil - }) - r.Error(err) - - openSt := ` -#X: {...} -x: #X & { - name: "xxx" - age: 12 -} -` - val, err = NewValue(openSt, nil, "") - r.NoError(err) - x, _ := val.LookupValue("x") - xs, _ := x.String() - _, err = val.MakeValue(xs) - 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" -do: "apply" -` - r := require.New(t) - val, err := NewValue(caseOk, nil, "") - r.NoError(err) - err = val.FillRaw(` -provider: "conflict"`) - r.Error(err) - - val, err = NewValue(caseOk, nil, "") - r.NoError(err) - err = val.FillObject(map[string]string{ - "provider": "abc", - }) - r.NoError(err) - r.Error(val.Error()) -} - -func TestField(t *testing.T) { - caseSrc := ` -name: "foo" -#name: "fly" -#age: 100 -bottom: _|_ -` - r := require.New(t) - val, err := NewValue(caseSrc, nil, "") - r.NoError(err) - - name, err := val.Field("name") - r.NoError(err) - nameValue, err := name.String() - r.NoError(err) - r.Equal(nameValue, "foo") - - dname, err := val.Field("#name") - r.NoError(err) - nameValue, err = dname.String() - r.NoError(err) - r.Equal(nameValue, "fly") - - _, err = val.Field("age") - r.Error(err) - - _, err = val.Field("bottom") - r.Error(err) -} - -func TestLookupByScript(t *testing.T) { - testCases := []struct { - src string - script string - expect string - }{ - { - src: ` -traits: { - ingress: { - // +patchKey=name - test: [{name: "main", image: "busybox"}] - } -} -`, - script: `traits["ingress"]`, - expect: `// +patchKey=name -test: [{ - name: "main" - image: "busybox" -}] -`, - }, - { - src: ` -apply: containers: [{name: "main", image: "busybox"}] -`, - script: `apply.containers[0].image`, - expect: `"busybox" -`, - }, - { - src: ` -apply: workload: name: "main" -`, - script: ` -apply.workload.name`, - expect: `"main" -`, - }, - { - src: ` -apply: arr: ["abc","def"] -`, - script: ` -import "strings" -strings.Join(apply.arr,".")+"$"`, - expect: `"abc.def$" -`, - }, - } - - for _, tCase := range testCases { - r := require.New(t) - srcV, err := NewValue(tCase.src, nil, "") - r.NoError(err) - v, err := srcV.LookupByScript(tCase.script) - r.NoError(err) - result, _ := v.String() - r.Equal(tCase.expect, result) - } - - errorCases := []struct { - src string - script string - err string - }{ - { - src: ` - op: string - op: "help" -`, - script: `op(1`, - err: "parse script: expected ')', found 'EOF'", - }, - { - src: ` - op: string - op: "help" -`, - script: `oss`, - err: "failed to lookup value: var(path=oss) not exist", - }, - } - - for _, tCase := range errorCases { - r := require.New(t) - srcV, err := NewValue(tCase.src, nil, "") - r.NoError(err) - _, err = srcV.LookupByScript(tCase.script) - r.Error(err, tCase.err) - r.Equal(err.Error(), tCase.err) - } -} - -func TestGet(t *testing.T) { - caseOk := ` -strKey: "xxx" -intKey: 100 -boolKey: true -` - r := require.New(t) - val, err := NewValue(caseOk, nil, "") - r.NoError(err) - - str, err := val.GetString("strKey") - r.NoError(err) - r.Equal(str, "xxx") - // err case - _, err = val.GetInt64("strKey") - r.Error(err) - - intv, err := val.GetInt64("intKey") - r.NoError(err) - r.Equal(intv, int64(100)) - // err case - _, err = val.GetBool("intKey") - r.Error(err) - - ok, err := val.GetBool("boolKey") - r.NoError(err) - r.Equal(ok, true) - // err case - _, err = val.GetString("boolKey") - r.Error(err) -} - -func TestImports(t *testing.T) { - cont := ` -context: stepSessionID: "3w9qkdgn5w"` - v, err := NewValue(` -import ( - "vela/custom" -) - -id: custom.context.stepSessionID - -`+cont, nil, cont) - r := require.New(t) - r.NoError(err) - id, err := v.GetString("id") - r.NoError(err) - r.Equal(id, "3w9qkdgn5w") -} - -func TestOpenCompleteValue(t *testing.T) { - v, err := NewValue(` -x: 10 -y: "100" -`, nil, "") - r := require.New(t) - r.NoError(err) - err = v.OpenCompleteValue() - r.NoError(err) - s, err := v.String() - r.NoError(err) - r.Equal(s, `x: *10 | _ -y: *"100" | _ -`) -} - -func TestFillByScript(t *testing.T) { - testCases := []struct { - name string - raw string - path string - v string - expected string - }{ - { - name: "insert array", - raw: `a: ["hello"]`, - path: "a[1]", - v: `"world"`, - expected: `a: ["hello", "world", ...] -`}, - { - name: "insert array", - raw: `a: b: [{x: 100},...]`, - path: "a.b[1]", - v: `{name: "foo"}`, - expected: `a: { - b: [{ - x: 100 - }, { - name: "foo" - }, ...] -} -`}, - { - name: "insert array to array", - raw: ` -a: b: c: [{x: 100}, {x: 101}, {x: 102}]`, - path: "a.b.c[0].value", - v: `"foo"`, - expected: `a: { - b: { - c: [{ - x: 100 - value: "foo" - }, { - x: 101 - }, { - x: 102 - }, ...] - } -} -`, - }, - { - name: "insert nest array ", - raw: `a: b: [{x: y:[{name: "key"}]}]`, - path: "a.b[0].x.y[0].value", - v: `"foo"`, - expected: `a: { - b: [{ - x: { - y: [{ - name: "key" - value: "foo" - }, ...] - } - }, ...] -} -`, - }, - { - name: "insert without array", - raw: `a: b: [{x: y:[{name: "key"}]}]`, - path: "a.c.x", - v: `"foo"`, - expected: `a: { - b: [{ - x: { - y: [{ - name: "key" - }, ...] - } - }, ...] - c: { - x: "foo" - } -} -`, - }, - { - name: "path with string index", - raw: `a: b: [{x: y:[{name: "key"}]}]`, - path: "a.c[\"x\"]", - v: `"foo"`, - expected: `a: { - b: [{ - x: { - y: [{ - name: "key" - }, ...] - } - }, ...] - c: { - x: "foo" - } -} -`, - }, - } - - for _, tCase := range testCases { - r := require.New(t) - v, err := NewValue(tCase.raw, nil, "") - r.NoError(err) - val, err := v.MakeValue(tCase.v) - r.NoError(err) - err = v.FillValueByScript(val, tCase.path) - r.NoError(err) - s, err := v.String() - r.NoError(err) - r.Equal(s, tCase.expected, tCase.name) - } - - errCases := []struct { - name string - raw string - path string - v string - err string - }{ - { - name: "invalid path", - raw: `a: b: [{x: 100},...]`, - path: "a.b[1]+1", - v: `{name: "foo"}`, - err: "invalid path: invalid label a.b[1]+1 ", - }, - { - name: "invalid path [float]", - raw: `a: b: [{x: 100},...]`, - path: "a.b[0.1]", - v: `{name: "foo"}`, - err: "invalid path: invalid literal 0.1", - }, - { - name: "conflict merge", - 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 \"foo\" and \"key\"", - }, - } - - for _, errCase := range errCases { - r := require.New(t) - v, err := NewValue(errCase.raw, nil, "") - r.NoError(err) - errV, err := v.MakeValue(errCase.v) - r.NoError(err) - err = v.FillValueByScript(errV, errCase.path) - r.Equal(errCase.err, err.Error()) - } -} +// func TestValueFix(t *testing.T) { +// testCases := []struct { +// original string +// expected string +// }{ +// { +// original: ` +// parameter: test: _ +// // comment +// y: { +// for k, v in parameter.test.p { +// "\(k)": v +// } +// }`, +// expected: `{ +// parameter: { +// test: _ +// } +// // comment +// y: { +// for k, v in *parameter.test.p | {} { +// "\(k)": v +// } +// } +// }`, +// }, +// } +// for i, tc := range testCases { +// t.Run(fmt.Sprint(i), func(t *testing.T) { +// r := require.New(t) +// v, err := NewValue(tc.original, nil, "") +// r.NoError(err) +// b, err := format.Node(v.CueValue().Syntax(cue.Docs(true))) +// r.NoError(err) +// r.Equal(tc.expected, string(b)) +// }) +// } +// } func TestSetByScript(t *testing.T) { testCases := []struct { @@ -1163,43 +280,13 @@ a: b: c: [{x: 100}, {x: 101}, {x: 102}]`, for _, tCase := range testCases { r := require.New(t) - v, err := NewValue(tCase.raw, nil, "") - r.NoError(err) - val, err := v.MakeValue(tCase.v) - r.NoError(err) - err = v.SetValueByScript(val, tCase.path) + cuectx := cuecontext.New() + base := cuectx.CompileString(tCase.raw) + new := cuectx.CompileString(tCase.v) + res, err := SetValueByScript(base, new, tCase.path) r.NoError(err, tCase.name) - s, err := v.String() + s, err := util.ToString(res) r.NoError(err) r.Equal(s, tCase.expected, tCase.name) } } - -func TestSubstituteInStruct(t *testing.T) { - base := ` -value: { - a: 1 -} -` - r := require.New(t) - val, err := NewValue(base, nil, "") - r.NoError(err) - expr, err := cuejson.Unmarshal([]byte(`{"b": 2}`)) - r.NoError(err) - err = val.SubstituteInStruct(expr, "value") - r.NoError(err) - s, err := val.String() - r.NoError(err) - r.Equal(s, `value: { - b: 2 -} -`) - err = val.SubstituteInStruct(expr, "notfound") - r.Error(err) - - errBase := `1` - val1, err := NewValue(errBase, nil, "") - r.NoError(err) - err = val1.SubstituteInStruct(expr, "value") - r.Error(err) -} diff --git a/pkg/cue/package_suit_test.go b/pkg/cue/package_suit_test.go deleted file mode 100644 index c43842c..0000000 --- a/pkg/cue/package_suit_test.go +++ /dev/null @@ -1,675 +0,0 @@ -/* -Copyright 2022 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" - "errors" - "strings" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "cuelang.org/go/cue/build" - "github.com/google/go-cmp/cmp" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - certificatesv1beta1 "k8s.io/api/certificates/v1beta1" - coordinationv1 "k8s.io/api/coordination/v1" - corev1 "k8s.io/api/core/v1" - discoveryv1beta1 "k8s.io/api/discovery/v1beta1" - networkingv1 "k8s.io/api/networking/v1" - policyv1beta1 "k8s.io/api/policy/v1beta1" - rbacv1 "k8s.io/api/rbac/v1" - crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/utils/pointer" - - "github.com/kubevela/workflow/pkg/cue/model" - "github.com/kubevela/workflow/pkg/utils" -) - -var _ = Describe("Package discovery resources for definition from K8s APIServer", func() { - PIt("check that all built-in k8s resource are registered", func() { - var localSchemeBuilder = runtime.SchemeBuilder{ - admissionregistrationv1.AddToScheme, - appsv1.AddToScheme, - batchv1.AddToScheme, - certificatesv1beta1.AddToScheme, - coordinationv1.AddToScheme, - corev1.AddToScheme, - discoveryv1beta1.AddToScheme, - networkingv1.AddToScheme, - policyv1beta1.AddToScheme, - rbacv1.AddToScheme, - } - - var localScheme = runtime.NewScheme() - err := localSchemeBuilder.AddToScheme(localScheme) - Expect(err).Should(BeNil()) - types := localScheme.AllKnownTypes() - for typ := range types { - if strings.HasSuffix(typ.Kind, "List") { - continue - } - if strings.HasSuffix(typ.Kind, "Options") { - continue - } - switch typ.Kind { - case "WatchEvent": - continue - case "APIGroup", "APIVersions": - continue - case "RangeAllocation", "ComponentStatus", "Status": - continue - case "SerializedReference", "EndpointSlice": - continue - case "PodStatusResult", "EphemeralContainers": - continue - } - - Expect(pd.Exist(metav1.GroupVersionKind{ - Group: typ.Group, - Version: typ.Version, - Kind: typ.Kind, - })).Should(BeTrue(), typ.String()) - } - }) - - // nolint:staticcheck - PIt("discovery built-in k8s resource with kube prefix", func() { - - By("test ingress in kube package") - bi := build.NewContext().NewInstance("", nil) - err := bi.AddFile("-", ` -import ( - kube "kube/networking.k8s.io/v1beta1" -) -output: kube.#Ingress -output: { - apiVersion: "networking.k8s.io/v1beta1" - kind: "Ingress" - metadata: name: "myapp" - spec: { - rules: [{ - host: parameter.domain - http: { - paths: [ - for k, v in parameter.http { - path: k - backend: { - serviceName: "myname" - servicePort: v - } - }, - ] - } - }] - } -} -parameter: { - domain: "abc.com" - http: { - "/": 80 - } -}`) - Expect(err).ToNot(HaveOccurred()) - inst, err := pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err := model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err := base.Unstructured() - Expect(err).Should(BeNil()) - - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Ingress", - "apiVersion": "networking.k8s.io/v1beta1", - "metadata": map[string]interface{}{"name": "myapp"}, - "spec": map[string]interface{}{ - "rules": []interface{}{ - map[string]interface{}{ - "host": "abc.com", - "http": map[string]interface{}{ - "paths": []interface{}{ - map[string]interface{}{ - "path": "/", - "backend": map[string]interface{}{ - "serviceName": "myname", - "servicePort": int64(80), - }}}}}}}}, - })).Should(BeEquivalentTo("")) - By("test Invalid Import path") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - kube "kube/networking.k8s.io/v1" -) -output: kube.#Deployment -output: { - metadata: { - "name": parameter.name - } - spec: template: spec: { - containers: [{ - name:"invalid-path", - image: parameter.image - }] - } -} - -parameter: { - name: "myapp" - image: "nginx" -}`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - _, err = model.NewBase(inst.Lookup("output")) - Expect(err).ShouldNot(BeNil()) - Expect(err.Error()).Should(Equal("_|_ // undefined field \"#Deployment\"")) - - By("test Deployment in kube package") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - kube "kube/apps/v1" -) -output: kube.#Deployment -output: { - metadata: { - "name": parameter.name - } - spec: template: spec: { - containers: [{ - name:"test", - image: parameter.image - }] - } -} -parameter: { - name: "myapp" - image: "nginx" -}`) - Expect(err).ShouldNot(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Deployment", - "apiVersion": "apps/v1", - "metadata": map[string]interface{}{"name": "myapp"}, - "spec": map[string]interface{}{ - "selector": map[string]interface{}{}, - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "test", - "image": "nginx"}}}}}}, - })).Should(BeEquivalentTo("")) - - By("test Secret in kube package") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - kube "kube/v1" -) -output: kube.#Secret -output: { - metadata: { - "name": parameter.name - } - type:"kubevela" -} -parameter: { - name: "myapp" -}`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Secret", - "apiVersion": "v1", - "metadata": map[string]interface{}{"name": "myapp"}, - "type": "kubevela"}})).Should(BeEquivalentTo("")) - - By("test Service in kube package") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - kube "kube/v1" -) -output: kube.#Service -output: { - metadata: { - "name": parameter.name - } - spec: type: "ClusterIP", -} -parameter: { - name: "myapp" -}`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Service", - "apiVersion": "v1", - "metadata": map[string]interface{}{"name": "myapp"}, - "spec": map[string]interface{}{ - "type": "ClusterIP"}}, - })).Should(BeEquivalentTo("")) - Expect(pd.Exist(metav1.GroupVersionKind{ - Group: "", - Version: "v1", - Kind: "Service", - })).Should(Equal(true)) - - By("Check newly added CRD refreshed and could be used in CUE package") - crd1 := crdv1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo.example.com", - }, - Spec: crdv1.CustomResourceDefinitionSpec{ - Group: "example.com", - Names: crdv1.CustomResourceDefinitionNames{ - Kind: "Foo", - ListKind: "FooList", - Plural: "foo", - Singular: "foo", - }, - Versions: []crdv1.CustomResourceDefinitionVersion{{ - Name: "v1", - Served: true, - Storage: true, - Subresources: &crdv1.CustomResourceSubresources{Status: &crdv1.CustomResourceSubresourceStatus{}}, - Schema: &crdv1.CustomResourceValidation{ - OpenAPIV3Schema: &crdv1.JSONSchemaProps{ - Type: "object", - Properties: map[string]crdv1.JSONSchemaProps{ - "spec": { - Type: "object", - XPreserveUnknownFields: pointer.BoolPtr(true), - Properties: map[string]crdv1.JSONSchemaProps{ - "key": {Type: "string"}, - }}, - "status": { - Type: "object", - XPreserveUnknownFields: pointer.BoolPtr(true), - Properties: map[string]crdv1.JSONSchemaProps{ - "key": {Type: "string"}, - "app-hash": {Type: "string"}, - }}}}}}, - }, - Scope: crdv1.NamespaceScoped, - }, - } - Expect(k8sClient.Create(context.Background(), &crd1)).Should(SatisfyAny(BeNil(), &utils.AlreadyExistMatcher{})) - - Expect(pd.Exist(metav1.GroupVersionKind{ - Group: "example.com", - Version: "v1", - Kind: "Foo", - })).Should(Equal(false)) - - By("test new added CRD in kube package") - Eventually(func() error { - if err := pd.RefreshKubePackagesFromCluster(); err != nil { - return err - } - if !pd.Exist(metav1.GroupVersionKind{ - Group: "example.com", - Version: "v1", - Kind: "Foo", - }) { - return errors.New("crd(example.com/v1.Foo) not register to openAPI") - } - return nil - }, time.Second*30, time.Millisecond*300).Should(BeNil()) - - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - kv1 "kube/example.com/v1" -) -output: kv1.#Foo -output: { - spec: key: "test1" - status: key: "test2" -} -`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Foo", - "apiVersion": "example.com/v1", - "spec": map[string]interface{}{ - "key": "test1"}, - "status": map[string]interface{}{ - "key": "test2"}}, - })).Should(BeEquivalentTo("")) - - }) - - // nolint:staticcheck - PIt("discovery built-in k8s resource with third-party path", func() { - - By("test ingress in kube package") - bi := build.NewContext().NewInstance("", nil) - err := bi.AddFile("-", ` -import ( - network "k8s.io/networking/v1beta1" -) -output: network.#Ingress -output: { - metadata: name: "myapp" - spec: { - rules: [{ - host: parameter.domain - http: { - paths: [ - for k, v in parameter.http { - path: k - backend: { - serviceName: "myname" - servicePort: v - } - }, - ] - } - }] - } -} -parameter: { - domain: "abc.com" - http: { - "/": 80 - } -}`) - Expect(err).ToNot(HaveOccurred()) - inst, err := pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err := model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err := base.Unstructured() - Expect(err).Should(BeNil()) - - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Ingress", - "apiVersion": "networking.k8s.io/v1beta1", - "metadata": map[string]interface{}{"name": "myapp"}, - "spec": map[string]interface{}{ - "rules": []interface{}{ - map[string]interface{}{ - "host": "abc.com", - "http": map[string]interface{}{ - "paths": []interface{}{ - map[string]interface{}{ - "path": "/", - "backend": map[string]interface{}{ - "serviceName": "myname", - "servicePort": int64(80), - }}}}}}}}, - })).Should(BeEquivalentTo("")) - By("test Invalid Import path") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - "k8s.io/networking/v1" -) -output: v1.#Deployment -output: { - metadata: { - "name": parameter.name - } - spec: template: spec: { - containers: [{ - name:"invalid-path", - image: parameter.image - }] - } -} - -parameter: { - name: "myapp" - image: "nginx" -}`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - _, err = model.NewBase(inst.Lookup("output")) - Expect(err).ShouldNot(BeNil()) - Expect(err.Error()).Should(Equal("_|_ // undefined field \"#Deployment\"")) - - By("test Deployment in kube package") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - apps "k8s.io/apps/v1" -) -output: apps.#Deployment -output: { - metadata: { - "name": parameter.name - } - spec: template: spec: { - containers: [{ - name:"test", - image: parameter.image - }] - } -} -parameter: { - name: "myapp" - image: "nginx" -}`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Deployment", - "apiVersion": "apps/v1", - "metadata": map[string]interface{}{"name": "myapp"}, - "spec": map[string]interface{}{ - "selector": map[string]interface{}{}, - "template": map[string]interface{}{ - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "test", - "image": "nginx"}}}}}}, - })).Should(BeEquivalentTo("")) - - By("test Secret in kube package") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - "k8s.io/core/v1" -) -output: v1.#Secret -output: { - metadata: { - "name": parameter.name - } - type:"kubevela" -} -parameter: { - name: "myapp" -}`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Secret", - "apiVersion": "v1", - "metadata": map[string]interface{}{"name": "myapp"}, - "type": "kubevela"}})).Should(BeEquivalentTo("")) - - By("test Service in kube package") - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - "k8s.io/core/v1" -) -output: v1.#Service -output: { - metadata: { - "name": parameter.name - } - spec: type: "ClusterIP", -} -parameter: { - name: "myapp" -}`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Service", - "apiVersion": "v1", - "metadata": map[string]interface{}{"name": "myapp"}, - "spec": map[string]interface{}{ - "type": "ClusterIP"}}, - })).Should(BeEquivalentTo("")) - Expect(pd.Exist(metav1.GroupVersionKind{ - Group: "", - Version: "v1", - Kind: "Service", - })).Should(Equal(true)) - - By("Check newly added CRD refreshed and could be used in CUE package") - crd1 := crdv1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "bar.example.com", - }, - Spec: crdv1.CustomResourceDefinitionSpec{ - Group: "example.com", - Names: crdv1.CustomResourceDefinitionNames{ - Kind: "Bar", - ListKind: "BarList", - Plural: "bar", - Singular: "bar", - }, - Versions: []crdv1.CustomResourceDefinitionVersion{{ - Name: "v1", - Served: true, - Storage: true, - Subresources: &crdv1.CustomResourceSubresources{Status: &crdv1.CustomResourceSubresourceStatus{}}, - Schema: &crdv1.CustomResourceValidation{ - OpenAPIV3Schema: &crdv1.JSONSchemaProps{ - Type: "object", - Properties: map[string]crdv1.JSONSchemaProps{ - "spec": { - Type: "object", - XPreserveUnknownFields: pointer.BoolPtr(true), - Properties: map[string]crdv1.JSONSchemaProps{ - "key": {Type: "string"}, - }}, - "status": { - Type: "object", - XPreserveUnknownFields: pointer.BoolPtr(true), - Properties: map[string]crdv1.JSONSchemaProps{ - "key": {Type: "string"}, - "app-hash": {Type: "string"}, - }}}}}}, - }, - Scope: crdv1.NamespaceScoped, - }, - } - Expect(k8sClient.Create(context.Background(), &crd1)).Should(SatisfyAny(BeNil(), &utils.AlreadyExistMatcher{})) - - Expect(pd.Exist(metav1.GroupVersionKind{ - Group: "example.com", - Version: "v1", - Kind: "Bar", - })).Should(Equal(false)) - - By("test new added CRD in kube package") - Eventually(func() error { - if err := pd.RefreshKubePackagesFromCluster(); err != nil { - return err - } - if !pd.Exist(metav1.GroupVersionKind{ - Group: "example.com", - Version: "v1", - Kind: "Bar", - }) { - return errors.New("crd(example.com/v1.Bar) not register to openAPI") - } - return nil - }, time.Second*30, time.Millisecond*300).Should(BeNil()) - - bi = build.NewContext().NewInstance("", nil) - err = bi.AddFile("-", ` -import ( - ev1 "example.com/v1" -) -output: ev1.#Bar -output: { - spec: key: "test1" - status: key: "test2" -} -`) - Expect(err).Should(BeNil()) - inst, err = pd.ImportPackagesAndBuildInstance(bi) - Expect(err).Should(BeNil()) - base, err = model.NewBase(inst.Lookup("output")) - Expect(err).Should(BeNil()) - data, err = base.Unstructured() - Expect(err).Should(BeNil()) - Expect(cmp.Diff(data, &unstructured.Unstructured{Object: map[string]interface{}{ - "kind": "Bar", - "apiVersion": "example.com/v1", - "spec": map[string]interface{}{ - "key": "test1"}, - "status": map[string]interface{}{ - "key": "test2"}}, - })).Should(BeEquivalentTo("")) - }) -}) diff --git a/pkg/cue/packages/package.go b/pkg/cue/packages/package.go deleted file mode 100644 index d121045..0000000 --- a/pkg/cue/packages/package.go +++ /dev/null @@ -1,465 +0,0 @@ -/* -Copyright 2022 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 packages - -import ( - "fmt" - "path/filepath" - "strings" - "sync" - "time" - - "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" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - "github.com/kubevela/workflow/pkg/stdlib" -) - -const ( - // BuiltinPackageDomain Specify the domain of the built-in package - BuiltinPackageDomain = "kube" - // K8sResourcePrefix Indicates that the definition comes from kubernetes - K8sResourcePrefix = "io_k8s_api_" - - // ParseJSONSchemaErr describes the error that occurs when cue parses json - ParseJSONSchemaErr ParseErrType = "parse json schema of k8s crds error" -) - -// PackageDiscover defines the inner CUE packages loaded from K8s cluster -type PackageDiscover struct { - velaBuiltinPackages []*build.Instance - pkgKinds map[string][]VersionKind - mutex sync.RWMutex - client *rest.RESTClient -} - -// VersionKind contains the resource metadata and reference name -type VersionKind struct { - DefinitionName string - APIVersion string - Kind string -} - -// ParseErrType represents the type of CUEParseError -type ParseErrType string - -// CUEParseError describes an error when CUE parse error -type CUEParseError struct { - err error - errType ParseErrType -} - -// Error implements the Error interface. -func (cueErr CUEParseError) Error() string { - return fmt.Sprintf("%s: %s", cueErr.errType, cueErr.err.Error()) -} - -// IsCUEParseErr returns true if the specified error is CUEParseError type. -func IsCUEParseErr(err error) bool { - return errors.As(err, &CUEParseError{}) -} - -// NewPackageDiscover will create a PackageDiscover client with the K8s config file. -func NewPackageDiscover(config *rest.Config) (*PackageDiscover, error) { - client, err := getClusterOpenAPIClient(config) - if err != nil { - return nil, err - } - pd := &PackageDiscover{ - client: client, - pkgKinds: make(map[string][]VersionKind), - } - if err = pd.RefreshKubePackagesFromCluster(); err != nil { - return pd, err - } - return pd, nil -} - -// ImportBuiltinPackagesFor will add KubeVela built-in packages into your CUE instance -func (pd *PackageDiscover) ImportBuiltinPackagesFor(bi *build.Instance) { - pd.mutex.RLock() - defer pd.mutex.RUnlock() - bi.Imports = append(bi.Imports, pd.velaBuiltinPackages...) -} - -// 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 { - return r.Build(bi) - } - pd.ImportBuiltinPackagesFor(bi) - if err := stdlib.AddImportsFor(bi, ""); err != nil { - return nil, err - } - pd.mutex.Lock() - defer pd.mutex.Unlock() - 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() - defer pd.mutex.RUnlock() - return pd.pkgKinds -} - -// RefreshKubePackagesFromCluster will use K8s client to load/refresh all K8s open API as a reference kube package using in template -func (pd *PackageDiscover) RefreshKubePackagesFromCluster() error { - return nil - // body, err := pd.client.Get().AbsPath("/openapi/v2").Do(context.Background()).Raw() - // if err != nil { - // return err - // } - // return pd.addKubeCUEPackagesFromCluster(string(body)) -} - -// Exist checks if the GVK exists in the built-in packages -func (pd *PackageDiscover) Exist(gvk metav1.GroupVersionKind) bool { - dgvk := convert2DGVK(gvk) - // package name equals to importPath - importPath := genStandardPkgName(dgvk) - pd.mutex.RLock() - defer pd.mutex.RUnlock() - pkgKinds, ok := pd.pkgKinds[importPath] - if !ok { - pkgKinds = pd.pkgKinds[genOpenPkgName(dgvk)] - } - for _, v := range pkgKinds { - if v.Kind == dgvk.Kind { - return true - } - } - return false -} - -// mount will mount the new parsed package into PackageDiscover built-in packages -func (pd *PackageDiscover) mount(pkg *pkgInstance, pkgKinds []VersionKind) { - pd.mutex.Lock() - defer pd.mutex.Unlock() - if pkgKinds == nil { - pkgKinds = []VersionKind{} - } - for i, p := range pd.velaBuiltinPackages { - if p.ImportPath == pkg.ImportPath { - pd.pkgKinds[pkg.ImportPath] = pkgKinds - pd.velaBuiltinPackages[i] = pkg.Instance - return - } - } - pd.pkgKinds[pkg.ImportPath] = pkgKinds - pd.velaBuiltinPackages = append(pd.velaBuiltinPackages, pkg.Instance) -} - -func (pd *PackageDiscover) pkgBuild(packages map[string]*pkgInstance, pkgName string, - dGVK domainGroupVersionKind, def string, kubePkg *pkgInstance, groupKinds map[string][]VersionKind) error { - pkg, ok := packages[pkgName] - if !ok { - pkg = newPackage(pkgName) - pkg.Imports = []*build.Instance{kubePkg.Instance} - } - - mykinds := groupKinds[pkgName] - mykinds = append(mykinds, VersionKind{ - APIVersion: dGVK.APIVersion, - Kind: dGVK.Kind, - DefinitionName: "#" + dGVK.Kind, - }) - - file, err := parser.ParseFile(dGVK.reverseString(), def) - if err != nil { - return err - } - if err := pkg.AddSyntax(file); err != nil { - return err - } - - packages[pkgName] = pkg - groupKinds[pkgName] = mykinds - return nil -} - -func (pd *PackageDiscover) addKubeCUEPackagesFromCluster(apiSchema string) error { - 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.LookupPath(cue.ParsePath("paths")) - if pathValue.Exists() { - iter, err := pathValue.Fields() - if err != nil { - return err - } - for iter.Next() { - 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 - } - } - } - } - oaFile, err := jsonschema.Extract(oaInst, &jsonschema.Config{ - Root: "#/definitions", - Map: openAPIMapping(dgvkMapper), - }) - if err != nil { - return CUEParseError{ - err: err, - errType: ParseJSONSchemaErr, - } - } - kubePkg := newPackage("kube") - kubePkg.processOpenAPIFile(oaFile) - if err := kubePkg.AddSyntax(oaFile); err != nil { - return err - } - packages := make(map[string]*pkgInstance) - groupKinds := make(map[string][]VersionKind) - - for k := range dgvkMapper { - v := dgvkMapper[k] - apiVersion := v.APIVersion - def := fmt.Sprintf(` -import "kube" - -#%s: kube.%s & { -kind: "%s" -apiVersion: "%s", -}`, v.Kind, k, v.Kind, apiVersion) - - if err := pd.pkgBuild(packages, genStandardPkgName(v), v, def, kubePkg, groupKinds); err != nil { - return err - } - if err := pd.pkgBuild(packages, genOpenPkgName(v), v, def, kubePkg, groupKinds); err != nil { - return err - } - } - for name, pkg := range packages { - pd.mount(pkg, groupKinds[name]) - } - return nil -} - -func genOpenPkgName(v domainGroupVersionKind) string { - return BuiltinPackageDomain + "/" + v.APIVersion -} - -func genStandardPkgName(v domainGroupVersionKind) string { - res := []string{v.Group, v.Version} - if v.Domain != "" { - res = []string{v.Domain, v.Group, v.Version} - } - - return strings.Join(res, "/") -} - -func setDiscoveryDefaults(config *rest.Config) { - config.APIPath = "" - config.GroupVersion = nil - if config.Timeout == 0 { - config.Timeout = 32 * time.Second - } - if config.Burst == 0 && config.QPS < 100 { - // discovery is expected to be bursty, increase the default burst - // to accommodate looking up resource info for many API groups. - // matches burst set by ConfigFlags#ToDiscoveryClient(). - // see https://issue.k8s.io/86149 - config.Burst = 100 - } - codec := runtime.NoopEncoder{Decoder: clientgoscheme.Codecs.UniversalDecoder()} - config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec}) - if len(config.UserAgent) == 0 { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } -} - -func getClusterOpenAPIClient(config *rest.Config) (*rest.RESTClient, error) { - copyConfig := *config - setDiscoveryDefaults(©Config) - return rest.UnversionedRESTClientFor(©Config) -} - -func openAPIMapping(dgvkMapper map[string]domainGroupVersionKind) func(pos token.Pos, a []string) ([]ast.Label, error) { - return func(pos token.Pos, a []string) ([]ast.Label, error) { - if len(a) < 2 { - return nil, errors.New("openAPIMapping format invalid") - } - - name := strings.ReplaceAll(a[1], ".", "_") - name = strings.ReplaceAll(name, "-", "_") - if _, ok := dgvkMapper[name]; !ok && strings.HasPrefix(name, K8sResourcePrefix) { - trimName := strings.TrimPrefix(name, K8sResourcePrefix) - if v, ok := dgvkMapper[trimName]; ok { - v.Domain = "k8s.io" - dgvkMapper[name] = v - delete(dgvkMapper, trimName) - } - } - - if strings.HasSuffix(a[1], ".JSONSchemaProps") && pos != token.NoPos { - return []ast.Label{ast.NewIdent("_")}, nil - } - - return []ast.Label{ast.NewIdent(name)}, nil - } - -} - -type domainGroupVersionKind struct { - Domain string - Group string - Version string - Kind string - APIVersion string -} - -func (dgvk domainGroupVersionKind) reverseString() string { - var s = []string{dgvk.Kind, dgvk.Version} - s = append(s, strings.Split(dgvk.Group, ".")...) - domain := dgvk.Domain - if domain == "k8s.io" { - domain = "api.k8s.io" - } - - if domain != "" { - s = append(s, strings.Split(domain, ".")...) - } - - for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return strings.ReplaceAll(strings.Join(s, "_"), "-", "_") -} - -type pkgInstance struct { - *build.Instance -} - -func newPackage(name string) *pkgInstance { - return &pkgInstance{ - &build.Instance{ - PkgName: filepath.Base(name), - ImportPath: name, - }, - } -} - -func (pkg *pkgInstance) processOpenAPIFile(f *ast.File) { - ast.Walk(f, func(node ast.Node) bool { - if st, ok := node.(*ast.StructLit); ok { - hasEllipsis := false - for index, elt := range st.Elts { - if _, isEllipsis := elt.(*ast.Ellipsis); isEllipsis { - if hasEllipsis { - st.Elts = st.Elts[:index] - return true - } - if index > 0 { - st.Elts = st.Elts[:index] - return true - } - hasEllipsis = true - } - } - } - return true - }, nil) - - for _, decl := range f.Decls { - if field, ok := decl.(*ast.Field); ok { - if val, ok := field.Value.(*ast.Ident); ok && val.Name == "string" { - field.Value = ast.NewBinExpr(token.OR, ast.NewIdent("int"), ast.NewIdent("string")) - } - } - } -} - -func getDGVK(v cue.Value) (ret domainGroupVersionKind, err error) { - gvk := metav1.GroupVersionKind{} - gvk.Group, err = v.LookupPath(cue.ParsePath("group")).String() - if err != nil { - return - } - gvk.Version, err = v.LookupPath(cue.ParsePath("version")).String() - if err != nil { - return - } - - gvk.Kind, err = v.LookupPath(cue.ParsePath("kind")).String() - if err != nil { - return - } - - ret = convert2DGVK(gvk) - return -} - -func convert2DGVK(gvk metav1.GroupVersionKind) domainGroupVersionKind { - ret := domainGroupVersionKind{ - Version: gvk.Version, - Kind: gvk.Kind, - APIVersion: gvk.Version, - } - if gvk.Group == "" { - ret.Group = "core" - ret.Domain = "k8s.io" - } else { - ret.APIVersion = gvk.Group + "/" + ret.APIVersion - sv := strings.Split(gvk.Group, ".") - // Domain must contain dot - if len(sv) > 2 { - ret.Domain = strings.Join(sv[1:], ".") - ret.Group = sv[0] - } else { - ret.Group = gvk.Group - } - } - return ret -} diff --git a/pkg/cue/packages/package_test.go b/pkg/cue/packages/package_test.go deleted file mode 100644 index 8824244..0000000 --- a/pkg/cue/packages/package_test.go +++ /dev/null @@ -1,639 +0,0 @@ -/* -Copyright 2022 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 packages - -import ( - "fmt" - "testing" - - "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" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/kubevela/workflow/pkg/cue/model" -) - -func TestPackage(t *testing.T) { - var openAPISchema = ` -{ - "paths": { - "paths...": { - "post":{ - "x-kubernetes-group-version-kind": { - "group": "apps.test.io", - "kind": "Bucket", - "version": "v1" - } - } - } - }, - "definitions":{ - "io.test.apps.v1.Bucket":{ - "properties":{ - "apiVersion": {"type": "string"} - "kind": {"type": "string"} - "acl":{ - "default":"private", - "enum":[ - "public-read-write", - "public-read", - "private" - ], - "type":"string" - }, - "dataRedundancyType":{ - "default":"LRS", - "enum":[ - "LRS", - "ZRS" - ], - "type":"string" - }, - "dataSourceRef":{ - "properties":{ - "dsPath":{ - "type":"string" - } - }, - "required":[ - "dsPath" - ], - "type":"object" - }, - "importRef":{ - "properties":{ - "importKey":{ - "type":"string" - } - }, - "required":[ - "importKey" - ], - "type":"object" - }, - "output":{ - "additionalProperties":{ - "oneOf":[ - { - "properties":{ - "outRef":{ - "type":"string" - } - }, - "required":[ - "outRef" - ] - }, - { - "properties":{ - "valueRef":{ - "description":"Example: demoVpc.vpcId", - "type":"string" - } - }, - "required":[ - "valueRef" - ] - } - ], - "type":"object" - }, - "properties":{ - "bucketName":{ - "properties":{ - "outRef":{ - "enum":[ - "self.name" - ], - "type":"string" - } - }, - "required":[ - "outRef" - ], - "type":"object" - }, - "extranetEndpoint":{ - "properties":{ - "outRef":{ - "enum":[ - "self.state.extranetEndpoint" - ], - "type":"string" - } - }, - "required":[ - "outRef" - ], - "type":"object" - }, - "intranetEndpoint":{ - "properties":{ - "outRef":{ - "enum":[ - "self.state.intranetEndpoint" - ], - "type":"string" - } - }, - "required":[ - "outRef" - ], - "type":"object" - }, - "masterUserId":{ - "properties":{ - "outRef":{ - "enum":[ - "self.state.masterUserId" - ], - "type":"string" - } - }, - "required":[ - "outRef" - ], - "type":"object" - } - }, - "required":[ - "bucketName", - "extranetEndpoint", - "intranetEndpoint", - "masterUserId" - ], - "type":"object" - }, - "profile":{ - "properties":{ - "baasRepo":{ - "oneOf":[ - { - "type":"string" - }, - { - "properties":{ - "valueRef":{ - "description":"Example: demoVpc.vpcId", - "type":"string" - } - }, - "required":[ - "valueRef" - ], - "type":"object" - } - ] - }, - "cloudProduct":{ - "enum":[ - "AliCloudOSS" - ], - "type":"string" - }, - "endpoint":{ - "oneOf":[ - { - "type":"string" - }, - { - "properties":{ - "valueRef":{ - "description":"Example: demoVpc.vpcId", - "type":"string" - } - }, - "required":[ - "valueRef" - ], - "type":"object" - } - ] - }, - "envType":{ - "oneOf":[ - { - "enum":[ - "testing", - "product" - ] - }, - { - "properties":{ - "valueRef":{ - "description":"Example: demoVpc.vpcId", - "type":"string" - } - }, - "required":[ - "valueRef" - ], - "type":"object" - } - ] - }, - "provider":{ - "enum":[ - "alicloud" - ], - "type":"string" - }, - "region":{ - "oneOf":[ - { - "type":"string" - }, - { - "properties":{ - "valueRef":{ - "description":"Example: demoVpc.vpcId", - "type":"string" - } - }, - "required":[ - "valueRef" - ], - "type":"object" - } - ] - }, - "serviceAccount":{ - "oneOf":[ - { - "type":"string" - }, - { - "properties":{ - "valueRef":{ - "description":"Example: demoVpc.vpcId", - "type":"string" - } - }, - "required":[ - "valueRef" - ], - "type":"object" - } - ] - } - }, - "required":[ - "cloudProduct", - "provider", - "baasRepo", - "region" - ], - "type":"object" - }, - "storageClass":{ - "default":"Standard", - "enum":[ - "Standard", - "IA", - "Archive", - "ColdArchive" - ], - "type":"string" - }, - "type":{ - "enum":[ - "alicloud_oss_bucket" - ], - "type":"string" - } - }, - "required":[ - "type", - "output", - "profile", - "acl" - ], - "type":"object" - } - } -} -` - r := require.New(t) - mypd := &PackageDiscover{pkgKinds: make(map[string][]VersionKind)} - err := mypd.addKubeCUEPackagesFromCluster(openAPISchema) - r.NoError(err) - expectPkgKinds := map[string][]VersionKind{ - "test.io/apps/v1": []VersionKind{{ - DefinitionName: "#Bucket", - APIVersion: "apps.test.io/v1", - Kind: "Bucket", - }}, - "kube/apps.test.io/v1": []VersionKind{{ - DefinitionName: "#Bucket", - APIVersion: "apps.test.io/v1", - Kind: "Bucket", - }}, - } - r.Equal(mypd.ListPackageKinds(), expectPkgKinds) - - // TODO: fix losing close struct in cue - exceptObj := `output: { - apiVersion: "apps.test.io/v1" - kind: "Bucket" - acl: *"private" | "public-read" | "public-read-write" - dataRedundancyType?: "LRS" | "ZRS" | *"LRS" - dataSourceRef?: { - dsPath: string - } - importRef?: { - importKey: string - } - output: { - bucketName: { - outRef: "self.name" - } - extranetEndpoint: { - outRef: "self.state.extranetEndpoint" - } - intranetEndpoint: { - outRef: "self.state.intranetEndpoint" - } - masterUserId: { - outRef: "self.state.masterUserId" - } - } - profile: { - baasRepo: string | { - // Example: demoVpc.vpcId - valueRef: string - } - cloudProduct: "AliCloudOSS" - endpoint?: string | { - // Example: demoVpc.vpcId - valueRef: string - } - envType?: "testing" | "product" | { - // Example: demoVpc.vpcId - valueRef: string - } - provider: "alicloud" - region: string | { - // Example: demoVpc.vpcId - valueRef: string - } - serviceAccount?: string | { - // Example: demoVpc.vpcId - valueRef: string - } - } - storageClass?: "Standard" | "IA" | "Archive" | "ColdArchive" | *"Standard" - type: "alicloud_oss_bucket" -} -` - bi := build.NewContext().NewInstance("", nil) - file, err := parser.ParseFile("-", ` -import "test.io/apps/v1" -output: v1.#Bucket -`) - r.NoError(err) - err = bi.AddSyntax(file) - r.NoError(err) - inst, err := mypd.ImportPackagesAndBuildValue(bi) - r.NoError(err) - base, err := model.NewBase(inst) - r.NoError(err) - s, err := base.String() - r.NoError(err) - r.Equal(s, exceptObj) - - bi = build.NewContext().NewInstance("", nil) - file, err = parser.ParseFile("-", ` -import "kube/apps.test.io/v1" -output: v1.#Bucket -`) - r.NoError(err) - err = bi.AddSyntax(file) - r.NoError(err) - inst, err = mypd.ImportPackagesAndBuildValue(bi) - r.NoError(err) - base, err = model.NewBase(inst) - r.NoError(err) - s, err = base.String() - r.NoError(err) - r.Equal(s, exceptObj) -} - -func TestProcessFile(t *testing.T) { - srcTmpl := ` -#Definition: { - kind?: string - apiVersion?: string - metadata: { - name: string - ... - } - ... -} -` - r := require.New(t) - file, err := parser.ParseFile("-", srcTmpl) - r.NoError(err) - testPkg := newPackage("foo") - testPkg.processOpenAPIFile(file) - cuectx := cuecontext.New() - inst := cuectx.BuildFile(file) - - testCasesInst := cuectx.CompileString(` -#Definition: {} -case1: #Definition & {additionalProperty: "test"} - -case2: #Definition & { - metadata: { - additionalProperty: "test" - } -} -`) - 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) { - mypd := &PackageDiscover{pkgKinds: make(map[string][]VersionKind)} - testPkg := newPackage("foo") - mypd.mount(testPkg, []VersionKind{}) - r := require.New(t) - r.Equal(len(mypd.velaBuiltinPackages), 1) - mypd.mount(testPkg, []VersionKind{}) - r.Equal(len(mypd.velaBuiltinPackages), 1) - r.Equal(mypd.velaBuiltinPackages[0], testPkg.Instance) -} - -func TestGetDGVK(t *testing.T) { - srcTmpl := ` -{ - "x-kubernetes-group-version-kind": { - "group": "apps.test.io", - "kind": "Foo", - "version": "v1" - } -} -` - 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", - Kind: "Foo", - APIVersion: "apps.test.io/v1", - }) - - srcTmpl = ` -{ - "x-kubernetes-group-version-kind": { - "group": "test.io", - "kind": "Foo", - "version": "v1" - } -} -` - 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", - APIVersion: "test.io/v1", - }) -} - -func TestOpenAPIMapping(t *testing.T) { - testCases := []struct { - input []string - pos token.Pos - result string - errMsg string - }{ - { - input: []string{"definitions", "io.k8s.api.discovery.v1beta1.Endpoint"}, - pos: token.NoPos, - result: "[io_k8s_api_discovery_v1beta1_Endpoint]", - }, - { - input: []string{"definitions", "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps"}, - pos: token.NoPos.Add(1), - result: "[_]", - }, - { - input: []string{"definitions", "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps"}, - pos: token.NoPos, - result: "[io_k8s_apiextensions_apiserver_pkg_apis_apiextensions_v1_JSONSchemaProps]", - }, - { - input: []string{"definitions"}, - pos: token.NoPos, - errMsg: "openAPIMapping format invalid", - }, - } - - emptyMapper := make(map[string]domainGroupVersionKind) - for _, tCase := range testCases { - r := require.New(t) - labels, err := openAPIMapping(emptyMapper)(tCase.pos, tCase.input) - if tCase.errMsg != "" { - r.Error(err, tCase.errMsg) - continue - } - r.NoError(err) - r.Equal(len(labels), 1) - r.Equal(tCase.result, fmt.Sprint(labels)) - } -} - -func TestGeneratePkgName(t *testing.T) { - testCases := []struct { - dgvk domainGroupVersionKind - sdPkgName string - openPkgName string - }{ - { - dgvk: domainGroupVersionKind{ - Domain: "k8s.io", - Group: "networking", - Version: "v1", - Kind: "Ingress", - }, - sdPkgName: "k8s.io/networking/v1", - openPkgName: "kube/networking.k8s.io", - }, - { - dgvk: domainGroupVersionKind{ - Group: "example.com", - Version: "v1", - Kind: "Sls", - }, - sdPkgName: "example.com/v1", - openPkgName: "kube/example.com/v1", - }, - } - - for _, tCase := range testCases { - r := require.New(t) - r.Equal(genStandardPkgName(tCase.dgvk), tCase.sdPkgName) - } -} - -func TestReverseString(t *testing.T) { - testCases := []struct { - gvr metav1.GroupVersionKind - reverseString string - }{ - { - gvr: metav1.GroupVersionKind{ - Group: "networking.k8s.io", - Version: "v1", - Kind: "NetworkPolicy", - }, - reverseString: "io_k8s_api_networking_v1_NetworkPolicy", - }, - { - gvr: metav1.GroupVersionKind{ - Group: "example.com", - Version: "v1", - Kind: "Sls", - }, - reverseString: "com_example_v1_Sls", - }, - { - gvr: metav1.GroupVersionKind{ - Version: "v1", - Kind: "Pod", - }, - reverseString: "io_k8s_api_core_v1_Pod", - }, - } - - for _, tCase := range testCases { - r := require.New(t) - r.Equal(convert2DGVK(tCase.gvr).reverseString(), tCase.reverseString) - } -} diff --git a/pkg/cue/suit_test.go b/pkg/cue/suit_test.go deleted file mode 100644 index ed6088a..0000000 --- a/pkg/cue/suit_test.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2022 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 ( - "testing" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "k8s.io/apimachinery/pkg/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - - "github.com/kubevela/workflow/pkg/cue/packages" -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment -var scheme = runtime.NewScheme() -var pd *packages.PackageDiscover - -func TestDefinition(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "Test Definition Suite") -} - -var _ = BeforeSuite(func(done Done) { - By("Bootstrapping test environment") - testEnv = &envtest.Environment{ - ControlPlaneStartTimeout: time.Minute, - ControlPlaneStopTimeout: time.Minute, - UseExistingCluster: pointer.BoolPtr(false), - } - var err error - cfg, err = testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - Expect(clientgoscheme.AddToScheme(scheme)).Should(BeNil()) - Expect(crdv1.AddToScheme(scheme)).Should(BeNil()) - // +kubebuilder:scaffold:scheme - By("Create the k8s client") - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) - pd, err = packages.NewPackageDiscover(cfg) - Expect(err).ToNot(HaveOccurred()) - - close(done) -}, 60) - -var _ = AfterSuite(func() { - By("Tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) diff --git a/pkg/cue/utils.go b/pkg/cue/utils.go deleted file mode 100644 index 6999bd1..0000000 --- a/pkg/cue/utils.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2022 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 ( - "bytes" - - "cuelang.org/go/pkg/encoding/json" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/kubevela/workflow/pkg/cue/model/value" -) - -// int data can evaluate with number in CUE, so it's OK if we convert the original float type data to int -func isIntegral(val float64) bool { - return val == float64(int(val)) -} - -// IntifyValues will make values to int. -// JSON marshalling of user values will put integer into float, -// we have to change it back so that CUE check will succeed. -func IntifyValues(raw interface{}) interface{} { - switch v := raw.(type) { - case map[string]interface{}: - return intifyMap(v) - case []interface{}: - return intifyList(v) - case float64: - if isIntegral(v) { - return int(v) - } - return v - default: - return raw - } -} - -func intifyList(l []interface{}) interface{} { - l2 := make([]interface{}, 0, len(l)) - for _, v := range l { - l2 = append(l2, IntifyValues(v)) - } - return l2 -} - -func intifyMap(m map[string]interface{}) interface{} { - m2 := make(map[string]interface{}, len(m)) - for k, v := range m { - m2[k] = IntifyValues(v) - } - return m2 -} - -// FillUnstructuredObject fill runtime.Unstructured to *value.Value -func FillUnstructuredObject(v *value.Value, obj runtime.Unstructured, paths ...string) error { - var buf bytes.Buffer - if err := unstructured.UnstructuredJSONScheme.Encode(obj, &buf); err != nil { - return v.FillObject(err.Error(), "err") - } - expr, err := json.Unmarshal(buf.Bytes()) - if err != nil { - return v.FillObject(err.Error(), "err") - } - return v.FillObject(expr, paths...) -} - -// SetUnstructuredObject set runtime.Unstructured to *value.Value -func SetUnstructuredObject(v *value.Value, obj runtime.Unstructured, path string) error { - var buf bytes.Buffer - if err := unstructured.UnstructuredJSONScheme.Encode(obj, &buf); err != nil { - return v.FillObject(err.Error(), "err") - } - expr, err := json.Unmarshal(buf.Bytes()) - if err != nil { - return v.FillObject(err.Error(), "err") - } - return v.SetObject(expr, path) -} diff --git a/pkg/cue/utils_test.go b/pkg/cue/utils_test.go deleted file mode 100644 index 4d755a2..0000000 --- a/pkg/cue/utils_test.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -Copyright 2022 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 ( - "testing" - - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/kubevela/workflow/pkg/cue/model/value" -) - -func TestIntifyValues(t *testing.T) { - testcases := map[string]struct { - input interface{} - output interface{} - }{ - "default case": { - input: "string", - output: "string", - }, - "float64": { - input: float64(1), - output: 1, - }, - "array": { - input: []interface{}{float64(1), float64(2)}, - output: []interface{}{1, 2}, - }, - "map": { - input: map[string]interface{}{"a": float64(1), "b": float64(2)}, - output: map[string]interface{}{"a": 1, "b": 2}, - }, - } - for name, testcase := range testcases { - t.Run(name, func(t *testing.T) { - r := require.New(t) - result := IntifyValues(testcase.input) - r.Equal(testcase.output, result) - }) - } -} - -func TestFillUnstructuredObject(t *testing.T) { - testcases := map[string]struct { - obj *unstructured.Unstructured - json string - }{ - "test unstructured object with nil value": { - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "spec": map[string]interface{}{ - "template": map[string]interface{}{ - "metadata": map[string]interface{}{ - "creationTimestamp": nil, - }, - }, - }, - }, - }, - json: `{"object":{"apiVersion":"apps/v1","kind":"Deployment","spec":{"template":{"metadata":{"creationTimestamp":null}}}}}`, - }, - "test unstructured object without nil value": { - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": map[string]interface{}{ - "creationTimestamp": "2022-05-25T12:07:02Z", - }, - }, - }, - json: `{"object":{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"creationTimestamp":"2022-05-25T12:07:02Z"}}}`, - }, - } - - for name, testcase := range testcases { - t.Run(name, func(t *testing.T) { - r := require.New(t) - value, err := value.NewValue("", nil, "") - r.NoError(err) - err = FillUnstructuredObject(value, testcase.obj, "object") - r.NoError(err) - json, err := value.CueValue().MarshalJSON() - r.NoError(err) - r.Equal(testcase.json, string(json)) - }) - } -} - -func TestSubstituteUnstructuredObject(t *testing.T) { - testcases := map[string]struct { - obj *unstructured.Unstructured - json string - }{ - "test unstructured object with nil value": { - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "spec": map[string]interface{}{ - "template": map[string]interface{}{ - "metadata": map[string]interface{}{ - "creationTimestamp": nil, - }, - }, - }, - }, - }, - json: `{"object":{"apiVersion":"apps/v1","kind":"Deployment","spec":{"template":{"metadata":{"creationTimestamp":null}}}}}`, - }, - "test unstructured object without nil value": { - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": map[string]interface{}{ - "creationTimestamp": "2022-05-25T12:07:02Z", - }, - }, - }, - json: `{"object":{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"creationTimestamp":"2022-05-25T12:07:02Z"}}}`, - }, - } - - for name, testcase := range testcases { - t.Run(name, func(t *testing.T) { - r := require.New(t) - value, err := value.NewValue(`object:{"test": "test"}`, nil, "") - r.NoError(err) - err = SetUnstructuredObject(value, testcase.obj, "object") - r.NoError(err) - json, err := value.CueValue().MarshalJSON() - r.NoError(err) - r.Equal(testcase.json, string(json)) - }) - } -} diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 5ca6d71..2c70a77 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -16,6 +16,8 @@ limitations under the License. package errors +import "fmt" + // ActionType is the type of action type ActionType string @@ -34,3 +36,11 @@ type GenericActionError ActionType func (e GenericActionError) Error() string { return "" } + +// LookUpNotFoundErr is the error type of lookup +type LookUpNotFoundErr string + +// Error . +func (e LookUpNotFoundErr) Error() string { + return fmt.Sprintf("failed to lookup value: var(path=%s) not exist", string(e)) +} diff --git a/pkg/executor/workflow.go b/pkg/executor/workflow.go index e4d4ca0..a6fe5b5 100644 --- a/pkg/executor/workflow.go +++ b/pkg/executor/workflow.go @@ -39,6 +39,7 @@ import ( "github.com/kubevela/workflow/pkg/hooks" "github.com/kubevela/workflow/pkg/monitor/metrics" "github.com/kubevela/workflow/pkg/providers" + "github.com/kubevela/workflow/pkg/providers/legacy/workspace" "github.com/kubevela/workflow/pkg/tasks/custom" "github.com/kubevela/workflow/pkg/types" ) @@ -71,17 +72,7 @@ func New(instance *types.WorkflowInstance, options ...Option) WorkflowExecutor { opt.ApplyTo(executor) } if executor.compiler == nil { - executor.compiler = providers.NewCompiler(nil) - // v, err := executor.compiler.CompileString(context.Background(), `import ( - // "vela/op" - // ) - // a: op.#Suspend & {}`) - // if err != nil { - // panic(err) - // } - // if v.Err() != nil { - // panic(v.Err()) - // } + executor.compiler = providers.Compiler.Get() } return executor } @@ -281,8 +272,7 @@ func handleSuspendBackoffTime(wfCtx wfContext.Context, step v1alpha1.WorkflowSte } } - //FIXME: - if ts := wfCtx.GetMutableValue(status.ID, "resumeTimeStamp"); ts != "" { + if ts := wfCtx.GetMutableValue(status.ID, workspace.ResumeTimeStamp); ts != "" { t, err := time.Parse(time.RFC3339, ts) if err != nil { return min diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index 96ee6f8..ee7bcd3 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -33,7 +33,6 @@ import ( "github.com/kubevela/workflow/pkg/executor" "github.com/kubevela/workflow/pkg/monitor/metrics" - // providertypes "github.com/kubevela/workflow/pkg/providers/types" "github.com/kubevela/workflow/pkg/tasks" "github.com/kubevela/workflow/pkg/tasks/template" "github.com/kubevela/workflow/pkg/types" diff --git a/pkg/providers/compiler.go b/pkg/providers/compiler.go index acdf5e5..b5e8bf3 100644 --- a/pkg/providers/compiler.go +++ b/pkg/providers/compiler.go @@ -20,9 +20,9 @@ import ( "github.com/kubevela/pkg/cue/cuex" cuexruntime "github.com/kubevela/pkg/cue/cuex/runtime" "github.com/kubevela/pkg/util/runtime" + "github.com/kubevela/pkg/util/singleton" "github.com/kubevela/workflow/pkg/providers/legacy" - providertypes "github.com/kubevela/workflow/pkg/providers/types" ) const ( @@ -30,15 +30,10 @@ const ( LegacyProviderName = "op" ) -var defaultCompiler *cuex.Compiler - -// NewCompiler create a cuex compiler -func NewCompiler(kubeHandlers *providertypes.KubeHandlers) *cuex.Compiler { - if defaultCompiler == nil { - defaultCompiler = cuex.NewCompilerWithInternalPackages( - // legacy packages - runtime.Must(cuexruntime.NewInternalPackage(LegacyProviderName, legacy.GetLegacyTemplate(), legacy.GetLegacyProviders(kubeHandlers))), - ) - } - return defaultCompiler -} +// Compiler is the workflow default compiler +var Compiler = singleton.NewSingletonE[*cuex.Compiler](func() (*cuex.Compiler, error) { + return cuex.NewCompilerWithInternalPackages( + // legacy packages + runtime.Must(cuexruntime.NewInternalPackage(LegacyProviderName, legacy.GetLegacyTemplate(), legacy.GetLegacyProviders())), + ), nil +}) diff --git a/pkg/providers/legacy/email/email.go b/pkg/providers/legacy/email/email.go index 9a2e027..eddac85 100644 --- a/pkg/providers/legacy/email/email.go +++ b/pkg/providers/legacy/email/email.go @@ -29,7 +29,6 @@ import ( "github.com/kubevela/workflow/pkg/cue/model" "github.com/kubevela/workflow/pkg/errors" providertypes "github.com/kubevela/workflow/pkg/providers/types" - "github.com/kubevela/workflow/pkg/types" ) const ( @@ -60,7 +59,7 @@ type MailVars struct { } // MailParams . -type MailParams = types.LegacyParams[MailVars] +type MailParams = providertypes.LegacyParams[MailVars] var emailRoutine sync.Map diff --git a/pkg/providers/legacy/email/email_test.go b/pkg/providers/legacy/email/email_test.go index 7c209ea..03fb866 100644 --- a/pkg/providers/legacy/email/email_test.go +++ b/pkg/providers/legacy/email/email_test.go @@ -31,7 +31,7 @@ import ( "github.com/kubevela/workflow/pkg/cue/model" "github.com/kubevela/workflow/pkg/cue/process" "github.com/kubevela/workflow/pkg/mock" - "github.com/kubevela/workflow/pkg/types" + providertypes "github.com/kubevela/workflow/pkg/providers/types" ) func TestSendEmail(t *testing.T) { @@ -100,7 +100,7 @@ func TestSendEmail(t *testing.T) { } _, err := Send(ctx, &MailParams{ Params: tc.vars, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ ProcessContext: pCtx, Action: act, }, @@ -116,7 +116,7 @@ func TestSendEmail(t *testing.T) { time.Sleep(time.Second) _, err = Send(ctx, &MailParams{ Params: tc.vars, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ ProcessContext: pCtx, Action: act, }, diff --git a/pkg/providers/legacy/http/http.go b/pkg/providers/legacy/http/http.go index 29719ee..2fbfd18 100644 --- a/pkg/providers/legacy/http/http.go +++ b/pkg/providers/legacy/http/http.go @@ -37,7 +37,6 @@ import ( "github.com/kubevela/workflow/pkg/providers/legacy/http/ratelimiter" providertypes "github.com/kubevela/workflow/pkg/providers/types" - "github.com/kubevela/workflow/pkg/types" ) const ( @@ -91,7 +90,7 @@ type ResponseVars struct { } // DoParams is the params for http request -type DoParams = types.LegacyParams[RequestVars] +type DoParams = providertypes.LegacyParams[RequestVars] // Do process http request. func Do(ctx context.Context, params *DoParams) (*ResponseVars, error) { diff --git a/pkg/providers/legacy/http/http_test.go b/pkg/providers/legacy/http/http_test.go index 30cbd75..a237e35 100644 --- a/pkg/providers/legacy/http/http_test.go +++ b/pkg/providers/legacy/http/http_test.go @@ -36,7 +36,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kubevela/pkg/util/singleton" - "github.com/kubevela/workflow/pkg/cue/model/value" "github.com/kubevela/workflow/pkg/providers/legacy/http/ratelimiter" "github.com/kubevela/workflow/pkg/providers/legacy/http/testdata" ) @@ -280,13 +279,7 @@ func TestHTTPSDo(t *testing.T) { } singleton.KubeClient.Set(cli) r := require.New(t) - v, err := value.NewValue(` -method: "GET" -url: "https://127.0.0.1:8443/api/v1/token?val=test-token" -`, nil, "") - r.NoError(err) - r.NoError(v.FillObject("certs", "tls_config", "secret")) - _, err = Do(ctx, &DoParams{ + _, err := Do(ctx, &DoParams{ Params: RequestVars{ Method: "GET", URL: "https://127.0.0.1:8443/api/v1/token?val=test-token", diff --git a/pkg/providers/legacy/kube/kube.go b/pkg/providers/legacy/kube/kube.go index 78c9b2f..d27c5cb 100644 --- a/pkg/providers/legacy/kube/kube.go +++ b/pkg/providers/legacy/kube/kube.go @@ -37,7 +37,6 @@ import ( "github.com/kubevela/workflow/pkg/cue/model" providertypes "github.com/kubevela/workflow/pkg/providers/types" - "github.com/kubevela/workflow/pkg/types" ) const ( @@ -47,10 +46,6 @@ const ( AnnoWorkflowLastAppliedTime = "workflow.oam.dev/last-applied-time" ) -type provider struct { - handlers providertypes.KubeHandlers -} - const ( // WorkflowResourceCreator is the creator name of workflow resource WorkflowResourceCreator string = "workflow" @@ -125,11 +120,22 @@ type ResourceReturns struct { } // ResourceParams . -type ResourceParams = types.LegacyParams[ResourceVars] +type ResourceParams = providertypes.LegacyParams[ResourceVars] + +func getHandlers(runtimeParams providertypes.RuntimeParams) *providertypes.KubeHandlers { + if runtimeParams.KubeHandlers != nil { + return runtimeParams.KubeHandlers + } + return &providertypes.KubeHandlers{ + Apply: apply, + Delete: delete, + } +} // Apply create or update CR in cluster. -func (h *provider) Apply(ctx context.Context, params *ResourceParams) (*ResourceReturns, error) { +func Apply(ctx context.Context, params *ResourceParams) (*ResourceReturns, error) { workload := params.Params.Resource + handlers := getHandlers(params.RuntimeParams) if workload.GetNamespace() == "" { workload.SetNamespace("default") } @@ -139,7 +145,7 @@ func (h *provider) Apply(ctx context.Context, params *ResourceParams) (*Resource } } deployCtx := handleContext(ctx, params.Params.Cluster) - if err := h.handlers.Apply(deployCtx, params.Params.Cluster, WorkflowResourceCreator, workload); err != nil { + if err := handlers.Apply(deployCtx, params.Params.Cluster, WorkflowResourceCreator, workload); err != nil { return nil, err } return &ResourceReturns{ @@ -159,18 +165,19 @@ type ApplyInParallelReturns struct { } // ApplyInParallelParams . -type ApplyInParallelParams = types.LegacyParams[ApplyInParallelVars] +type ApplyInParallelParams = providertypes.LegacyParams[ApplyInParallelVars] // ApplyInParallel create or update CRs in parallel. -func (h *provider) ApplyInParallel(ctx context.Context, params *ApplyInParallelParams) (*ApplyInParallelReturns, error) { +func ApplyInParallel(ctx context.Context, params *ApplyInParallelParams) (*ApplyInParallelReturns, error) { workloads := params.Params.Resources + handlers := getHandlers(params.RuntimeParams) for i := range workloads { if workloads[i].GetNamespace() == "" { workloads[i].SetNamespace("default") } } deployCtx := handleContext(ctx, params.Params.Cluster) - if err := h.handlers.Apply(deployCtx, params.Params.Cluster, WorkflowResourceCreator, workloads...); err != nil { + if err := handlers.Apply(deployCtx, params.Params.Cluster, WorkflowResourceCreator, workloads...); err != nil { return nil, err } return &ApplyInParallelReturns{ @@ -179,7 +186,8 @@ func (h *provider) ApplyInParallel(ctx context.Context, params *ApplyInParallelP } // Patch patch CR in cluster. -func (h *provider) Patch(ctx context.Context, params *types.LegacyParams[cue.Value]) (cue.Value, error) { +func Patch(ctx context.Context, params *providertypes.LegacyParams[cue.Value]) (cue.Value, error) { + handlers := getHandlers(params.RuntimeParams) val := params.Params.LookupPath(cue.ParsePath("value")) obj := new(unstructured.Unstructured) b, err := val.MarshalJSON() @@ -220,14 +228,14 @@ func (h *provider) Patch(ctx context.Context, params *types.LegacyParams[cue.Val return cue.Value{}, err } } - if err := h.handlers.Apply(multiCtx, cluster, WorkflowResourceCreator, workload); err != nil { + if err := handlers.Apply(multiCtx, cluster, WorkflowResourceCreator, workload); err != nil { return cue.Value{}, err } return params.Params.FillPath(cue.ParsePath("result"), workload), nil } // Read get CR from cluster. -func (h *provider) Read(ctx context.Context, params *ResourceParams) (*ResourceReturns, error) { +func Read(ctx context.Context, params *ResourceParams) (*ResourceReturns, error) { workload := params.Params.Resource key := client.ObjectKeyFromObject(workload) if key.Namespace == "" { @@ -251,7 +259,7 @@ type ListReturns struct { } // List lists CRs from cluster. -func (h *provider) List(ctx context.Context, params *ResourceParams) (*ListReturns, error) { +func List(ctx context.Context, params *ResourceParams) (*ListReturns, error) { workload := params.Params.Resource list := &unstructured.UnstructuredList{Object: map[string]interface{}{ "kind": workload.GetKind(), @@ -275,8 +283,9 @@ func (h *provider) List(ctx context.Context, params *ResourceParams) (*ListRetur } // Delete deletes CR from cluster. -func (h *provider) Delete(ctx context.Context, params *ResourceParams) (*ResourceReturns, error) { +func Delete(ctx context.Context, params *ResourceParams) (*ResourceReturns, error) { workload := params.Params.Resource + handlers := getHandlers(params.RuntimeParams) deleteCtx := handleContext(ctx, params.Params.Cluster) if filter := params.Params.Filter; filter != nil { @@ -292,7 +301,7 @@ func (h *provider) Delete(ctx context.Context, params *ResourceParams) (*Resourc return nil, nil } - if err := h.handlers.Delete(deleteCtx, params.Params.Cluster, WorkflowResourceCreator, workload); err != nil { + if err := handlers.Delete(deleteCtx, params.Params.Cluster, WorkflowResourceCreator, workload); err != nil { return &ResourceReturns{ Error: err, }, nil @@ -310,22 +319,13 @@ func GetTemplate() string { } // GetProviders get kube providers. -func GetProviders(handlers *providertypes.KubeHandlers) map[string]cuexruntime.ProviderFn { - if handlers == nil { - handlers = &providertypes.KubeHandlers{ - Apply: apply, - Delete: delete, - } - } - prd := &provider{ - handlers: *handlers, - } +func GetProviders() map[string]cuexruntime.ProviderFn { return map[string]cuexruntime.ProviderFn{ - "apply": providertypes.LegacyGenericProviderFn[ResourceVars, ResourceReturns](prd.Apply), - "apply-in-parallel": providertypes.LegacyGenericProviderFn[ApplyInParallelVars, ApplyInParallelReturns](prd.ApplyInParallel), - "read": providertypes.LegacyGenericProviderFn[ResourceVars, ResourceReturns](prd.Read), - "list": providertypes.LegacyGenericProviderFn[ResourceVars, ListReturns](prd.List), - "delete": providertypes.LegacyGenericProviderFn[ResourceVars, ResourceReturns](prd.Delete), - "patch": providertypes.LegacyNativeProviderFn(prd.Patch), + "apply": providertypes.LegacyGenericProviderFn[ResourceVars, ResourceReturns](Apply), + "apply-in-parallel": providertypes.LegacyGenericProviderFn[ApplyInParallelVars, ApplyInParallelReturns](ApplyInParallel), + "read": providertypes.LegacyGenericProviderFn[ResourceVars, ResourceReturns](Read), + "list": providertypes.LegacyGenericProviderFn[ResourceVars, ListReturns](List), + "delete": providertypes.LegacyGenericProviderFn[ResourceVars, ResourceReturns](Delete), + "patch": providertypes.LegacyNativeProviderFn(Patch), } } diff --git a/pkg/providers/legacy/kube/kube_test.go b/pkg/providers/legacy/kube/kube_test.go index e84e3b3..df3af14 100644 --- a/pkg/providers/legacy/kube/kube_test.go +++ b/pkg/providers/legacy/kube/kube_test.go @@ -42,9 +42,7 @@ import ( "github.com/kubevela/pkg/util/singleton" - "github.com/kubevela/workflow/pkg/cue/packages" providertypes "github.com/kubevela/workflow/pkg/providers/types" - "github.com/kubevela/workflow/pkg/types" ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to @@ -54,8 +52,6 @@ var cfg *rest.Config var k8sClient client.Client var testEnv *envtest.Environment var scheme = runtime.NewScheme() -var pd *packages.PackageDiscover -var p *provider func TestProvider(t *testing.T) { RegisterFailHandler(Fail) @@ -81,16 +77,8 @@ var _ = BeforeSuite(func(done Done) { k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) Expect(err).ToNot(HaveOccurred()) Expect(k8sClient).ToNot(BeNil()) - pd, err = packages.NewPackageDiscover(cfg) - Expect(err).ToNot(HaveOccurred()) singleton.KubeClient.Set(k8sClient) - p = &provider{ - handlers: providertypes.KubeHandlers{ - Apply: apply, - Delete: delete, - }, - } close(done) }, 120) @@ -111,7 +99,7 @@ var _ = Describe("Test Workflow Provider Kube", func() { un.SetLabels(map[string]string{ "test": "test", }) - res, err := p.Apply(ctx, &ResourceParams{ + res, err := Apply(ctx, &ResourceParams{ Params: ResourceVars{ Resource: un, }, @@ -130,7 +118,7 @@ var _ = Describe("Test Workflow Provider Kube", func() { "hello": "world", })) - res, err = p.Read(ctx, &ResourceParams{ + res, err = Read(ctx, &ResourceParams{ Params: ResourceVars{ Resource: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -159,7 +147,7 @@ var _ = Describe("Test Workflow Provider Kube", func() { un.SetLabels(map[string]string{ "test": "test", }) - _, err := p.Apply(ctx, &ResourceParams{ + _, err := Apply(ctx, &ResourceParams{ Params: ResourceVars{ Resource: &un, }, @@ -183,7 +171,7 @@ patch: { } } `) - _, err = p.Patch(ctx, &types.LegacyParams[cue.Value]{ + _, err = Patch(ctx, &providertypes.LegacyParams[cue.Value]{ Params: v, }) Expect(err).ToNot(HaveOccurred()) @@ -225,7 +213,7 @@ patch: { } By("List pods with labels test=test") - res, err := p.List(ctx, &ResourceParams{ + res, err := List(ctx, &ResourceParams{ Params: ResourceVars{ Resource: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -245,7 +233,7 @@ patch: { Expect(len(res.Resources.Items)).Should(Equal(5)) By("List pods with labels index=test-1") - res, err = p.List(ctx, &ResourceParams{ + res, err = List(ctx, &ResourceParams{ Params: ResourceVars{ Resource: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -287,7 +275,7 @@ patch: { }, &corev1.Pod{}) Expect(err).ToNot(HaveOccurred()) - _, err = p.Delete(ctx, &ResourceParams{ + _, err = Delete(ctx, &ResourceParams{ Params: ResourceVars{ Resource: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -336,7 +324,7 @@ patch: { }, &corev1.Pod{}) Expect(err).ToNot(HaveOccurred()) - _, err = p.Delete(ctx, &ResourceParams{ + _, err = Delete(ctx, &ResourceParams{ Params: ResourceVars{ Resource: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -370,7 +358,7 @@ patch: { un2 := testUnstructured.DeepCopy() un2.SetName("app2") ctx := context.Background() - _, err := p.ApplyInParallel(ctx, &ApplyInParallelParams{ + _, err := ApplyInParallel(ctx, &ApplyInParallelParams{ Params: ApplyInParallelVars{ Resources: []*unstructured.Unstructured{un1, un2}, }, @@ -393,7 +381,7 @@ patch: { It("test error case", func() { ctx := context.Background() - res, err := p.Read(ctx, &ResourceParams{ + res, err := Read(ctx, &ResourceParams{ Params: ResourceVars{ Resource: &unstructured.Unstructured{ Object: map[string]interface{}{ diff --git a/pkg/providers/legacy/legacy.go b/pkg/providers/legacy/legacy.go index d12d387..43e9001 100644 --- a/pkg/providers/legacy/legacy.go +++ b/pkg/providers/legacy/legacy.go @@ -11,7 +11,6 @@ import ( "github.com/kubevela/workflow/pkg/providers/legacy/metrics" "github.com/kubevela/workflow/pkg/providers/legacy/util" "github.com/kubevela/workflow/pkg/providers/legacy/workspace" - providertypes "github.com/kubevela/workflow/pkg/providers/types" ) func registerProviders(providers map[string]cuexruntime.ProviderFn, new map[string]cuexruntime.ProviderFn) map[string]cuexruntime.ProviderFn { @@ -22,11 +21,11 @@ func registerProviders(providers map[string]cuexruntime.ProviderFn, new map[stri } // GetLegacyProviders get legacy providers -func GetLegacyProviders(handlers *providertypes.KubeHandlers) map[string]cuexruntime.ProviderFn { +func GetLegacyProviders() map[string]cuexruntime.ProviderFn { providers := make(map[string]cuexruntime.ProviderFn, 0) registerProviders(providers, email.GetProviders()) registerProviders(providers, http.GetProviders()) - registerProviders(providers, kube.GetProviders(handlers)) + registerProviders(providers, kube.GetProviders()) registerProviders(providers, metrics.GetProviders()) registerProviders(providers, util.GetProviders()) registerProviders(providers, workspace.GetProviders()) diff --git a/pkg/providers/legacy/metrics/prom_check.go b/pkg/providers/legacy/metrics/prom_check.go index 4babe98..068183c 100644 --- a/pkg/providers/legacy/metrics/prom_check.go +++ b/pkg/providers/legacy/metrics/prom_check.go @@ -34,7 +34,6 @@ import ( wfContext "github.com/kubevela/workflow/pkg/context" "github.com/kubevela/workflow/pkg/cue/model" providertypes "github.com/kubevela/workflow/pkg/providers/types" - "github.com/kubevela/workflow/pkg/types" ) const ( @@ -59,7 +58,7 @@ type PromReturns struct { } // PromParams . -type PromParams = types.LegacyParams[PromVars] +type PromParams = providertypes.LegacyParams[PromVars] // PromCheck do health check from metrics from prometheus func PromCheck(ctx context.Context, params *PromParams) (*PromReturns, error) { diff --git a/pkg/providers/legacy/metrics/prom_check_test.go b/pkg/providers/legacy/metrics/prom_check_test.go index 6e093c7..c6967a9 100644 --- a/pkg/providers/legacy/metrics/prom_check_test.go +++ b/pkg/providers/legacy/metrics/prom_check_test.go @@ -31,7 +31,7 @@ import ( context2 "github.com/kubevela/workflow/pkg/context" "github.com/kubevela/workflow/pkg/cue/model" "github.com/kubevela/workflow/pkg/cue/process" - "github.com/kubevela/workflow/pkg/types" + providertypes "github.com/kubevela/workflow/pkg/providers/types" ) func TestMetricCheck(t *testing.T) { @@ -62,7 +62,7 @@ func TestMetricCheck(t *testing.T) { FailDuration: "2s", Condition: ">=3", }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ WorkflowContext: wfCtx, ProcessContext: pCtx, }, diff --git a/pkg/providers/legacy/util/util.go b/pkg/providers/legacy/util/util.go index e8bfa01..0ce236f 100644 --- a/pkg/providers/legacy/util/util.go +++ b/pkg/providers/legacy/util/util.go @@ -48,7 +48,7 @@ type PatchResult struct { } // PatchK8sObject patch k8s object -func PatchK8sObject(ctx context.Context, params *types.LegacyParams[cue.Value]) (cue.Value, error) { +func PatchK8sObject(ctx context.Context, params *providertypes.LegacyParams[cue.Value]) (cue.Value, error) { base, err := model.NewBase(params.Params.LookupPath(cue.ParsePath("value"))) if err != nil { return cue.Value{}, err @@ -75,7 +75,7 @@ type StringReturns struct { } // StringParams . -type StringParams = types.LegacyParams[StringVars] +type StringParams = providertypes.LegacyParams[StringVars] // String convert byte to string func String(ctx context.Context, params *StringParams) (*StringReturns, error) { @@ -112,7 +112,7 @@ type LogVars struct { } // LogParams . -type LogParams = types.LegacyParams[LogVars] +type LogParams = providertypes.LegacyParams[LogVars] // Log print cue value in log func Log(ctx context.Context, params *LogParams) (*any, error) { diff --git a/pkg/providers/legacy/util/util_test.go b/pkg/providers/legacy/util/util_test.go index d12935d..b36c515 100644 --- a/pkg/providers/legacy/util/util_test.go +++ b/pkg/providers/legacy/util/util_test.go @@ -32,7 +32,7 @@ import ( wfContext "github.com/kubevela/workflow/pkg/context" "github.com/kubevela/workflow/pkg/cue/model" "github.com/kubevela/workflow/pkg/cue/process" - "github.com/kubevela/workflow/pkg/types" + providertypes "github.com/kubevela/workflow/pkg/providers/types" ) func TestPatchK8sObject(t *testing.T) { @@ -129,7 +129,7 @@ spec: template: metadata: name: "test-patchStrategy"`, for name, tc := range testcases { t.Run(name, func(t *testing.T) { r := require.New(t) - res, err := PatchK8sObject(ctx, &types.LegacyParams[cue.Value]{ + res, err := PatchK8sObject(ctx, &providertypes.LegacyParams[cue.Value]{ Params: cuectx.CompileString(tc.value), }) if tc.expectedErr != nil { @@ -254,7 +254,7 @@ func TestLog(t *testing.T) { r := require.New(t) _, err := Log(ctx, &LogParams{ Params: tc.value, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ ProcessContext: pCtx, WorkflowContext: wfCtx, }, diff --git a/pkg/providers/legacy/workspace/workspace.go b/pkg/providers/legacy/workspace/workspace.go index 2bdf88a..6a70b58 100644 --- a/pkg/providers/legacy/workspace/workspace.go +++ b/pkg/providers/legacy/workspace/workspace.go @@ -32,7 +32,6 @@ import ( "github.com/kubevela/workflow/pkg/cue/model" "github.com/kubevela/workflow/pkg/errors" providertypes "github.com/kubevela/workflow/pkg/providers/types" - "github.com/kubevela/workflow/pkg/types" ) const ( @@ -57,7 +56,7 @@ type VarReturns struct { } // VarParams . -type VarParams = types.LegacyParams[VarVars] +type VarParams = providertypes.LegacyParams[VarVars] // DoVar get & put variable from context. func DoVar(ctx context.Context, params *VarParams) (*VarReturns, error) { @@ -100,7 +99,7 @@ type ActionVars struct { } // ActionParams . -type ActionParams = types.LegacyParams[ActionVars] +type ActionParams = providertypes.LegacyParams[ActionVars] // WaitVars . type WaitVars struct { @@ -109,7 +108,7 @@ type WaitVars struct { } // WaitParams . -type WaitParams = types.LegacyParams[WaitVars] +type WaitParams = providertypes.LegacyParams[WaitVars] // Wait let workflow wait. func Wait(ctx context.Context, params *WaitParams) (*any, error) { @@ -139,7 +138,7 @@ type SuspendVars struct { } // SuspendParams . -type SuspendParams = types.LegacyParams[SuspendVars] +type SuspendParams = providertypes.LegacyParams[SuspendVars] // Suspend let the step suspend, its status is suspending and reason is Suspend func Suspend(ctx context.Context, params *SuspendParams) (*any, error) { diff --git a/pkg/providers/legacy/workspace/workspace_test.go b/pkg/providers/legacy/workspace/workspace_test.go index 691ef7f..624525e 100644 --- a/pkg/providers/legacy/workspace/workspace_test.go +++ b/pkg/providers/legacy/workspace/workspace_test.go @@ -22,6 +22,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/yaml" @@ -30,8 +31,7 @@ import ( "github.com/kubevela/workflow/pkg/cue/model" "github.com/kubevela/workflow/pkg/cue/process" "github.com/kubevela/workflow/pkg/errors" - "github.com/kubevela/workflow/pkg/types" - "github.com/stretchr/testify/require" + providertypes "github.com/kubevela/workflow/pkg/providers/types" ) func TestProvider_DoVar(t *testing.T) { @@ -45,7 +45,7 @@ func TestProvider_DoVar(t *testing.T) { Path: "clusterIP", Value: "1.1.1.1", }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ WorkflowContext: wfCtx, }, }) @@ -61,7 +61,7 @@ func TestProvider_DoVar(t *testing.T) { Method: "Get", Path: "clusterIP", }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ WorkflowContext: wfCtx, }, }) @@ -81,7 +81,7 @@ func TestProvider_Wait(t *testing.T) { Message: "test log", }, }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, }, }) @@ -98,7 +98,7 @@ func TestProvider_Wait(t *testing.T) { Message: "omit msg", }, }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, }, }) @@ -112,7 +112,7 @@ func TestProvider_Break(t *testing.T) { r := require.New(t) act := &mockAction{} _, err := Break(ctx, &ActionParams{ - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, }, }) @@ -125,7 +125,7 @@ func TestProvider_Break(t *testing.T) { Params: ActionVars{ Message: "terminate", }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, }, }) @@ -147,7 +147,7 @@ func TestProvider_Suspend(t *testing.T) { Params: SuspendVars{ Duration: "1s", }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, WorkflowContext: wfCtx, ProcessContext: pCtx, @@ -174,7 +174,7 @@ func TestProvider_Fail(t *testing.T) { r := require.New(t) act := &mockAction{} _, err := Fail(ctx, &ActionParams{ - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, }, }) @@ -187,7 +187,7 @@ func TestProvider_Fail(t *testing.T) { Params: ActionVars{ Message: "fail", }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, }, }) @@ -205,7 +205,7 @@ func TestProvider_Message(t *testing.T) { Params: ActionVars{ Message: "test", }, - RuntimeParams: types.RuntimeParams{ + RuntimeParams: providertypes.RuntimeParams{ Action: act, }, }) diff --git a/pkg/providers/types/types.go b/pkg/providers/types/types.go index cbedfbb..ee864b9 100644 --- a/pkg/providers/types/types.go +++ b/pkg/providers/types/types.go @@ -35,10 +35,39 @@ const ( processContextKey contextKey = "processContext" actionKey contextKey = "action" labelsKey contextKey = "labels" + kubeHandlersKey contextKey = "kubeHandlers" ) +// Dispatcher is a client for apply resources. +type Dispatcher func(ctx context.Context, cluster, owner string, manifests ...*unstructured.Unstructured) error + +// Deleter is a client for delete resources. +type Deleter func(ctx context.Context, cluster, owner string, manifest *unstructured.Unstructured) error + +// KubeHandlers handles resources. +type KubeHandlers struct { + Apply Dispatcher + Delete Deleter +} + +// RuntimeParams is the runtime parameters of a provider. +type RuntimeParams struct { + WorkflowContext wfContext.Context + ProcessContext process.Context + Action types.Action + FieldLabel string + Labels map[string]string + KubeHandlers *KubeHandlers +} + +// LegacyParams is the legacy input parameters of a provider. +type LegacyParams[T any] struct { + Params T + RuntimeParams +} + // LegacyGenericProviderFn is the legacy provider function -type LegacyGenericProviderFn[T any, U any] func(context.Context, *types.LegacyParams[T]) (*U, error) +type LegacyGenericProviderFn[T any, U any] func(context.Context, *LegacyParams[T]) (*U, error) // Call marshal value into json and decode into underlying function input // parameters, then fill back the returned output value @@ -54,7 +83,7 @@ func (fn LegacyGenericProviderFn[T, U]) Call(ctx context.Context, value cue.Valu runtimeParams := RuntimeParamsFrom(ctx) label, _ := value.Label() runtimeParams.FieldLabel = label - ret, err := fn(ctx, &types.LegacyParams[T]{Params: *params, RuntimeParams: runtimeParams}) + ret, err := fn(ctx, &LegacyParams[T]{Params: *params, RuntimeParams: runtimeParams}) if err != nil { return value, err } @@ -62,13 +91,13 @@ func (fn LegacyGenericProviderFn[T, U]) Call(ctx context.Context, value cue.Valu } // LegacyNativeProviderFn is the legacy native provider function -type LegacyNativeProviderFn func(context.Context, *types.LegacyParams[cue.Value]) (cue.Value, error) +type LegacyNativeProviderFn func(context.Context, *LegacyParams[cue.Value]) (cue.Value, error) // Call marshal value into json and decode into underlying function input // parameters, then fill back the returned output value func (fn LegacyNativeProviderFn) Call(ctx context.Context, value cue.Value) (cue.Value, error) { runtimeParams := RuntimeParamsFrom(ctx) - return fn(ctx, &types.LegacyParams[cue.Value]{Params: value, RuntimeParams: runtimeParams}) + return fn(ctx, &LegacyParams[cue.Value]{Params: value, RuntimeParams: runtimeParams}) } // WithLabelParams returns a copy of parent in which the labels value is set @@ -77,7 +106,7 @@ func WithLabelParams(parent context.Context, labels map[string]string) context.C } // WithRuntimeParams returns a copy of parent in which the runtime params value is set -func WithRuntimeParams(parent context.Context, params types.RuntimeParams) context.Context { +func WithRuntimeParams(parent context.Context, params RuntimeParams) context.Context { ctx := context.WithValue(parent, workflowContextKey, params.WorkflowContext) ctx = context.WithValue(ctx, processContextKey, params.ProcessContext) ctx = context.WithValue(ctx, actionKey, params.Action) @@ -85,8 +114,8 @@ func WithRuntimeParams(parent context.Context, params types.RuntimeParams) conte } // RuntimeParamsFrom returns the runtime params value stored in ctx, if any. -func RuntimeParamsFrom(ctx context.Context) types.RuntimeParams { - params := types.RuntimeParams{} +func RuntimeParamsFrom(ctx context.Context) RuntimeParams { + params := RuntimeParams{} if wfCtx, ok := ctx.Value(workflowContextKey).(wfContext.Context); ok { params.WorkflowContext = wfCtx } @@ -99,17 +128,8 @@ func RuntimeParamsFrom(ctx context.Context) types.RuntimeParams { if labels, ok := ctx.Value(labelsKey).(map[string]string); ok { params.Labels = labels } + if kubeHandlers, ok := ctx.Value(kubeHandlersKey).(*KubeHandlers); ok { + params.KubeHandlers = kubeHandlers + } return params } - -// Dispatcher is a client for apply resources. -type Dispatcher func(ctx context.Context, cluster, owner string, manifests ...*unstructured.Unstructured) error - -// Deleter is a client for delete resources. -type Deleter func(ctx context.Context, cluster, owner string, manifest *unstructured.Unstructured) error - -// KubeHandlers handles resources. -type KubeHandlers struct { - Apply Dispatcher - Delete Deleter -} diff --git a/pkg/tasks/custom/task.go b/pkg/tasks/custom/task.go index 7168a0b..4545c76 100644 --- a/pkg/tasks/custom/task.go +++ b/pkg/tasks/custom/task.go @@ -162,7 +162,7 @@ func (t *TaskLoader) makeTaskGenerator(templ string) (types.TaskGenerator, error resetter := tRunner.fillContext(tracer, options.PCtx) defer resetter(options.PCtx) - ctx := providertypes.WithRuntimeParams(tracer.GetContext(), types.RuntimeParams{ + ctx := providertypes.WithRuntimeParams(tracer.GetContext(), providertypes.RuntimeParams{ WorkflowContext: wfCtx, ProcessContext: options.PCtx, Action: exec, diff --git a/pkg/tasks/custom/task_test.go b/pkg/tasks/custom/task_test.go index 632b2b9..b81f43c 100644 --- a/pkg/tasks/custom/task_test.go +++ b/pkg/tasks/custom/task_test.go @@ -63,26 +63,26 @@ func TestTaskLoader(t *testing.T) { "templateError": cuexruntime.NativeProviderFn(func(ctx context.Context, v cue.Value) (cue.Value, error) { return v.Context().CompileString("output: xxx"), nil }), - "wait": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "wait": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { val.RuntimeParams.Action.Wait("I am waiting") return nil, nil }), - "terminate": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "terminate": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { val.RuntimeParams.Action.Terminate("I am terminated") return nil, nil }), - "suspend": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "suspend": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { val.RuntimeParams.Action.Suspend("I am suspended") return nil, nil }), - "resume": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "resume": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { val.RuntimeParams.Action.Resume("I am resumed") return nil, nil }), - "executeFailed": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "executeFailed": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { return nil, errors.New("execute error") }), - "ok": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "ok": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { return nil, nil }), }, @@ -197,10 +197,10 @@ func TestErrCases(t *testing.T) { compiler := cuex.NewCompilerWithInternalPackages( // legacy packages pkgruntime.Must(cuexruntime.NewInternalPackage("test", "", map[string]cuexruntime.ProviderFn{ - "ok": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "ok": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { return nil, nil }), - "error": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "error": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { return nil, errors.New("mock error") }), "input": cuexruntime.NativeProviderFn(func(ctx context.Context, v cue.Value) (cue.Value, error) { @@ -316,139 +316,6 @@ func TestErrCases(t *testing.T) { } } -// func TestSteps(t *testing.T) { - -// var ( -// echo string -// mockErr = errors.New("mock error") -// ) - -// wfCtx := newWorkflowContextForTest(t) -// r := require.New(t) -// discover := providers.NewProviders() -// discover.Register("test", map[string]types.Handler{ -// "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { -// echo = echo + "ok" -// return nil -// }, -// "error": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { -// return mockErr -// }, -// }) -// exec := &executor{ -// handlers: discover, -// } - -// testCases := []struct { -// base string -// expected string -// hasErr bool -// }{ -// { -// base: ` -// process: { -// #provider: "test" -// #do: "ok" -// } - -// #up: [process] -// `, -// expected: "okok", -// }, -// { -// base: ` -// process: { -// #provider: "test" -// #do: "ok" -// } - -// #up: [process,{ -// #do: "steps" -// p1: process -// #up: [process] -// }] -// `, -// expected: "okokokok", -// }, -// { -// base: ` -// process: { -// #provider: "test" -// #do: "ok" -// } - -// #up: [process,{ -// p1: process -// #up: [process] -// }] -// `, -// expected: "okok", -// }, -// { -// base: ` -// process: { -// #provider: "test" -// #do: "ok" -// } - -// #up: [process,{ -// #do: "steps" -// err: { -// #provider: "test" -// #do: "error" -// } @step(1) -// #up: [{},process] @step(2) -// }] -// `, -// expected: "okok", -// hasErr: true, -// }, - -// { -// base: ` -// #provider: "test" -// #do: "ok" -// `, -// expected: "ok", -// }, -// { -// base: ` -// process: { -// #provider: "test" -// #do: "ok" -// err: true -// } - -// if process.err { -// err: { -// #provider: "test" -// #do: "error" -// } -// } - -// apply: { -// #provider: "test" -// #do: "ok" -// } - -// #up: [process,{}] -// `, -// expected: "ok", -// hasErr: true, -// }, -// } - -// for i, tc := range testCases { -// echo = "" -// v, err := value.NewValue(tc.base, nil, "", value.TagFieldOrder) -// r.NoError(err) -// err = exec.doSteps(nil, wfCtx, v) -// r.Equal(err != nil, tc.hasErr) -// r.Equal(tc.expected, echo, i) -// } - -// } - func TestPendingInputCheck(t *testing.T) { wfCtx := newWorkflowContextForTest(t) r := require.New(t) @@ -549,7 +416,7 @@ func TestTimeout(t *testing.T) { compiler := cuex.NewCompilerWithInternalPackages( // legacy packages pkgruntime.Must(cuexruntime.NewInternalPackage("test", "", map[string]cuexruntime.ProviderFn{ - "ok": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *types.LegacyParams[any]) (*any, error) { + "ok": providertypes.LegacyGenericProviderFn[any, any](func(ctx context.Context, val *providertypes.LegacyParams[any]) (*any, error) { return nil, nil }), })), diff --git a/pkg/types/types.go b/pkg/types/types.go index 4453bb8..868afa1 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -29,27 +29,11 @@ import ( "github.com/kubevela/workflow/api/v1alpha1" wfContext "github.com/kubevela/workflow/pkg/context" - "github.com/kubevela/workflow/pkg/cue/model/value" "github.com/kubevela/workflow/pkg/cue/process" "github.com/kubevela/workflow/pkg/features" "github.com/kubevela/workflow/pkg/tasks/template" ) -// RuntimeParams is the runtime parameters of a provider. -type RuntimeParams struct { - WorkflowContext wfContext.Context - ProcessContext process.Context - Action Action - FieldLabel string - Labels map[string]string -} - -// LegacyParams is the legacy input parameters of a provider. -type LegacyParams[T any] struct { - Params T - RuntimeParams -} - // WorkflowInstance is the instance for workflow engine to execute type WorkflowInstance struct { WorkflowMeta @@ -98,7 +82,6 @@ type Engine interface { // TaskRunOptions is the options for task run type TaskRunOptions struct { - Data *value.Value PCtx process.Context PreCheckHooks []TaskPreCheckHook PreStartHooks []TaskPreStartHook diff --git a/pkg/utils/operation.go b/pkg/utils/operation.go index ca5f4fe..b765ef2 100644 --- a/pkg/utils/operation.go +++ b/pkg/utils/operation.go @@ -24,6 +24,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/format" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -34,7 +35,6 @@ import ( "github.com/kubevela/workflow/api/v1alpha1" wfContext "github.com/kubevela/workflow/pkg/context" "github.com/kubevela/workflow/pkg/cue/model/sets" - "github.com/kubevela/workflow/pkg/cue/model/value" wfTypes "github.com/kubevela/workflow/pkg/types" ) @@ -424,10 +424,7 @@ func CleanStatusFromStep(steps []v1alpha1.WorkflowStep, stepStatus []v1alpha1.Wo return nil, nil, fmt.Errorf("failed step %s not found", stepName) } if contextCM != nil && contextCM.Data != nil { - v, err := value.NewValue(contextCM.Data[wfContext.ConfigMapKeyVars], nil, "") - if err != nil { - return nil, nil, err - } + v := cuecontext.New().CompileString(contextCM.Data[wfContext.ConfigMapKeyVars]) s, err := clearContextVars(steps, v, stepName, dependency) if err != nil { return nil, nil, err @@ -438,7 +435,7 @@ func CleanStatusFromStep(steps []v1alpha1.WorkflowStep, stepStatus []v1alpha1.Wo } // nolint:staticcheck -func clearContextVars(steps []v1alpha1.WorkflowStep, v *value.Value, stepName string, dependency []string) (string, error) { +func clearContextVars(steps []v1alpha1.WorkflowStep, v cue.Value, stepName string, dependency []string) (string, error) { outputs := make([]string, 0) for _, step := range steps { if step.Name == stepName || stringsContain(dependency, step.Name) { @@ -454,7 +451,7 @@ func clearContextVars(steps []v1alpha1.WorkflowStep, v *value.Value, stepName st } } } - node := v.CueValue().Syntax(cue.ResolveReferences(true)) + node := v.Syntax(cue.ResolveReferences(true)) x, ok := node.(*ast.StructLit) if !ok { return "", fmt.Errorf("value is not a struct lit") diff --git a/pkg/webhook/v1alpha1/workflowrun/suite_test.go b/pkg/webhook/v1alpha1/workflowrun/suite_test.go index 3261332..6ac5d6f 100644 --- a/pkg/webhook/v1alpha1/workflowrun/suite_test.go +++ b/pkg/webhook/v1alpha1/workflowrun/suite_test.go @@ -36,7 +36,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/kubevela/workflow/api/v1alpha1" - "github.com/kubevela/workflow/pkg/cue/packages" // +kubebuilder:scaffold:imports ) @@ -48,7 +47,6 @@ var k8sClient client.Client var testEnv *envtest.Environment var testScheme = runtime.NewScheme() var decoder *admission.Decoder -var pd *packages.PackageDiscover var ctx = context.Background() var handler *ValidatingHandler @@ -86,13 +84,7 @@ var _ = BeforeSuite(func(done Done) { Expect(err).ToNot(HaveOccurred()) Expect(k8sClient).ToNot(BeNil()) - pd, err = packages.NewPackageDiscover(cfg) - Expect(err).ToNot(HaveOccurred()) - Expect(pd).ToNot(BeNil()) - - handler = &ValidatingHandler{ - pd: pd, - } + handler = &ValidatingHandler{} decoder, err = admission.NewDecoder(testScheme) Expect(err).Should(BeNil()) diff --git a/pkg/webhook/v1alpha1/workflowrun/validating_handler.go b/pkg/webhook/v1alpha1/workflowrun/validating_handler.go index da9ceac..b9b6d7d 100644 --- a/pkg/webhook/v1alpha1/workflowrun/validating_handler.go +++ b/pkg/webhook/v1alpha1/workflowrun/validating_handler.go @@ -30,7 +30,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/kubevela/workflow/api/v1alpha1" - "github.com/kubevela/workflow/pkg/cue/packages" "github.com/kubevela/workflow/pkg/types" "github.com/kubevela/workflow/controllers" @@ -40,7 +39,6 @@ var _ admission.Handler = &ValidatingHandler{} // ValidatingHandler handles application type ValidatingHandler struct { - pd *packages.PackageDiscover Client client.Client // Decoder decodes objects Decoder *admission.Decoder