component

package
v0.8.14 Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Package component provides the generic bundler framework and shared utilities.

Component configuration is defined declaratively in recipes/registry.yaml. This package provides reusable utilities for bundle generation. With the declarative registry, no separate Go packages are needed per component - just add an entry to registry.yaml to configure a new component.

Generic Bundler Framework

The framework provides a declarative approach to bundle generation using:

ComponentConfig: Defines all component-specific settings in one struct:

  • Name and DisplayName for identification
  • ValueOverrideKeys for CLI --set flag mapping
  • Node selector and toleration paths for workload placement
  • DefaultHelmRepository, DefaultHelmChart, DefaultHelmChartVersion for Helm deployment
  • TemplateGetter function for embedded templates
  • Optional CustomManifestFunc for generating additional manifests
  • Optional MetadataExtensions map for custom README template data (preferred over MetadataFunc)

MakeBundle: Generic function that handles all common bundling steps:

  • Extracting component values from recipe input
  • Applying user value overrides from CLI flags
  • Applying node selectors and tolerations to Helm paths
  • Creating directory structure
  • Writing values.yaml with proper YAML headers
  • Calling optional CustomManifestFunc for additional files
  • Generating README from templates
  • Computing checksums

Minimal Bundler Example

Most bundlers can be implemented in ~50 lines using the framework:

var componentConfig = component.ComponentConfig{
    Name:                  "my-operator",
    DisplayName:           "My Operator",
    ValueOverrideKeys:     []string{"myoperator"},
    DefaultHelmRepository: "https://charts.example.com",
    DefaultHelmChart:      "example/my-operator",
    TemplateGetter:        GetTemplate,
}

type Bundler struct {
    *component.BaseBundler
}

func NewBundler(cfg *config.Config) *Bundler {
    return &Bundler{
        BaseBundler: component.NewBaseBundler(cfg, types.BundleTypeMyOperator),
    }
}

func (b *Bundler) Make(ctx context.Context, input recipe.RecipeInput, dir string) (*result.Result, error) {
    return component.MakeBundle(ctx, b.BaseBundler, input, dir, componentConfig)
}

Custom Metadata

Components that need additional template data beyond the default BundleMetadata can provide a MetadataExtensions map in ComponentConfig:

var componentConfig = component.ComponentConfig{
    // ... other fields ...
    MetadataExtensions: map[string]any{
        "InstallCRDs":   true,
        "CustomField":   "custom-value",
    },
}

These extensions are merged into the BundleMetadata.Extensions map and can be accessed in templates via {{ .Script.Extensions.InstallCRDs }}.

For more complex metadata requirements, MetadataFunc is still supported but MetadataExtensions is preferred for simple key-value additions.

Custom Manifest Generation

Components that need to generate additional manifests can provide a CustomManifestFunc:

var componentConfig = component.ComponentConfig{
    // ... other fields ...
    CustomManifestFunc: func(ctx context.Context, b *component.BaseBundler,
        values map[string]any, configMap map[string]string, dir string) ([]string, error) {
        // Generate manifests using b.WriteFile() or b.GenerateFileFromTemplate()
        return []string{"manifests/custom.yaml"}, nil
    },
}

BaseBundler Helper Methods

BaseBundler provides common functionality for file operations:

  • CreateBundleDir: Creates directory structure with proper permissions
  • WriteFile: Writes content with automatic directory creation
  • WriteFileString: Convenience wrapper for string content
  • RenderTemplate: Renders Go templates with error handling
  • GenerateFileFromTemplate: One-step template rendering and file writing
  • GenerateChecksums: Creates checksums.txt with SHA256 hashes
  • CheckContext: Periodic context cancellation checking
  • Finalize: Records timing and result metadata
  • BuildConfigMapFromInput: Creates baseline config map from recipe input

Helper Functions

