github.com/oam-dev/kubevela@v1.9.11/pkg/definition/definition.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package definition contains some helper functions used in vela CLI
    18  // and vela addon mechanism
    19  package definition
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"strings"
    26  
    27  	"cuelang.org/go/cue"
    28  	"cuelang.org/go/cue/ast"
    29  	"cuelang.org/go/cue/cuecontext"
    30  	"cuelang.org/go/cue/format"
    31  	"cuelang.org/go/cue/parser"
    32  	"cuelang.org/go/encoding/gocode/gocodec"
    33  	"cuelang.org/go/tools/fix"
    34  	"github.com/pkg/errors"
    35  	"k8s.io/apimachinery/pkg/api/meta"
    36  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    37  	"k8s.io/apimachinery/pkg/runtime"
    38  	"k8s.io/apimachinery/pkg/runtime/schema"
    39  	"k8s.io/client-go/rest"
    40  	"sigs.k8s.io/controller-runtime/pkg/client"
    41  	"sigs.k8s.io/yaml"
    42  
    43  	"github.com/kubevela/workflow/pkg/cue/model/sets"
    44  	"github.com/kubevela/workflow/pkg/cue/model/value"
    45  	"github.com/kubevela/workflow/pkg/cue/packages"
    46  
    47  	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    48  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    49  	velacue "github.com/oam-dev/kubevela/pkg/cue"
    50  	"github.com/oam-dev/kubevela/pkg/oam"
    51  	"github.com/oam-dev/kubevela/pkg/utils"
    52  	"github.com/oam-dev/kubevela/pkg/utils/filters"
    53  )
    54  
    55  const (
    56  	// DescriptionKey the key for accessing definition description
    57  	DescriptionKey = "definition.oam.dev/description"
    58  	// AliasKey the key for accessing definition alias
    59  	AliasKey = "definition.oam.dev/alias"
    60  	// UserPrefix defines the prefix of user customized label or annotation
    61  	UserPrefix = "custom.definition.oam.dev/"
    62  )
    63  
    64  // the names for different type of definition
    65  const (
    66  	componentDefType    = "component"
    67  	traitDefType        = "trait"
    68  	policyDefType       = "policy"
    69  	workflowStepDefType = "workflow-step"
    70  	workloadDefType     = "workload"
    71  )
    72  
    73  var (
    74  	// DefinitionTemplateKeys the keys for accessing definition template
    75  	DefinitionTemplateKeys = []string{"spec", "schematic", "cue", "template"}
    76  	// DefinitionTypeToKind maps the definition types to corresponding kinds
    77  	DefinitionTypeToKind = map[string]string{
    78  		componentDefType:    v1beta1.ComponentDefinitionKind,
    79  		traitDefType:        v1beta1.TraitDefinitionKind,
    80  		policyDefType:       v1beta1.PolicyDefinitionKind,
    81  		workloadDefType:     v1beta1.WorkloadDefinitionKind,
    82  		workflowStepDefType: v1beta1.WorkflowStepDefinitionKind,
    83  	}
    84  	// StringToDefinitionType converts user input to DefinitionType used in DefinitionRevisions
    85  	StringToDefinitionType = map[string]common.DefinitionType{
    86  		// component
    87  		componentDefType: common.ComponentType,
    88  		// trait
    89  		traitDefType: common.TraitType,
    90  		// policy
    91  		policyDefType: common.PolicyType,
    92  		// workflow-step
    93  		workflowStepDefType: common.WorkflowStepType,
    94  	}
    95  	// DefinitionKindToNameLabel records DefinitionRevision types and labels to search its name
    96  	DefinitionKindToNameLabel = map[common.DefinitionType]string{
    97  		common.ComponentType:    oam.LabelComponentDefinitionName,
    98  		common.TraitType:        oam.LabelTraitDefinitionName,
    99  		common.PolicyType:       oam.LabelPolicyDefinitionName,
   100  		common.WorkflowStepType: oam.LabelWorkflowStepDefinitionName,
   101  	}
   102  	// DefinitionKindToType maps the definition kinds to a shorter type
   103  	DefinitionKindToType = map[string]string{
   104  		v1beta1.ComponentDefinitionKind:    componentDefType,
   105  		v1beta1.TraitDefinitionKind:        traitDefType,
   106  		v1beta1.PolicyDefinitionKind:       policyDefType,
   107  		v1beta1.WorkloadDefinitionKind:     workloadDefType,
   108  		v1beta1.WorkflowStepDefinitionKind: workflowStepDefType,
   109  	}
   110  )
   111  
   112  // Definition the general struct for handling all kinds of definitions like ComponentDefinition or TraitDefinition
   113  type Definition struct {
   114  	unstructured.Unstructured
   115  }
   116  
   117  // SetGVK set the GroupVersionKind of Definition
   118  func (def *Definition) SetGVK(kind string) {
   119  	def.SetGroupVersionKind(schema.GroupVersionKind{
   120  		Group:   v1beta1.Group,
   121  		Version: v1beta1.Version,
   122  		Kind:    kind,
   123  	})
   124  }
   125  
   126  // GetType gets the type of Definition
   127  func (def *Definition) GetType() string {
   128  	kind := def.GetKind()
   129  	for k, v := range DefinitionTypeToKind {
   130  		if v == kind {
   131  			return k
   132  		}
   133  	}
   134  	return strings.ToLower(strings.TrimSuffix(kind, "Definition"))
   135  }
   136  
   137  // SetType sets the type of Definition
   138  func (def *Definition) SetType(t string) error {
   139  	kind, ok := DefinitionTypeToKind[t]
   140  	if !ok {
   141  		return fmt.Errorf("invalid type %s", t)
   142  	}
   143  	def.SetGVK(kind)
   144  	return nil
   145  }
   146  
   147  // ToCUE converts Definition to CUE value (with predefined Definition's cue format)
   148  // nolint:staticcheck
   149  func (def *Definition) ToCUE() (*cue.Value, string, error) {
   150  	annotations := map[string]string{}
   151  	for key, val := range def.GetAnnotations() {
   152  		if strings.HasPrefix(key, UserPrefix) {
   153  			annotations[strings.TrimPrefix(key, UserPrefix)] = val
   154  		}
   155  	}
   156  	alias := def.GetAnnotations()[AliasKey]
   157  	desc := def.GetAnnotations()[DescriptionKey]
   158  	labels := map[string]string{}
   159  	for key, val := range def.GetLabels() {
   160  		if strings.HasPrefix(key, UserPrefix) {
   161  			labels[strings.TrimPrefix(key, UserPrefix)] = val
   162  		}
   163  	}
   164  	spec := map[string]interface{}{}
   165  	for key, val := range def.Object["spec"].(map[string]interface{}) {
   166  		if key != "schematic" {
   167  			spec[key] = val
   168  		}
   169  	}
   170  	obj := map[string]interface{}{
   171  		def.GetName(): map[string]interface{}{
   172  			"type":        def.GetType(),
   173  			"alias":       alias,
   174  			"description": desc,
   175  			"annotations": annotations,
   176  			"labels":      labels,
   177  			"attributes":  spec,
   178  		},
   179  	}
   180  	codec := gocodec.New((*cue.Runtime)(cuecontext.New()), &gocodec.Config{})
   181  	val, err := codec.Decode(obj)
   182  	if err != nil {
   183  		return nil, "", err
   184  	}
   185  
   186  	templateString, _, err := unstructured.NestedString(def.Object, DefinitionTemplateKeys...)
   187  	if err != nil {
   188  		return nil, "", err
   189  	}
   190  	templateString, err = formatCUEString(templateString)
   191  	if err != nil {
   192  		return nil, "", err
   193  	}
   194  	return &val, templateString, nil
   195  }
   196  
   197  // ToCUEString converts definition to CUE value and then encode to string
   198  func (def *Definition) ToCUEString() (string, error) {
   199  	val, templateString, err := def.ToCUE()
   200  	if err != nil {
   201  		return "", err
   202  	}
   203  	metadataString, err := sets.ToString(*val)
   204  	if err != nil {
   205  		return "", err
   206  	}
   207  
   208  	f, err := parser.ParseFile("-", templateString, parser.ParseComments)
   209  	if err != nil {
   210  		return "", errors.Wrapf(err, "failed to parse template cue string")
   211  	}
   212  	f = fix.File(f)
   213  	var importDecls, templateDecls []ast.Decl
   214  	for _, decl := range f.Decls {
   215  		if importDecl, ok := decl.(*ast.ImportDecl); ok {
   216  			importDecls = append(importDecls, importDecl)
   217  		} else {
   218  			templateDecls = append(templateDecls, decl)
   219  		}
   220  	}
   221  	importString, err := encodeDeclsToString(importDecls)
   222  	if err != nil {
   223  		return "", errors.Wrapf(err, "failed to encode import decls")
   224  	}
   225  	templateString, err = encodeDeclsToString(templateDecls)
   226  	if err != nil {
   227  		return "", errors.Wrapf(err, "failed to encode template decls")
   228  	}
   229  	templateString = fmt.Sprintf("template: {\n%s}", templateString)
   230  
   231  	completeCUEString := importString + "\n" + metadataString + "\n" + templateString
   232  	if completeCUEString, err = formatCUEString(completeCUEString); err != nil {
   233  		return "", errors.Wrapf(err, "failed to format cue format string")
   234  	}
   235  	return completeCUEString, nil
   236  }
   237  
   238  // FromCUE converts CUE value (predefined Definition's cue format) to Definition
   239  // nolint:gocyclo,staticcheck
   240  func (def *Definition) FromCUE(val *cue.Value, templateString string) error {
   241  	if def.Object == nil {
   242  		def.Object = map[string]interface{}{}
   243  	}
   244  	annotations := map[string]string{}
   245  	for k, v := range def.GetAnnotations() {
   246  		if !strings.HasPrefix(k, UserPrefix) && k != DescriptionKey {
   247  			annotations[k] = v
   248  		}
   249  	}
   250  	labels := map[string]string{}
   251  	for k, v := range def.GetLabels() {
   252  		if !strings.HasPrefix(k, UserPrefix) {
   253  			labels[k] = v
   254  		}
   255  	}
   256  	spec, ok := def.Object["spec"].(map[string]interface{})
   257  	if !ok {
   258  		spec = map[string]interface{}{}
   259  	}
   260  	codec := gocodec.New(&cue.Runtime{}, &gocodec.Config{})
   261  	nameFlag := false
   262  	fields, err := val.Fields()
   263  	if err != nil {
   264  		return err
   265  	}
   266  	for fields.Next() {
   267  		definitionName := fields.Label()
   268  		v := fields.Value()
   269  		if nameFlag {
   270  			return fmt.Errorf("duplicated definition name found, %s and %s", def.GetName(), definitionName)
   271  		}
   272  		nameFlag = true
   273  		def.SetName(definitionName)
   274  		_fields, err := v.Fields()
   275  		if err != nil {
   276  			return err
   277  		}
   278  		for _fields.Next() {
   279  			_key := _fields.Label()
   280  			_value := _fields.Value()
   281  			switch _key {
   282  			case "type":
   283  				_type, err := _value.String()
   284  				if err != nil {
   285  					return err
   286  				}
   287  				if err = def.SetType(_type); err != nil {
   288  					return err
   289  				}
   290  			case "alias":
   291  				alias, err := _value.String()
   292  				if err != nil {
   293  					return err
   294  				}
   295  				annotations[AliasKey] = alias
   296  			case "description":
   297  				desc, err := _value.String()
   298  				if err != nil {
   299  					return err
   300  				}
   301  				annotations[DescriptionKey] = desc
   302  			case "annotations":
   303  				var _annotations map[string]string
   304  				if err := codec.Encode(_value, &_annotations); err != nil {
   305  					return err
   306  				}
   307  				for _k, _v := range _annotations {
   308  					if strings.Contains(_k, "oam.dev") {
   309  						annotations[_k] = _v
   310  					} else {
   311  						annotations[UserPrefix+_k] = _v
   312  					}
   313  				}
   314  			case "labels":
   315  				var _labels map[string]string
   316  				if err := codec.Encode(_value, &_labels); err != nil {
   317  					return err
   318  				}
   319  				for _k, _v := range _labels {
   320  					if strings.Contains(_k, "oam.dev") {
   321  						labels[_k] = _v
   322  					} else {
   323  						labels[UserPrefix+_k] = _v
   324  					}
   325  				}
   326  			case "attributes":
   327  				if err := codec.Encode(_value, &spec); err != nil {
   328  					return err
   329  				}
   330  			}
   331  		}
   332  	}
   333  	def.SetAnnotations(annotations)
   334  	def.SetLabels(labels)
   335  	if err := unstructured.SetNestedField(spec, templateString, DefinitionTemplateKeys[1:]...); err != nil {
   336  		return err
   337  	}
   338  	if err = validateSpec(spec, def.GetType()); err != nil {
   339  		return fmt.Errorf("invalid definition spec: %w", err)
   340  	}
   341  	def.Object["spec"] = spec
   342  	return nil
   343  }
   344  
   345  func validateSpec(spec map[string]interface{}, t string) error {
   346  	bs, err := json.Marshal(spec)
   347  	if err != nil {
   348  		return err
   349  	}
   350  	var tpl interface{}
   351  	switch t {
   352  	case componentDefType:
   353  		tpl = &v1beta1.ComponentDefinitionSpec{}
   354  	case traitDefType:
   355  		tpl = &v1beta1.TraitDefinitionSpec{}
   356  	case policyDefType:
   357  		tpl = &v1beta1.PolicyDefinitionSpec{}
   358  	case workflowStepDefType:
   359  		tpl = &v1beta1.WorkflowStepDefinitionSpec{}
   360  	default:
   361  	}
   362  	if tpl != nil {
   363  		return utils.StrictUnmarshal(bs, tpl)
   364  	}
   365  	return nil
   366  }
   367  
   368  func encodeDeclsToString(decls []ast.Decl) (string, error) {
   369  	bs, err := format.Node(&ast.File{Decls: decls}, format.Simplify())
   370  	if err != nil {
   371  		return "", fmt.Errorf("failed to encode cue: %w", err)
   372  	}
   373  	return strings.TrimSpace(string(bs)) + "\n", nil
   374  }
   375  
   376  // FromYAML converts yaml into Definition
   377  func (def *Definition) FromYAML(data []byte) error {
   378  	return yaml.Unmarshal(data, def)
   379  }
   380  
   381  // FromCUEString converts cue string into Definition
   382  func (def *Definition) FromCUEString(cueString string, config *rest.Config) error {
   383  	cuectx := cuecontext.New()
   384  	f, err := parser.ParseFile("-", cueString, parser.ParseComments)
   385  	if err != nil {
   386  		return err
   387  	}
   388  	n := fix.File(f)
   389  	var importDecls, metadataDecls, templateDecls []ast.Decl
   390  	for _, decl := range n.Decls {
   391  		if importDecl, ok := decl.(*ast.ImportDecl); ok {
   392  			importDecls = append(importDecls, importDecl)
   393  		} else if field, ok := decl.(*ast.Field); ok {
   394  			label := ""
   395  			switch l := field.Label.(type) {
   396  			case *ast.Ident:
   397  				label = l.Name
   398  			case *ast.BasicLit:
   399  				label = l.Value
   400  			}
   401  			if label == "" {
   402  				return errors.Errorf("found unexpected decl when parsing cue: %v", label)
   403  			}
   404  			if label == "template" {
   405  				if v, ok := field.Value.(*ast.StructLit); ok {
   406  					templateDecls = append(templateDecls, v.Elts...)
   407  				} else {
   408  					return errors.Errorf("unexpected decl found in template: %v", decl)
   409  				}
   410  			} else {
   411  				metadataDecls = append(metadataDecls, field)
   412  			}
   413  		}
   414  	}
   415  	if len(metadataDecls) == 0 {
   416  		return errors.Errorf("no metadata found, invalid")
   417  	}
   418  	if len(templateDecls) == 0 {
   419  		return errors.Errorf("no template found, invalid")
   420  	}
   421  	var importString, metadataString, templateString string
   422  	if importString, err = encodeDeclsToString(importDecls); err != nil {
   423  		return errors.Wrapf(err, "failed to encode import decls to string")
   424  	}
   425  	if metadataString, err = encodeDeclsToString(metadataDecls); err != nil {
   426  		return errors.Wrapf(err, "failed to encode metadata decls to string")
   427  	}
   428  	// notice that current template decls are concatenated without any blank lines which might be inconsistent with original cue file, but it would not affect the syntax
   429  	if templateString, err = encodeDeclsToString(templateDecls); err != nil {
   430  		return errors.Wrapf(err, "failed to encode template decls to string")
   431  	}
   432  
   433  	inst := cuectx.CompileString(metadataString)
   434  	if inst.Err() != nil {
   435  		return inst.Err()
   436  	}
   437  	templateString, err = formatCUEString(importString + templateString)
   438  	if err != nil {
   439  		return err
   440  	}
   441  	var pd *packages.PackageDiscover
   442  	// validate template
   443  	if config != nil {
   444  		pd, err = packages.NewPackageDiscover(config)
   445  		if err != nil {
   446  			return err
   447  		}
   448  	}
   449  	if _, err = value.NewValue(templateString+"\n"+velacue.BaseTemplate, pd, ""); err != nil {
   450  		return err
   451  	}
   452  	return def.FromCUE(&inst, templateString)
   453  }
   454  
   455  // ValidDefinitionTypes return the list of valid definition types
   456  func ValidDefinitionTypes() []string {
   457  	var types []string
   458  	for k := range DefinitionTypeToKind {
   459  		types = append(types, k)
   460  	}
   461  	return types
   462  }
   463  
   464  // SearchDefinition search the Definition in k8s by traversing all possible results across types or namespaces
   465  func SearchDefinition(c client.Client, definitionType, namespace string, additionalFilters ...filters.Filter) ([]unstructured.Unstructured, error) {
   466  	ctx := context.Background()
   467  	var kinds []string
   468  	if definitionType != "" {
   469  		kind, ok := DefinitionTypeToKind[definitionType]
   470  		if !ok {
   471  			return nil, fmt.Errorf("invalid definition type %s", kind)
   472  		}
   473  		kinds = []string{kind}
   474  	} else {
   475  		for _, kind := range DefinitionTypeToKind {
   476  			kinds = append(kinds, kind)
   477  		}
   478  	}
   479  	var listOptions []client.ListOption
   480  	if namespace != "" {
   481  		listOptions = []client.ListOption{client.InNamespace(namespace)}
   482  	}
   483  	var definitions []unstructured.Unstructured
   484  	for _, kind := range kinds {
   485  		objs := unstructured.UnstructuredList{}
   486  		objs.SetGroupVersionKind(schema.GroupVersionKind{
   487  			Group:   v1beta1.Group,
   488  			Version: v1beta1.Version,
   489  			Kind:    kind + "List",
   490  		})
   491  		if err := c.List(ctx, &objs, listOptions...); err != nil {
   492  			if meta.IsNoMatchError(err) {
   493  				continue
   494  			}
   495  			return nil, errors.Wrapf(err, "failed to get %s", kind)
   496  		}
   497  
   498  		// Apply filters to the object list
   499  		filteredList := filters.ApplyToList(objs, additionalFilters...)
   500  
   501  		definitions = append(definitions, filteredList.Items...)
   502  	}
   503  	return definitions, nil
   504  }
   505  
   506  // SearchDefinitionRevisions finds DefinitionRevisions.
   507  // Use defName to filter DefinitionRevisions using the name of the underlying Definition.
   508  // Empty defName will keep everything.
   509  // Use defType to only keep DefinitionRevisions of the specified DefinitionType.
   510  // Empty defType will search every possible type.
   511  // Use rev to only keep the revision you want. rev=0 will keep every revision.
   512  func SearchDefinitionRevisions(ctx context.Context, c client.Client, namespace string,
   513  	defName string, defType common.DefinitionType, rev int64) ([]v1beta1.DefinitionRevision, error) {
   514  	var nameLabels []string
   515  
   516  	if defName == "" {
   517  		// defName="" means we don't care about the underlying definition names.
   518  		// So, no need to add name labels, just use anything to let the loop run once.
   519  		nameLabels = append(nameLabels, "")
   520  	} else {
   521  		// Since different definitions have different labels for its name, we need to
   522  		// find the corresponding label for definition names, to match names later.
   523  		// Empty defType will give all possible name labels of DefinitionRevisions,
   524  		// so that we can search for DefinitionRevisions of all Definition types.
   525  		for k, v := range DefinitionKindToNameLabel {
   526  			if defType != "" && defType != k {
   527  				continue
   528  			}
   529  			nameLabels = append(nameLabels, v)
   530  		}
   531  	}
   532  
   533  	var defRev []v1beta1.DefinitionRevision
   534  
   535  	// Search DefinitionRevisions using each possible label
   536  	for _, l := range nameLabels {
   537  		var listOptions []client.ListOption
   538  		if namespace != "" {
   539  			listOptions = append(listOptions, client.InNamespace(namespace))
   540  		}
   541  		// Using name label to find DefinitionRevisions with specified name.
   542  		if defName != "" {
   543  			listOptions = append(listOptions, client.MatchingLabels{
   544  				l: defName,
   545  			})
   546  		}
   547  
   548  		objs := v1beta1.DefinitionRevisionList{}
   549  		objs.SetGroupVersionKind(schema.GroupVersionKind{
   550  			Group:   v1beta1.Group,
   551  			Version: v1beta1.Version,
   552  			Kind:    v1beta1.DefinitionRevisionKind,
   553  		})
   554  
   555  		// Search for DefinitionRevisions
   556  		if err := c.List(ctx, &objs, listOptions...); err != nil {
   557  			return nil, errors.Wrapf(err, "failed to list DefinitionRevisions of %s", defName)
   558  		}
   559  
   560  		for _, dr := range objs.Items {
   561  			// Keep only the specified type
   562  			if defType != "" && defType != dr.Spec.DefinitionType {
   563  				continue
   564  			}
   565  			// Only give the revision that the user wants
   566  			if rev != 0 && rev != dr.Spec.Revision {
   567  				continue
   568  			}
   569  			defRev = append(defRev, dr)
   570  		}
   571  	}
   572  
   573  	return defRev, nil
   574  }
   575  
   576  // GetDefinitionFromDefinitionRevision will extract the underlying Definition from a DefinitionRevision.
   577  func GetDefinitionFromDefinitionRevision(rev *v1beta1.DefinitionRevision) (*Definition, error) {
   578  	var def *Definition
   579  	var u map[string]interface{}
   580  	var err error
   581  
   582  	switch rev.Spec.DefinitionType {
   583  	case common.ComponentType:
   584  		u, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&rev.Spec.ComponentDefinition)
   585  	case common.TraitType:
   586  		u, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&rev.Spec.TraitDefinition)
   587  	case common.PolicyType:
   588  		u, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&rev.Spec.PolicyDefinition)
   589  	case common.WorkflowStepType:
   590  		u, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&rev.Spec.WorkflowStepDefinition)
   591  	default:
   592  		return nil, fmt.Errorf("unsupported definition type: %s", rev.Spec.DefinitionType)
   593  	}
   594  
   595  	if err != nil {
   596  		return nil, err
   597  	}
   598  
   599  	def = &Definition{Unstructured: unstructured.Unstructured{Object: u}}
   600  
   601  	return def, nil
   602  }
   603  
   604  // GetDefinitionDefaultSpec returns the default spec of Definition with given kind. This may be implemented with cue in the future.
   605  func GetDefinitionDefaultSpec(kind string) map[string]interface{} {
   606  	switch kind {
   607  	case v1beta1.ComponentDefinitionKind:
   608  		return map[string]interface{}{
   609  			"workload": map[string]interface{}{
   610  				"definition": map[string]interface{}{
   611  					"apiVersion": "<change me> apps/v1",
   612  					"kind":       "<change me> Deployment",
   613  				},
   614  			},
   615  			"schematic": map[string]interface{}{
   616  				"cue": map[string]interface{}{
   617  					"template": "output: {}\nparameter: {}\n",
   618  				},
   619  			},
   620  		}
   621  	case v1beta1.TraitDefinitionKind:
   622  		return map[string]interface{}{
   623  			"appliesToWorkloads": []interface{}{},
   624  			"conflictsWith":      []interface{}{},
   625  			"workloadRefPath":    "",
   626  			"definitionRef":      map[string]interface{}{},
   627  			"podDisruptive":      false,
   628  			"schematic": map[string]interface{}{
   629  				"cue": map[string]interface{}{
   630  					"template": "patch: {}\nparameter: {}\n",
   631  				},
   632  			},
   633  		}
   634  	}
   635  	return map[string]interface{}{}
   636  }
   637  
   638  func formatCUEString(cueString string) (string, error) {
   639  	f, err := parser.ParseFile("-", cueString, parser.ParseComments)
   640  	if err != nil {
   641  		return "", errors.Wrapf(err, "failed to parse file during format cue string")
   642  	}
   643  	n := fix.File(f)
   644  	b, err := format.Node(n, format.Simplify())
   645  	if err != nil {
   646  		return "", errors.Wrapf(err, "failed to format node during formating cue string")
   647  	}
   648  	return string(b), nil
   649  }