component

package
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 16, 2025 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package component provides scaffolding generators for OpenChoreo Component YAML files.

Generator Implementation

Generator uses the ApplyDefaults approach to ensure generated YAML accurately reflects runtime behavior. Default values in scaffolded YAML match exactly what the OpenChoreo component rendering pipeline applies when processing Component definitions.

Output Format

The generated YAML has a consistent, user-friendly structure:

parameters:
  port: <TODO_PORT>           # Required field - user must fill
  name: <TODO_NAME>           # Required field - user must fill
  database:
    host: <TODO_HOST>         # Required nested field

    # Empty object, or customize:
    options: {}               # Required object with all optional children
    # options:
      # timeout: 30           # Commented structure for reference

    # Defaults: Uncomment to customize
    # port: 5432              # Optional field with default

  # Defaults: Uncomment to customize
  # replicas: 3               # Optional field with default
  # cache:                    # Optional object - entirely commented
    # enabled: false

Field Ordering

Fields are sorted for clarity:

  • Required fields first (sorted by type, then alphabetically)
  • Separator comment ("# Defaults: Uncomment to customize")
  • Optional fields last (sorted by type, then alphabetically)

Type order: primitives → arrays → maps → objects (keeps simple fields at top)

Field Rendering Rules

Primitive fields:

  • Required without default → Placeholder: `field: <TODO_FIELD>`
  • Optional with default → Commented: `# field: value`

Object fields:

  • Required with some required children → Expanded normally
  • Required with all optional children → Empty object + commented structure: `field: {}`
  • Optional → Entirely commented out: `# field:`

Array fields:

  • Arrays of objects → Expanded with 2 example items
  • Arrays of primitives → Inline format: `[value1, value2]`
  • Optional arrays with defaults → Commented inline: `# tags: [a, b]`

Map fields (additionalProperties):

  • map<T> → 2 example key-value pairs with primitive values
  • map<CustomType> → 2 example keys with nested object structures
  • map<[]T> → 2 example keys with inline array values

Collection shapes (complex nested types):

  • []map<T> → Array of maps with primitive values
  • []map<CustomType> → Array of maps with nested object structures
  • map<[]T> → Map with primitive array values
  • map<[]CustomType> → Map with arrays of custom type objects

Additional Generated Fields

The generator always includes these hardcoded fields:

  • spec.autoDeploy → Commented out by default: `# autoDeploy: true`
  • spec.workflow.systemParameters → When workflow is included, adds repository configuration (repository.url, repository.revision.branch, repository.appPath)

Structural Comments

Section headers are included when IncludeStructuralComments is true:

  • "Parameters for the ComponentType" (before spec.parameters)
  • "Traits augment the component with additional capabilities" (before spec.traits)
  • "Workflow configuration for building this component" (before spec.workflow)
  • "System parameters for workflow execution" (before spec.workflow.systemParameters)

All structural comments are preceded by an empty line for visual separation.

High-Level Flow

  1. Schema Conversion - Convert OpenChoreo schema (shorthand syntax like "integer | default=8080") to OpenAPI v3 JSON Schema, then to Kubernetes Structural Schema format.

  2. Empty Structure Building - Create a minimal object structure with empty arrays/objects at all levels where defaults need to be resolved.

  3. ApplyDefaults - Use Kubernetes apiextensions-apiserver's defaulting logic to populate defaults. This matches the OpenChoreo component rendering pipeline at runtime.

  4. YAML Generation - Traverse schema and defaulted object together, applying the field ordering and rendering rules described above.

Architecture

The generator processes multiple schema sources:

  • Component parameters (from ComponentType)
  • Trait instances (from each Trait in the Component)
  • Workflow parameters (from ComponentWorkflow)
  • Nested objects and arrays (recursive processing with same rules)

YAMLBuilder handles low-level YAML node manipulation, providing methods for sequences, mappings, inline arrays, and comment handling.

Index

Constants

View Source
const (
	CommentComponentParameters = "\nParameters for the ComponentType"
	CommentTraitsSection       = "\nTraits augment the component with additional capabilities"
	CommentTraitName           = "Trait resource name"
	CommentTraitInstanceName   = "Unique instance name within this Component"
	CommentTraitParameters     = "Parameters for %s trait"
	CommentWorkflowSection     = "\nWorkflow configuration for building this component"
	CommentWorkflowName        = "ComponentWorkflow to use for builds"
)