Utility functions for common operations:

  • GetConfigValue: Safely extracts config map values with defaults
  • GetBundlerVersion: Returns bundler version from config
  • GetRecipeBundlerVersion: Returns recipe version from config
  • MarshalYAMLWithHeader: Serializes values with component header
  • ApplyMapOverrides: Applies dot-notation overrides to nested maps
  • ApplyNodeSelectorOverrides: Applies node selectors to Helm paths
  • ApplyTolerationsOverrides: Applies tolerations to Helm paths
  • GenerateDefaultBundleMetadata: Creates default BundleMetadata struct

Default BundleMetadata

Components using the default metadata get:

  • Namespace, HelmRepository, HelmChart, HelmChartVersion
  • Version (bundler version), RecipeVersion

Access in templates via {{ .Script.Namespace }}, {{ .Script.Version }}, etc.

TestHarness

TestHarness simplifies bundler testing by providing common setup and assertions:

func TestMyBundler_Make(t *testing.T) {
    h := component.NewTestHarness(t, "my-bundler")
    bundler := NewMyBundler(h.Config())
    h.TestMake(bundler)
}

The harness automatically creates temporary directories, generates test recipes, validates output files, and cleans up resources.

Index

Constants

View Source
const (
	StrTrue  = "true"
	StrFalse = "false"
)

Common string constants for boolean values in Helm templates.

Variables

This section is empty.

Functions

func ApplyMapOverrides

func ApplyMapOverrides(target map[string]any, overrides map[string]string) error

ApplyMapOverrides applies overrides to a map[string]any using dot-notation paths. Handles nested maps by traversing the path segments and creating nested maps as needed. Useful for applying --set flag overrides to values.yaml content.

func ApplyNodeSelectorOverrides

func ApplyNodeSelectorOverrides(values map[string]any, nodeSelector map[string]string, paths ...string)

ApplyNodeSelectorOverrides applies node selector overrides to a values map. If nodeSelector is non-empty, it sets or merges with the existing nodeSelector field. The function applies to the specified paths in the values map (e.g., "nodeSelector", "webhook.nodeSelector").

func ApplyTolerationsOverrides

func ApplyTolerationsOverrides(values map[string]any, tolerations []corev1.Toleration, paths ...string)

ApplyTolerationsOverrides applies toleration overrides to a values map. If tolerations is non-empty, it sets or replaces the existing tolerations field. The function applies to the specified paths in the values map (e.g., "tolerations", "webhook.tolerations").

func ApplyValueOverrides

func ApplyValueOverrides(target any, overrides map[string]string) error

ApplyValueOverrides applies overrides to a struct using reflection. Supports dot-notation paths (e.g., "gds.enabled", "driver.version"). Automatically handles type conversion for strings, bools, ints, and nested structs. Returns an error containing all failed overrides instead of stopping at the first failure.

func AssertConfigValue

func AssertConfigValue(t *testing.T, config map[string]string, key, expected string)

AssertConfigValue checks if a config value matches the expected value.

func BoolToString

func BoolToString(b bool) string

BoolToString converts a boolean to "true" or "false" string. Use this for Helm values that require string booleans.

func ComputeChecksum

func ComputeChecksum(content []byte) string

ComputeChecksum computes the SHA256 checksum of the given content.

func ConfigSubtype

func ConfigSubtype(configs map[string]any) measurement.Subtype

ConfigSubtype creates a config subtype with common config data.

func ExtractCustomAnnotations

func ExtractCustomAnnotations(config map[string]string) map[string]string

ExtractCustomAnnotations extracts custom annotations from config map with "annotation_" prefix.

func ExtractCustomLabels

func ExtractCustomLabels(config map[string]string) map[string]string

ExtractCustomLabels extracts custom labels from config map with "label_" prefix.

func GetBundlerVersion

func GetBundlerVersion(m map[string]string) string

GetBundlerVersion retrieves the bundler version from the config map.

func GetConfigValue

func GetConfigValue(config map[string]string, key, defaultValue string) string

GetConfigValue gets a value from config map with a default fallback.

func GetRecipeBundlerVersion

func GetRecipeBundlerVersion(m map[string]string) string

GetRecipeBundlerVersion retrieves the bundler version from the recipe config map.

func ImageSubtype

func ImageSubtype(images map[string]string) measurement.Subtype

ImageSubtype creates an image subtype with common image data.

