Files
kubevela/pkg/definition/defkit/resource.go
Ayush Kumar 3f7ad2ba95 Feat: defkit comp def discrepancies (#7048)
* Feat: add MapVariant operation and support for OneOf parameters with default variant

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance string parameter output to include optional prefix in CUE generation

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add ConditionalOrFieldRef for fallback handling and support inline array values

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: update CUE generation to support inline arrays with conditional wrapping

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: update CUE generation to support inline arrays with conditional wrapping

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add support for compound optional fields and enhance array builder with guarded filtering

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add comprehensive tests for ArrayBuilder functionality

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: implement field grouping in StatusBuilder for consolidated CUE output

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance CUE decomposition to support condValues and improve filtering logic

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: add metadata labels to ComponentDefinition and update CUE generation

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: clean up comments and formatting in cuegen and param files

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance CUE generation, trait definitions, and PatchContainer logic

CUE Generation:
- Simplify condition decomposition logic and rename GetDirective method
- Add condition decomposition and lifting logic to improve generated CUE output
- Refactor cueTypeForParamType to use a standalone cueTypeStr function for reusability

Collections:
- Enhance MapVariant operation to merge variant mappings and preserve non-matching items

Trait Definitions:
- Enhance TraitDefinition and PatchContainerConfig with new attributes (MultiContainerCheckField, MultiContainerErrMsg)
- Update emission logic in TraitCUEGenerator for multi-container support

PatchContainer:
- Update error messages to use camelCase for consistency with KubeVela conventions

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance test descriptions for clarity and accuracy in array_builder, collections, and status tests

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance handling of optional fields in collections and improve test descriptions for clarity

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: improve ConditionalOrFieldRef tests for clarity and accuracy in handling primary and fallback fields

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: enhance test descriptions for clarity and accuracy in array_builder and expr tests

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* Feat: improve formatting of test data in collections tests for better readability

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

---------

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2026-03-01 21:29:18 -08:00

256 lines
7.5 KiB
Go

/*
Copyright 2025 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 defkit
// ResourceOp represents an operation recorded during resource building.
type ResourceOp interface {
resourceOp()
}
// SetOp represents a Set operation on a resource field.
type SetOp struct {
path string
value Value
}
func (s *SetOp) resourceOp() {}
// Path returns the field path.
func (s *SetOp) Path() string { return s.path }
// Value returns the value being set.
func (s *SetOp) Value() Value { return s.value }
// SetIfOp represents a conditional Set operation.
type SetIfOp struct {
path string
value Value
cond Condition
}
func (s *SetIfOp) resourceOp() {}
// Path returns the field path.
func (s *SetIfOp) Path() string { return s.path }
// Value returns the value being set.
func (s *SetIfOp) Value() Value { return s.value }
// Cond returns the condition for the operation.
func (s *SetIfOp) Cond() Condition { return s.cond }
// IfBlock represents a conditional block of operations.
type IfBlock struct {
cond Condition
ops []ResourceOp
}
func (i *IfBlock) resourceOp() {}
// Cond returns the block condition.
func (i *IfBlock) Cond() Condition { return i.cond }
// Ops returns the operations within the block.
func (i *IfBlock) Ops() []ResourceOp { return i.ops }
// SpreadIfOp represents a conditional spread operation inside a block.
// Instead of conditionally setting a whole field, it spreads a value
// inside a block when a condition is met.
// Example: labels: { if param.labels != _|_ { parameter.labels } ... }
type SpreadIfOp struct {
path string // parent path (e.g., "spec.template.metadata.labels")
value Value // value to spread
cond Condition // condition for spreading
}
func (s *SpreadIfOp) resourceOp() {}
// Path returns the parent field path.
func (s *SpreadIfOp) Path() string { return s.path }
// Value returns the value being spread.
func (s *SpreadIfOp) Value() Value { return s.value }
// Cond returns the condition for the spread.
func (s *SpreadIfOp) Cond() Condition { return s.cond }
// VersionConditional represents a conditional apiVersion setting.
type VersionConditional struct {
Condition Condition
ApiVersion string
}
// Resource represents a Kubernetes resource being built.
type Resource struct {
apiVersion string
kind string
ops []ResourceOp
currentIf *IfBlock // tracks current If block being built
outputCondition Condition // condition for conditional output (used by OutputsIf)
versionConditionals []VersionConditional
}
// NewResource creates a new resource builder with API version and kind.
func NewResource(apiVersion, kind string) *Resource {
return &Resource{
apiVersion: apiVersion,
kind: kind,
ops: make([]ResourceOp, 0),
}
}
// NewResourceWithConditionalVersion creates a resource with conditional apiVersion.
// This is used when the apiVersion depends on runtime conditions like cluster version.
//
// Example:
//
// vela := defkit.VelaCtx()
// cronjob := defkit.NewResourceWithConditionalVersion("CronJob").
// VersionIf(Lt(vela.ClusterVersion().Minor(), 25), "batch/v1beta1").
// VersionIf(Gte(vela.ClusterVersion().Minor(), 25), "batch/v1")
func NewResourceWithConditionalVersion(kind string) *Resource {
return &Resource{
kind: kind,
ops: make([]ResourceOp, 0),
versionConditionals: make([]VersionConditional, 0),
}
}
// VersionIf adds a conditional apiVersion to the resource.
// When the condition is true, this apiVersion will be used.
func (r *Resource) VersionIf(cond Condition, apiVersion string) *Resource {
r.versionConditionals = append(r.versionConditionals, VersionConditional{
Condition: cond,
ApiVersion: apiVersion,
})
return r
}
// HasVersionConditionals returns true if the resource has conditional apiVersion.
func (r *Resource) HasVersionConditionals() bool {
return len(r.versionConditionals) > 0
}
// VersionConditionals returns all conditional apiVersion settings.
func (r *Resource) VersionConditionals() []VersionConditional {
return r.versionConditionals
}
// APIVersion returns the resource's API version.
func (r *Resource) APIVersion() string { return r.apiVersion }
// Kind returns the resource's kind.
func (r *Resource) Kind() string { return r.kind }
// Ops returns all recorded operations.
func (r *Resource) Ops() []ResourceOp { return r.ops }
// Set records a field assignment operation.
func (r *Resource) Set(path string, value Value) *Resource {
op := &SetOp{path: path, value: value}
if r.currentIf != nil {
r.currentIf.ops = append(r.currentIf.ops, op)
} else {
r.ops = append(r.ops, op)
}
return r
}
// SetIf records a conditional field assignment.
func (r *Resource) SetIf(cond Condition, path string, value Value) *Resource {
op := &SetIfOp{path: path, value: value, cond: cond}
if r.currentIf != nil {
r.currentIf.ops = append(r.currentIf.ops, op)
} else {
r.ops = append(r.ops, op)
}
return r
}
// SpreadIf records a conditional spread operation inside a struct block.
// Unlike SetIf which wraps the entire field in a conditional, SpreadIf spreads
// the value inside the block only when the condition is true, while the block
// itself always exists with its other fields.
//
// Example usage:
//
// Set("spec.template.metadata.labels[app.oam.dev/name]", vela.AppName()).
// SpreadIf(labels.IsSet(), "spec.template.metadata.labels", labels)
//
// Generates:
//
// labels: {
// "app.oam.dev/name": context.appName
// if parameter.labels != _|_ {
// parameter.labels
// }
// }
func (r *Resource) SpreadIf(cond Condition, path string, value Value) *Resource {
op := &SpreadIfOp{path: path, value: value, cond: cond}
if r.currentIf != nil {
r.currentIf.ops = append(r.currentIf.ops, op)
} else {
r.ops = append(r.ops, op)
}
return r
}
// If starts a conditional block. Operations until EndIf are conditional.
func (r *Resource) If(cond Condition) *Resource {
r.currentIf = &IfBlock{
cond: cond,
ops: make([]ResourceOp, 0),
}
return r
}
// EndIf ends the current conditional block.
func (r *Resource) EndIf() *Resource {
if r.currentIf != nil {
r.ops = append(r.ops, r.currentIf)
r.currentIf = nil
}
return r
}
// Directive records a CUE directive annotation on a field path.
// The directive string should be like "patchKey=ip" and will be rendered as // +patchKey=ip.
func (r *Resource) Directive(path string, directive string) *Resource {
op := &DirectiveOp{path: path, directive: directive}
if r.currentIf != nil {
r.currentIf.ops = append(r.currentIf.ops, op)
} else {
r.ops = append(r.ops, op)
}
return r
}
// DirectiveOp records a CUE directive annotation on a field path.
// The directive string (e.g. "patchKey=ip") is rendered as // +patchKey=ip.
type DirectiveOp struct {
path string
directive string
}
func (d *DirectiveOp) resourceOp() {}
// Path returns the field path.
func (d *DirectiveOp) Path() string { return d.path }
// GetDirective returns the directive string.
func (d *DirectiveOp) GetDirective() string { return d.directive }