Structural comment constants for consistent messaging across generated YAML.

Variables

This section is empty.

Functions

func GetFieldTypeOrder

func GetFieldTypeOrder(prop extv1.JSONSchemaProps) int

GetFieldTypeOrder returns the sort order for a field type. Lower numbers come first: primitives (0-2), objects (3), maps (4), arrays (5). This is used to sort fields in a consistent order across generators.

Types

type ArrayFieldStrategy

type ArrayFieldStrategy struct {
	// contains filtered or unexported fields
}

ArrayFieldStrategy handles rendering of array fields.

func NewArrayFieldStrategy

func NewArrayFieldStrategy(renderer *FieldRenderer, nestedRenderer *NestedTypeRenderer) *ArrayFieldStrategy

NewArrayFieldStrategy creates a new array field strategy.

func (*ArrayFieldStrategy) CanHandle

func (s *ArrayFieldStrategy) CanHandle(ctx *FieldContext) bool

CanHandle returns true for array fields.

func (*ArrayFieldStrategy) Render

func (s *ArrayFieldStrategy) Render(b *YAMLBuilder, ctx *FieldContext)

Render generates YAML for an array field.

Rendering rules: - []CustomType: Expanded with 2 example items (nested objects) - []map<T>: Array of maps with primitive values - []map<CustomType>: Array of maps with custom type values - []primitive: Inline format [val1, val2] - With default: Commented - Optional without default: Commented (if IncludeAllFields) or omitted

type FieldContext

type FieldContext struct {
	// Field identification
	Name   string
	Schema *extv1.JSONSchemaProps

	// Field value (from ApplyDefaults)
	Value any

	// Schema metadata
	IsRequired bool
	HasDefault bool

	// Parent schema (for recursive rendering)
	ParentSchema *extv1.JSONSchemaProps

	// Rendering options
	AddSeparator bool // Add "Defaults: Uncomment to customize" separator before this field
	Depth        int  // Nesting depth (0 = top-level parameters)

	// Renderer reference for recursive calls
	Renderer *FieldRenderer
}

FieldContext contains all information needed to render a single field. It encapsulates the rendering decision inputs so strategies can focus on output generation.

func (*FieldContext) BuildFieldComment

func (ctx *FieldContext) BuildFieldComment() string

BuildFieldComment builds the comment string for this field using the renderer's settings.

func (*FieldContext) BuildFieldOptions

func (ctx *FieldContext) BuildFieldOptions(comment string) []FieldOption

BuildFieldOptions creates standard field options for comments and separators.

func (*FieldContext) DetermineRenderMode

func (ctx *FieldContext) DetermineRenderMode() RenderMode

DetermineRenderMode decides whether this field should be rendered as active or commented. The decision is based on: - Required fields without defaults are active - Fields with defaults are commented (showing the default) - Optional fields without defaults are commented (when IncludeAllFields is true)

func (*FieldContext) GetArrayItemSchema

func (ctx *FieldContext) GetArrayItemSchema() *extv1.JSONSchemaProps

GetArrayItemSchema returns the schema for array items, or nil if not an array.

func (*FieldContext) GetMapValueSchema

func (ctx *FieldContext) GetMapValueSchema() *extv1.JSONSchemaProps

GetMapValueSchema returns the schema for map values, or nil if not a map.

func (*FieldContext) GetValueAsMap

func (ctx *FieldContext) GetValueAsMap() map[string]any

GetValueAsMap returns the field value as a map[string]any, or an empty map if conversion fails.

func (*FieldContext) GetValueAsSlice

func (ctx *FieldContext) GetValueAsSlice() []any

GetValueAsSlice returns the field value as []any, or an empty slice if conversion fails.

func (*FieldContext) IsArrayField

func (ctx *FieldContext) IsArrayField() bool

IsArrayField returns true if this field is an array.

func (*FieldContext) IsMapField

func (ctx *FieldContext) IsMapField() bool

IsMapField returns true if this field is a map (object with additionalProperties).