func MakeBundle

func MakeBundle(ctx context.Context, b *BaseBundler, input recipe.RecipeInput, outputDir string, cfg ComponentConfig) (*result.Result, error)

MakeBundle generates a bundle using the generic bundling logic. This function handles the common steps: creating directories, applying overrides, writing values.yaml, generating README, generating checksums, and finalizing. Configuration is enriched from the component registry when values are not explicitly set in the ComponentConfig.

func MarshalYAML

func MarshalYAML(v any) ([]byte, error)

MarshalYAML serializes a value to YAML format.

func MarshalYAMLWithHeader

func MarshalYAMLWithHeader(v any, header ValuesHeader) ([]byte, error)

MarshalYAMLWithHeader serializes a value to YAML format with a metadata header.

func NodeSelectorToMatchExpressions

func NodeSelectorToMatchExpressions(nodeSelector map[string]string) []map[string]any

NodeSelectorToMatchExpressions converts a map of node selectors to matchExpressions format. This format is used by some CRDs like Skyhook that use label selector syntax. Each key=value pair becomes a matchExpression with operator "In" and single value.

func ParseBoolString

func ParseBoolString(s string) bool

ParseBoolString parses a string boolean value. Returns true if the value is "true" or "1", false otherwise.

func RunStandardBundlerTests

func RunStandardBundlerTests(t *testing.T, cfg StandardBundlerTestConfig)

RunStandardBundlerTests runs all standard tests for a bundler. This includes TestNewBundler, TestBundler_Make, and TestGetTemplate. Use this to reduce test boilerplate in bundler packages.

Example usage:

func TestBundler(t *testing.T) {
    internal.RunStandardBundlerTests(t, internal.StandardBundlerTestConfig{
        ComponentName:     "cert-manager",
        NewBundler:        func(cfg *config.Config) internal.BundlerInterface { return NewBundler(cfg) },
        GetTemplate:       GetTemplate,
        ExpectedTemplates: []string{"README.md"},
        ExpectedFiles:     []string{"values.yaml", "README.md", "checksums.txt"},
    })
}

func SMISubtype

func SMISubtype(data map[string]string) measurement.Subtype

SMISubtype creates an SMI subtype for GPU measurements.

func TestTemplateGetter

func TestTemplateGetter(t *testing.T, getTemplate func(string) (string, bool), expectedTemplates []string)

TestTemplateGetter tests a template getter function.

func TestValidateRecipe

func TestValidateRecipe(t *testing.T, validateFunc func(*recipe.Recipe) error)

TestValidateRecipe is a reusable test for recipe validation.

func TolerationsToPodSpec

func TolerationsToPodSpec(tolerations []corev1.Toleration) []map[string]any

TolerationsToPodSpec converts a slice of corev1.Toleration to a YAML-friendly format. This format matches what Kubernetes expects in pod specs and Helm values.

Types

type BaseBundler

type BaseBundler struct {
	Config *config.Config
	Result *result.Result
}

BaseBundler provides common functionality for bundler implementations. Bundlers can use this to reuse standard operations and reduce boilerplate.

Thread-safety: BaseBundler is safe for use by a single bundler instance. Do not share BaseBundler instances between concurrent bundler executions.

func NewBaseBundler

func NewBaseBundler(cfg *config.Config, bundlerType types.BundleType) *BaseBundler

NewBaseBundler creates a new base bundler helper.

func (*BaseBundler) AddError

func (b *BaseBundler) AddError(err error)

AddError adds a non-fatal error to the result. These errors are collected but do not stop bundle generation.

func (*BaseBundler) BuildBaseConfigMap

func (b *BaseBundler) BuildBaseConfigMap() map[string]string

BuildBaseConfigMap creates a configuration map with common bundler settings. Returns a map containing bundler version. Bundlers can extend this map with their specific values.

func (*BaseBundler) BuildConfigMapFromInput

func (b *BaseBundler) BuildConfigMapFromInput(input interface{ GetVersion() string }) map[string]string

BuildConfigMapFromInput creates a configuration map from a RecipeInput. This includes base config from bundler settings plus recipe version. Use this when working with RecipeResult (new format) instead of Recipe.