func (*FieldContext) IsObjectField

func (ctx *FieldContext) IsObjectField() bool

IsObjectField returns true if this field is an object with defined properties.

func (*FieldContext) IsPrimitiveField

func (ctx *FieldContext) IsPrimitiveField() bool

IsPrimitiveField returns true if this field is a primitive type.

func (*FieldContext) ShouldOmitOptionalField

func (ctx *FieldContext) ShouldOmitOptionalField() bool

ShouldOmitOptionalField determines if an optional field without a default should be omitted. Returns true when: - Field is optional (not required) - Field has no default value - IncludeAllFields option is false

type FieldOption

type FieldOption func(key, value *yaml.Node)

FieldOption configures comment and style for a field.

func WithFootComment

func WithFootComment(comment string) FieldOption

WithFootComment adds a comment below the field.

func WithHeadComment

func WithHeadComment(comment string) FieldOption

WithHeadComment adds a comment above the field.

func WithLineComment

func WithLineComment(comment string) FieldOption

WithLineComment adds an inline comment after the value.

type FieldRenderer

type FieldRenderer struct {
	// contains filtered or unexported fields
}

FieldRenderer handles YAML field rendering from JSON Schema definitions. It isolates tree-walking/rendering rules from spec/trait/workflow assembly.

Supported field types:

  • Primitives: string, integer, number, boolean (with enum support)
  • Arrays: []T, []CustomType, []map<T>, []map<CustomType>
  • Maps: map<T>, map<CustomType>, map<[]T>, map<[]CustomType>
  • Objects: inline nested objects and custom type references

Options:

  • includeFieldDescriptions: adds schema descriptions and enum alternatives as comments
  • includeAllFields: includes optional fields without defaults (normally omitted)

func NewFieldRenderer

func NewFieldRenderer(includeFieldDescriptions, includeAllFields, includeStructuralComments bool) *FieldRenderer

NewFieldRenderer creates a new FieldRenderer with the given options.

func (*FieldRenderer) RenderFields

func (r *FieldRenderer) RenderFields(b *YAMLBuilder, schema *extv1.JSONSchemaProps, defaultedObj map[string]any, depth int)

RenderFields generates fields from schema and defaulted object. Fields are sorted with required fields first, then optional fields, with a separator comment.

type FieldStrategy

type FieldStrategy interface {
	// CanHandle returns true if this strategy can handle the given field context.
	CanHandle(ctx *FieldContext) bool

	// Render generates YAML for the field using the provided builder and context.
	Render(b *YAMLBuilder, ctx *FieldContext)
}

FieldStrategy defines the interface for rendering different field types. Each strategy handles a specific field type (primitive, object, array, map).

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

Generator generates Component YAML using ApplyDefaults approach. This generator builds an empty object structure, applies Kubernetes defaults, then generates YAML showing defaults as commented and required fields as placeholders.

func NewGenerator

func NewGenerator(
	componentType *corev1alpha1.ComponentType,
	traits []*corev1alpha1.Trait,
	workflow *corev1alpha1.ComponentWorkflow,
	opts *Options,
) (*Generator, error)

NewGenerator creates a new generator instance from CRD objects. This is a convenience constructor that extracts schemas and metadata from CRDs and delegates to NewGeneratorFromSchemas.

func NewGeneratorFromSchemas

func NewGeneratorFromSchemas(
	componentTypeName string,
	workloadType string,
	componentSchema *extv1.JSONSchemaProps,
	traitSchemas map[string]*extv1.JSONSchemaProps,
	workflowName string,
	workflowSchema *extv1.JSONSchemaProps,
	opts *Options,
) (*Generator, error)

NewGeneratorFromSchemas creates a new generator instance directly from JSON schemas. This constructor is useful when you already have processed JSON schemas and don't have access to the full CRD objects.

func (*Generator) Generate

func (g *Generator) Generate() (string, error)

Generate produces the scaffolded Component YAML.

type MapFieldStrategy

type MapFieldStrategy struct {
	// contains filtered or unexported fields
}

MapFieldStrategy handles rendering of map fields (objects with additionalProperties).

func NewMapFieldStrategy

func NewMapFieldStrategy(renderer *FieldRenderer, nestedRenderer *NestedTypeRenderer) *MapFieldStrategy

NewMapFieldStrategy creates a new map field strategy.

func (*MapFieldStrategy) CanHandle

func (s *MapFieldStrategy) CanHandle(ctx *FieldContext) bool

CanHandle returns true for map fields (objects with additionalProperties).

func (*MapFieldStrategy) Render

func (s *MapFieldStrategy) Render(b *YAMLBuilder, ctx *FieldContext)

Render generates YAML for a map field.

Rendering rules: - map<CustomType>: 2 example keys with nested object values - map<[]T>: 2 example keys with inline array values - map<[]CustomType>: 2 example keys with arrays of custom objects - map<T>: 2 example key-value pairs with primitive values - With default: Commented with actual values - Optional without default: Commented (if IncludeAllFields) or omitted

type NestedTypeRenderer

type NestedTypeRenderer struct {
	// contains filtered or unexported fields
}

NestedTypeRenderer handles rendering of complex nested collection types. This includes: - map<CustomType>: maps with object values - map<[]T>: maps with primitive array values - map<[]CustomType>: maps with custom type array values - []map<T>: arrays of maps with primitive values - []map<CustomType>: arrays of maps with custom type values - []CustomType: arrays of custom type objects

func NewNestedTypeRenderer

func NewNestedTypeRenderer(renderer *FieldRenderer) *NestedTypeRenderer

NewNestedTypeRenderer creates a new nested type renderer.

func (*NestedTypeRenderer) RenderArrayOfCustomType

func (r *NestedTypeRenderer) RenderArrayOfCustomType(b *YAMLBuilder, name string, itemSchema *extv1.JSONSchemaProps, values []any, mode RenderMode, opts []FieldOption)

RenderArrayOfCustomType renders []CustomType where items are objects with properties. Generates 2 example array items with nested object structures.

func (*NestedTypeRenderer) RenderArrayOfMaps

func (r *NestedTypeRenderer) RenderArrayOfMaps(b *YAMLBuilder, name string, itemSchema *extv1.JSONSchemaProps, values []any, mode RenderMode, opts []FieldOption)

RenderArrayOfMaps renders []map<T> where items are maps. Generates 2 example array items, each containing map entries.

func (*NestedTypeRenderer) RenderMapOfCustomType

func (r *NestedTypeRenderer) RenderMapOfCustomType(b *YAMLBuilder, name string, valueSchema *extv1.JSONSchemaProps, value any, mode RenderMode, opts []FieldOption)

RenderMapOfCustomType renders map<CustomType> where values are objects with properties. Generates 2 example map entries with nested object structures.

func (*NestedTypeRenderer) RenderMapOfCustomTypeArray

func (r *NestedTypeRenderer) RenderMapOfCustomTypeArray(b *YAMLBuilder, name string, arraySchema *extv1.JSONSchemaProps, mode RenderMode, opts []FieldOption)

RenderMapOfCustomTypeArray renders map<[]CustomType> where values are arrays of custom objects. Generates 2 example map entries, each with 2 example custom type objects.

func (*NestedTypeRenderer) RenderMapOfPrimitiveArray

func (r *NestedTypeRenderer) RenderMapOfPrimitiveArray(b *YAMLBuilder, name string, arraySchema *extv1.JSONSchemaProps, mode RenderMode, opts []FieldOption)

RenderMapOfPrimitiveArray renders map<[]primitive> where values are arrays of primitives. Generates 2 example map entries with inline array values.

func (*NestedTypeRenderer) RenderMapOfPrimitives

func (r *NestedTypeRenderer) RenderMapOfPrimitives(b *YAMLBuilder, name string, valueSchema *extv1.JSONSchemaProps, mode RenderMode, opts []FieldOption)

RenderMapOfPrimitives renders map<primitive> where values are primitive types. Generates 2 example key-value pairs.

type ObjectFieldStrategy

type ObjectFieldStrategy struct {
	// contains filtered or unexported fields
}

ObjectFieldStrategy handles rendering of object fields with defined properties.

func NewObjectFieldStrategy

func NewObjectFieldStrategy(renderer *FieldRenderer) *ObjectFieldStrategy