func (*BaseBundler) CheckContext

func (b *BaseBundler) CheckContext(ctx context.Context) error

CheckContext checks if the context has been canceled. This should be called periodically during long-running operations to allow for graceful cancellation.

func (*BaseBundler) CreateBundleDir

func (b *BaseBundler) CreateBundleDir(outputDir, bundleName string) (BundleDirectories, error)

CreateBundleDir creates the root bundle directory. Subdirectories (scripts, manifests) are created on-demand when files are written. Returns the bundle directories for easy access to each subdirectory path.

func (*BaseBundler) Finalize

func (b *BaseBundler) Finalize(start time.Time)

Finalize marks the bundler as successful and updates metrics. This should be called at the end of a successful bundle generation. It updates the result duration and marks success. Note: Bundlers should record their own Prometheus metrics after calling this.

func (*BaseBundler) GenerateChecksums

func (b *BaseBundler) GenerateChecksums(ctx context.Context, bundleDir string) error

GenerateChecksums creates a checksums.txt file for all generated files. The checksum file contains SHA256 hashes for verification of bundle integrity. Each line follows the format: "<hash> <relative-path>"

func (*BaseBundler) GenerateFileFromTemplate

func (b *BaseBundler) GenerateFileFromTemplate(ctx context.Context, getTemplate TemplateFunc,
	templateName, outputPath string, data any, perm os.FileMode) error

GenerateFileFromTemplate is a convenience method that combines template retrieval, rendering, and file writing in one call. This reduces boilerplate in bundler implementations by handling the common pattern of: 1. Get template by name 2. Check if template exists 3. Render template with data 4. Write rendered content to file

Example usage:

err := b.GenerateFileFromTemplate(ctx, GetTemplate, "values.yaml",
    filepath.Join(dir, "values.yaml"), data, 0644)

func (*BaseBundler) MakeExecutable

func (b *BaseBundler) MakeExecutable(path string) error

MakeExecutable changes file permissions to make a file executable. This is typically used for shell scripts after writing them.

func (*BaseBundler) RenderAndWriteTemplate

func (b *BaseBundler) RenderAndWriteTemplate(tmplContent, name, outputPath string, data any, perm os.FileMode) error

RenderAndWriteTemplate renders a template and writes it to a file. This combines RenderTemplate and WriteFile for convenience.

func (*BaseBundler) RenderTemplate

func (b *BaseBundler) RenderTemplate(tmplContent, name string, data any) (string, error)

RenderTemplate renders a template with the given data. The template is parsed and executed with the provided data structure. Returns the rendered content as a string.

func (*BaseBundler) WriteFile

func (b *BaseBundler) WriteFile(path string, content []byte, perm os.FileMode) error

WriteFile writes content to a file and tracks it in the result. The file is created with the specified permissions and automatically added to the result's file list with its size. Parent directories are created automatically if they don't exist.

func (*BaseBundler) WriteFileString

func (b *BaseBundler) WriteFileString(path, content string, perm os.FileMode) error

WriteFileString writes string content to a file. This is a convenience wrapper around WriteFile for string content.

type BundleDirectories

type BundleDirectories struct {
	Root      string
	Scripts   string
	Manifests string
}

BundleDirectories holds the standard bundle directory structure.

type BundleMetadata

type BundleMetadata struct {
	// Common fields used by all components
	Namespace        string
	HelmRepository   string
	HelmChart        string
	HelmChartVersion string
	HelmReleaseName  string
	Version          string
	RecipeVersion    string

	// Extensions holds component-specific fields.
	// Templates can access these via {{ .Script.Extensions.FieldName }}
	Extensions map[string]any
}

BundleMetadata contains common metadata used for README and manifest template rendering. This is the default metadata structure used when MetadataFunc is not provided. The Extensions map allows component-specific fields without custom structs.

func GenerateBundleMetadataWithExtensions

func GenerateBundleMetadataWithExtensions(config map[string]string, cfg ComponentConfig) *BundleMetadata

GenerateBundleMetadataWithExtensions creates bundle metadata with custom extensions. This is used when components need additional fields beyond the standard ones.

func GenerateDefaultBundleMetadata

func GenerateDefaultBundleMetadata(config map[string]string, name string, defaultHelmRepo string, defaultHelmChart string) *BundleMetadata

GenerateDefaultBundleMetadata creates default bundle metadata from config map.

type BundlerFactory

type BundlerFactory func(*config.Config) BundlerInterface

BundlerFactory is a function that creates a new bundler instance.

type BundlerInterface

type BundlerInterface interface {
	Make(ctx context.Context, input recipe.RecipeInput, outputDir string) (*result.Result, error)
}

BundlerInterface defines the interface that bundlers must implement for testing.

type ComponentConfig

type ComponentConfig struct {
	// Name is the component identifier used in recipes (e.g., "gpu-operator").
	Name string

	// DisplayName is the human-readable name used in templates (e.g., "GPU Operator").
	DisplayName string

	// ValueOverrideKeys are alternative keys to check for value overrides.
	// The Name is always checked first, then these alternatives (e.g., ["gpuoperator"]).
	ValueOverrideKeys []string

	// SystemNodeSelectorPaths are Helm value paths for system component node selectors.
	// Example: ["operator.nodeSelector", "nfd.nodeSelector"]
	SystemNodeSelectorPaths []string

	// SystemTolerationPaths are Helm value paths for system component tolerations.
	// Example: ["operator.tolerations"]
	SystemTolerationPaths []string

	// AcceleratedNodeSelectorPaths are Helm value paths for GPU node selectors.
	// Example: ["daemonsets.nodeSelector"]
	AcceleratedNodeSelectorPaths []string

	// AcceleratedTolerationPaths are Helm value paths for GPU node tolerations.
	// Example: ["daemonsets.tolerations"]
	AcceleratedTolerationPaths []string

	// DefaultHelmRepository is the default Helm repository URL.
	DefaultHelmRepository string

	// DefaultHelmChart is the chart name (e.g., "nvidia/gpu-operator").
	DefaultHelmChart string

	// DefaultHelmChartVersion is the default chart version if not specified in recipe.
	DefaultHelmChartVersion string

	// TemplateGetter is the function that retrieves templates by name.
	// If nil, TemplateNames will be used with the embedded TemplatesFS.
	TemplateGetter TemplateFunc

	// TemplateNames lists the template files to embed (e.g., ["README.md"]).
	// Used with auto-generated template getter when TemplateGetter is nil.
	TemplateNames []string

	// CustomManifestFunc is an optional function to generate additional manifests.
	// It receives the values map, config map, and output directory.
	// It should return the list of generated file paths, or nil if no manifests were generated.
	CustomManifestFunc CustomManifestFunc

	// MetadataFunc creates component-specific metadata for templates.
	// If nil, the default BundleMetadata is used.
	MetadataFunc MetadataFunc

	// MetadataExtensions provides additional fields for BundleMetadata.
	// These are merged into the Extensions map of the generated metadata.
	// Use this instead of MetadataFunc for simple extensions.
	MetadataExtensions map[string]any
}

ComponentConfig defines the configuration for a bundler component. This struct captures all component-specific settings, allowing the generic MakeBundle function to handle the common bundling logic.

type CustomManifestFunc

type CustomManifestFunc func(ctx context.Context, b *BaseBundler, values map[string]any, configMap map[string]string, dir string) ([]string, error)

CustomManifestFunc is a function type for generating custom manifests. It receives context, base bundler, values map, config map, and output directory. Returns slice of generated file paths (may be nil/empty if no manifests needed).

type MetadataFunc

type MetadataFunc func(configMap map[string]string) any

MetadataFunc is a function type for creating component-specific metadata.

type RecipeBuilder

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

RecipeBuilder helps build test recipes with fluent API.

func NewRecipeBuilder

func NewRecipeBuilder() *RecipeBuilder

NewRecipeBuilder creates a new recipe builder.

func (*RecipeBuilder) Build

func (rb *RecipeBuilder) Build() *recipe.Recipe

Build creates the recipe.

func (*RecipeBuilder) WithGPUMeasurement

func (rb *RecipeBuilder) WithGPUMeasurement(subtypes ...measurement.Subtype) *RecipeBuilder

WithGPUMeasurement adds a GPU measurement with the given subtypes.

func (*RecipeBuilder) WithK8sMeasurement

func (rb *RecipeBuilder) WithK8sMeasurement(subtypes ...measurement.Subtype) *RecipeBuilder

WithK8sMeasurement adds a K8s measurement with the given subtypes.

func (*RecipeBuilder) WithOSMeasurement

func (rb *RecipeBuilder) WithOSMeasurement(subtypes ...measurement.Subtype) *RecipeBuilder

WithOSMeasurement adds an OS measurement with the given subtypes.

func (*RecipeBuilder) WithSystemDMeasurement

func (rb *RecipeBuilder) WithSystemDMeasurement(subtypes ...measurement.Subtype) *RecipeBuilder

WithSystemDMeasurement adds a SystemD measurement with the given subtypes.

type StandardBundlerTestConfig

type StandardBundlerTestConfig struct {
	// ComponentName is the name of the component (e.g., "cert-manager").
	ComponentName string

	// NewBundler creates a new bundler instance.
	NewBundler BundlerFactory

	// GetTemplate is the template getter function.
	GetTemplate TemplateFunc

	// ExpectedTemplates lists templates that should be available (e.g., ["README.md"]).
	ExpectedTemplates []string

	// ExpectedFiles lists files that should be generated (e.g., ["values.yaml", "README.md"]).
	ExpectedFiles []string

	// DefaultOverrides are the default values to use in test recipes.
	DefaultOverrides map[string]any
}

StandardBundlerTestConfig holds configuration for running standard bundler tests.

type TemplateFunc

type TemplateFunc func(name string) (string, bool)

TemplateFunc is a function that retrieves templates by name. Returns the template content and whether it was found.

func NewTemplateGetter

func NewTemplateGetter(templates map[string]string) TemplateFunc

NewTemplateGetter creates a TemplateFunc from a map of template names to content. This is used to simplify template handling in bundlers by converting embedded templates into a standard lookup function.

Example usage:

//go:embed templates/README.md.tmpl
var readmeTemplate string

var GetTemplate = NewTemplateGetter(map[string]string{
    "README.md": readmeTemplate,
})

func StandardTemplates

func StandardTemplates(readmeTemplate string) TemplateFunc

StandardTemplates returns a TemplateFunc for components that only have a README template. This is the most common case and reduces boilerplate further.

Example usage:

//go:embed templates/README.md.tmpl
var readmeTemplate string

var GetTemplate = StandardTemplates(readmeTemplate)

type TestHarness

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

TestHarness provides common testing utilities for bundlers.

func NewTestHarness

func NewTestHarness(t *testing.T, bundlerName string) *TestHarness

NewTestHarness creates a new test harness for a bundler.

func (*TestHarness) AssertFileExists

func (h *TestHarness) AssertFileExists(outputDir, filename string)

AssertFileExists checks if a file exists in the bundle directory.

func (*TestHarness) AssertResult

func (h *TestHarness) AssertResult(result *result.Result, outputDir string)

AssertResult performs standard assertions on a bundler result.

func (*TestHarness) TestMake

func (h *TestHarness) TestMake(bundler BundlerInterface)

TestMake tests the Make method of a bundler with standard assertions.

func (*TestHarness) WithExpectedFiles

func (h *TestHarness) WithExpectedFiles(files []string) *TestHarness

WithExpectedFiles sets the list of files expected to be generated.

func (*TestHarness) WithRecipeBuilder

func (h *TestHarness) WithRecipeBuilder(builder func() *RecipeBuilder) *TestHarness

WithRecipeBuilder sets a custom recipe builder function.

type ValuesHeader

type ValuesHeader struct {
	ComponentName  string
	BundlerVersion string
	RecipeVersion  string
}

ValuesHeader contains metadata for values.yaml file headers.

Jump to

Keyboard shortcuts

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