NewObjectFieldStrategy creates a new object field strategy.

func (*ObjectFieldStrategy) CanHandle

func (s *ObjectFieldStrategy) CanHandle(ctx *FieldContext) bool

CanHandle returns true for objects with defined properties (not maps).

func (*ObjectFieldStrategy) Render

func (s *ObjectFieldStrategy) Render(b *YAMLBuilder, ctx *FieldContext)

Render generates YAML for an object field.

Semantics (applies to both depth == 0 and depth > 0): - NOT required (optional): show entire structure commented out - Required AND all children optional: show "field: {}" + commented children - Required AND some children required: expand normally

type Options

type Options struct {
	// ComponentName is the name for the generated Component
	ComponentName string `yaml:"componentName"`

	// Namespace is the target namespace (organization namespace)
	Namespace string `yaml:"namespace"`

	// ProjectName is the owning project name (required for spec.owner.projectName)
	ProjectName string `yaml:"projectName"`

	// IncludeAllFields includes optional fields (without defaults) as commented examples.
	// When true: all fields shown (required as active, optional as commented)
	// When false: only required fields and fields with defaults are shown
	IncludeAllFields bool `yaml:"includeAllFields"`

	// IncludeFieldDescriptions includes schema-derived comments (descriptions, enum alternatives).
	// When true: field descriptions and "also: x, y" enum hints are shown
	// When false: only field values without schema documentation
	IncludeFieldDescriptions bool `yaml:"includeFieldDescriptions"`

	// IncludeStructuralComments includes section headers and guidance comments.
	// When true: comments like "Parameters for the ComponentType" are shown
	// When false: cleaner output without structural guidance
	IncludeStructuralComments bool `yaml:"includeStructuralComments"`

	// TraitInstanceNames maps trait names to instance names
	// If not provided, uses placeholders like "<trait-instance>"
	TraitInstanceNames map[string]string `yaml:"traitInstanceNames,omitempty"`

	// IncludeWorkflow includes workflow section when workflow is provided
	IncludeWorkflow bool `yaml:"includeWorkflow"`
}

Options configures the component scaffolding generator.

type PrimitiveFieldStrategy

type PrimitiveFieldStrategy struct {
	// contains filtered or unexported fields
}

PrimitiveFieldStrategy handles rendering of primitive fields (string, integer, number, boolean).

func NewPrimitiveFieldStrategy

func NewPrimitiveFieldStrategy(renderer *FieldRenderer) *PrimitiveFieldStrategy

NewPrimitiveFieldStrategy creates a new primitive field strategy.

func (*PrimitiveFieldStrategy) CanHandle

func (s *PrimitiveFieldStrategy) CanHandle(ctx *FieldContext) bool

CanHandle returns true for primitive types: string, integer, number, boolean.

func (*PrimitiveFieldStrategy) Render

func (s *PrimitiveFieldStrategy) Render(b *YAMLBuilder, ctx *FieldContext)

Render generates YAML for a primitive field.

Rendering rules: - Required without default → Placeholder: field: <TODO_FIELD> - Required with default → Commented: # field: value - Optional with default → Commented: # field: value - Optional without default → Commented (if IncludeAllFields) or omitted

type RenderMode

type RenderMode int

RenderMode specifies whether a field should be rendered as active YAML or commented out.

const (
	// RenderActive renders the field as active YAML that will be parsed.
	RenderActive RenderMode = iota
	// RenderCommented renders the field as commented-out YAML for reference.
	RenderCommented
)

type StrategyDispatcher

type StrategyDispatcher struct {
	// contains filtered or unexported fields
}

StrategyDispatcher selects the appropriate strategy for a field and renders it.

func NewStrategyDispatcher

func NewStrategyDispatcher(renderer *FieldRenderer) *StrategyDispatcher

NewStrategyDispatcher creates a new dispatcher with all field strategies.

func (*StrategyDispatcher) Dispatch

func (d *StrategyDispatcher) Dispatch(b *YAMLBuilder, ctx *FieldContext) bool

Dispatch finds the appropriate strategy and renders the field. Returns true if a strategy handled the field, false otherwise.

type YAMLBuilder

type YAMLBuilder struct {
	// contains filtered or unexported fields
}

YAMLBuilder constructs yaml.Node trees with comment support. Commented fields are tracked explicitly via the commentedNodes map and rendered with "# " prefix during encoding.

func NewYAMLBuilder

func NewYAMLBuilder() *YAMLBuilder

NewYAMLBuilder creates a new YAMLBuilder instance.

func (*YAMLBuilder) AddCommentedArray

func (b *YAMLBuilder) AddCommentedArray(key string, items []any, opts ...FieldOption) *YAMLBuilder

AddCommentedArray adds a commented array field with block style. Each array item is rendered on its own line with "# - " prefix for better readability.

func (*YAMLBuilder) AddCommentedField

func (b *YAMLBuilder) AddCommentedField(key, value string, opts ...FieldOption) *YAMLBuilder

AddCommentedField adds a field that will be rendered as a comment (e.g., "# key: value").

func (*YAMLBuilder) AddCommentedInlineArray

func (b *YAMLBuilder) AddCommentedInlineArray(key string, items []any, opts ...FieldOption) *YAMLBuilder

AddCommentedInlineArray adds a commented array field with inline flow style (e.g., # key: [a, b]). Use this for empty arrays or when inline format is preferred.

func (*YAMLBuilder) AddCommentedInlineSequence

func (b *YAMLBuilder) AddCommentedInlineSequence(key string, values []string, opts ...FieldOption) *YAMLBuilder

AddCommentedInlineSequence adds a commented-out inline sequence.

func (*YAMLBuilder) AddField

func (b *YAMLBuilder) AddField(key, value string, opts ...FieldOption) *YAMLBuilder

AddField adds a key-value pair to the current mapping.

func (*YAMLBuilder) AddInlineArray

func (b *YAMLBuilder) AddInlineArray(key string, items []any, opts ...FieldOption) *YAMLBuilder

AddInlineArray adds an array field with inline flow style (e.g., [a, b, c]).

func (*YAMLBuilder) AddInlineSequence

func (b *YAMLBuilder) AddInlineSequence(key string, values []string, opts ...FieldOption) *YAMLBuilder

AddInlineSequence adds a sequence with flow style (inline: [a, b, c]).

func (*YAMLBuilder) AddMapping

func (b *YAMLBuilder) AddMapping(key string, opts ...FieldOption) *YAMLBuilder

AddMapping adds a nested mapping node and returns the builder for chaining.

func (*YAMLBuilder) AddSequence

func (b *YAMLBuilder) AddSequence(key string, opts ...FieldOption) *yaml.Node

AddSequence adds a sequence (array) node and returns the sequence node for adding items.

func (*YAMLBuilder) AddSequenceMapping

func (b *YAMLBuilder) AddSequenceMapping(seq *yaml.Node, fn func(*YAMLBuilder)) *YAMLBuilder

AddSequenceMapping adds a mapping item to a sequence node.

func (*YAMLBuilder) AddSequenceScalar

func (b *YAMLBuilder) AddSequenceScalar(seq *yaml.Node, value string) *YAMLBuilder

AddSequenceScalar adds a scalar item to a sequence node.

func (*YAMLBuilder) Encode

func (b *YAMLBuilder) Encode() (string, error)

Encode returns the YAML string with commented fields rendered as comments.

func (*YAMLBuilder) InCommentedMapping

func (b *YAMLBuilder) InCommentedMapping(key string, fn func(*YAMLBuilder), opts ...FieldOption) *YAMLBuilder

InCommentedMapping creates a commented nested mapping structure. The key and all nested content will be rendered as comments.

func (*YAMLBuilder) InCommentedMappingWithFunc

func (b *YAMLBuilder) InCommentedMappingWithFunc(key string, fn func(*YAMLBuilder), opts ...FieldOption) *YAMLBuilder

InCommentedMappingWithFunc creates a commented nested mapping structure. This is an alias for InCommentedMapping for API consistency with nested type renderer.

func (*YAMLBuilder) InMapping

func (b *YAMLBuilder) InMapping(key string, fn func(*YAMLBuilder), opts ...FieldOption) *YAMLBuilder

InMapping navigates into a mapping to add nested fields, then returns to parent.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL