bundle

package
v0.38.0 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: AGPL-3.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// BundleDefaultsFileName is the name of the optional bundle-level defaults file.
	BundleDefaultsFileName = "defaults.uds.hcl"
	// MaxConcurrency is the upper bound for parallel package deploys within a level.
	MaxConcurrency = 25
)
View Source
const BundleFileName = "bundle.uds.hcl"

BundleFileName is the name of the bundle definition file.

Variables

View Source
var (
	ErrConfigRequired             = errors.New("config is required")
	ErrConfigGlobalRequired       = errors.New("config.Global is required")
	ErrConfigOptionsRequired      = errors.New("config.Options is required")
	ErrInvalidConcurrency         = errors.New("invalid concurrency")
	ErrInvalidTemporaryDirectory  = errors.New("invalid temporary directory")
	ErrReadBundleFile             = errors.New("cannot read bundle file")
	ErrParseHCL                   = errors.New("failed to parse HCL")
	ErrDecodeBundle               = errors.New("failed to decode bundle")
	ErrDecodePackageMetadata      = errors.New("failed to decode package metadata")
	ErrDecodePackageDependencies  = errors.New("failed to decode package dependencies")
	ErrReadFile                   = errors.New("reading file")
	ErrInvalidFile                = errors.New("invalid file")
	ErrUnexpectedHCLBody          = errors.New("unexpected HCL body type")
	ErrFileFunctionUnavailable    = errors.New("file function is unavailable")
	ErrReadDefaultsFile           = errors.New("cannot read defaults file")
	ErrExtractLocals              = errors.New("failed to extract locals")
	ErrEvaluateHCLExpression      = errors.New("failed to evaluate HCL expression")
	ErrReadPackageDependencies    = errors.New("failed to read depends_on")
	ErrInvalidPackageDependencies = errors.New("invalid package dependencies")
	ErrInvalidPackageReference    = errors.New("invalid package reference")
	ErrReadConfigFile             = errors.New("cannot read config file")
	ErrParseConfig                = errors.New("failed to parse config HCL")
	ErrDecodeConfig               = errors.New("failed to decode config")
	ErrReadVariables              = errors.New("failed to read variables")
	ErrEvaluateVariables          = errors.New("failed to evaluate variables")
	ErrConvertVariables           = errors.New("failed to convert variables")
	ErrInvalidVariables           = errors.New("invalid variables")
	ErrUnsupportedVariableType    = errors.New("unsupported variable type")
	ErrParseDefaults              = errors.New("failed to parse defaults HCL")
	ErrInvalidDefaults            = errors.New("invalid defaults file")
	ErrUnknownPackageDependency   = errors.New("unknown package dependency")
	ErrDependencyCycle            = errors.New("dependency cycle detected")
	ErrPackageNotInBundle         = errors.New("package is not in the bundle")
	ErrUnknownPackages            = errors.New("unknown packages")
	ErrBuildDependencyGraph       = errors.New("failed to build dependency graph")
)

Functions

func AdjacentDefaultsPath

func AdjacentDefaultsPath(bundleDir string) (string, error)

AdjacentDefaultsPath returns the optional defaults file next to bundleDir. Missing files are represented by an empty path; other filesystem errors are returned to the caller.

func DeployViolations

func DeployViolations(ctx context.Context, streams iostreams.IOStreams, b *spec.UDSBundle, packageNames []string) (map[string][]string, error)

DeployViolations returns each selected package's dependencies that are not selected. An empty result means the deployment is safe.

func FilterLevels

func FilterLevels(levels [][]*spec.Package, filterNames []string) ([][]*spec.Package, error)

FilterLevels keeps only packages whose names appear in filterNames, preserving the topological level structure. When filterNames is empty, all levels are returned unchanged. Empty levels (after filtering) are dropped. Duplicate names are deduped. Returns an error if any requested name is not in the bundle.

func MaterializeDefaultsFile

func MaterializeDefaultsFile(path string) ([]byte, error)

MaterializeDefaultsFile resolves file() calls in a defaults file.

func RemovalViolations

func RemovalViolations(ctx context.Context, streams iostreams.IOStreams, b *spec.UDSBundle, packageNames []string) (map[string][]string, error)

RemovalViolations returns packages that would retain a dependency on each selected package after removal. An empty result means the removal is safe.

func ResolveBundlePath

func ResolveBundlePath(ref string) string

ResolveBundlePath resolves a user-provided bundle reference to the path of the bundle.uds.hcl file. If ref is a directory, the bundle file inside it is returned; otherwise ref is returned as-is.

Assumes the path has already been validated with ValidateBundlePath.

func ValidateConfig

func ValidateConfig(cfg *UDSBundleConfig) error

ValidateConfig is the single entry point for validating a fully-resolved UDSBundleConfig. It runs nil-checks on the structure and then delegates field-level checks to focused sub-validators.

Call this once at the boundary where config is produced (e.g. from the ConfigResolver). Downstream consumers should trust the config and skip re-validation.

func ValidatePackageNames

func ValidatePackageNames(names []string, packages []spec.Package) error

ValidatePackageNames checks that all names exist in the bundle's package list. The error message names the unknown packages and lists all available packages.

Types

type ConfigOptions

type ConfigOptions struct {
	LogLevel      string `hcl:"log_level,optional"`
	Architecture  string `hcl:"architecture,optional"`
	PlainHTTP     bool   `hcl:"plain_http,optional"`
	SkipTLSVerify bool   `hcl:"skip_tls_verify,optional"`
	TmpDir        string `hcl:"tmp_dir,optional"`
	Concurrency   int    `hcl:"concurrency,optional"`
}

ConfigOptions holds bundle-component CLI options from the options block. All fields are optional; unset values use the operation defaults.

type CyclicLocalDependencyError

type CyclicLocalDependencyError struct {
	Cycle []string
}

CyclicLocalDependencyError occurs when 2 or more variables reference each other resulting in a dependency cycle

func (*CyclicLocalDependencyError) Error

type DAG

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

DAG represents a directed acyclic graph of package dependencies. Edges point from a package to the packages it depends on.

func BuildDependencyGraph

func BuildDependencyGraph(ctx context.Context, streams iostreams.IOStreams, bundle *spec.UDSBundle) (*DAG, error)

BuildDependencyGraph constructs a DAG from bundle packages using hcl.Traversal. Each package is represented as a traversal "package.<name>", and dependencies are taken from the already-parsed PackageRef values in the Package struct. The graph is validated for missing references and cycles before being returned.

func (*DAG) Level

func (d *DAG) Level(name string) int

Level returns the deployment level (wave) for a specific package. Level 0 packages have no dependencies, level 1 depends only on level 0, etc. Returns -1 if the package is not found or if levels cannot be computed.

func (*DAG) TopologicalLevels

func (d *DAG) TopologicalLevels() ([][]*spec.Package, error)

TopologicalLevels returns packages grouped by deployment "waves" or "levels". Packages within the same level have no dependencies on each other and CAN be deployed in parallel. Levels must be deployed sequentially (level 0 before level 1, etc.).

Uses Kahn's algorithm with level tracking. In-degree counts the number of unmet dependencies for each package. Packages with in-degree 0 form the current level and are "removed" from the graph by decrementing dependents' in-degrees.

Example for diamond pattern (A has no deps, B and C depend on A, D depends on B and C):

Level 0: [A]        - deploy first, no dependencies
Level 1: [B, C]     - can deploy B and C in parallel after A completes
Level 2: [D]        - deploy after both B and C complete

func (*DAG) TopologicalSort

func (d *DAG) TopologicalSort() ([]*spec.Package, error)

TopologicalSort returns packages in deployment order (dependencies first). It flattens the levels from TopologicalLevels into a single slice.

func (*DAG) Traversal

func (d *DAG) Traversal(name string) (hcl.Traversal, bool)

Traversal returns the hcl.Traversal for a package by name. This can be used for enhanced error messages with HCL source locations.

type DuplicateLocalError

type DuplicateLocalError struct {
	Name      string
	Existing  hcl.Range
	Duplicate hcl.Range
}

DuplicateLocalError occurs when a local variable is declared twice in a single evaluation context

func (*DuplicateLocalError) Error

func (e *DuplicateLocalError) Error() string

type EmptyParameterError

type EmptyParameterError struct{ Name string }

func (EmptyParameterError) Error

func (e EmptyParameterError) Error() string

type HCLParser

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

HCLParser implements Parser for HCL bundle definitions. Its architecture is exposed to bundle expressions as ${sys.arch}; an empty architecture uses the runtime default. Diagnostics are written to streams.

func NewHCLParser

func NewHCLParser(arch string, streams iostreams.IOStreams) *HCLParser

NewHCLParser creates a new HCLParser. arch is the effective target architecture exposed as ${sys.arch}; pass an empty string to use runtime.GOARCH. streams carries the leveled logger used for parse diagnostics.

func (*HCLParser) ParseAndMaterializeBundleFile

func (p *HCLParser) ParseAndMaterializeBundleFile(ctx context.Context, path string) (*spec.UDSBundle, []byte, error)

ParseAndMaterializeBundleFile reads a source bundle once, using those bytes both for runtime evaluation and the self-contained artifact representation.

func (*HCLParser) ParseBundleBytes

func (p *HCLParser) ParseBundleBytes(ctx context.Context, src []byte) (*spec.UDSBundle, error)

ParseBundleBytes parses HCL bundle content from an in-memory byte slice. ctx is accepted for cancellation/propagation; HCL parsing does not use it, and diagnostics are written via p.streams.

func (*HCLParser) ParseBundleConfig

func (p *HCLParser) ParseBundleConfig(_ context.Context, filePath string) (*UDSBundleConfig, error)

ParseBundleConfig reads and parses a config.uds.hcl file. It uses gohcl.DecodeBody to decode the options block via HCL struct tags on UDSBundleConfig, and hcl:",remain" to capture the free-form variables attribute which is then manually extracted and converted from cty.Value to Variables. The context parameter is currently unused as none of the HCL parsing methods supports cancellation.

func (*HCLParser) ParseBundleFile

func (p *HCLParser) ParseBundleFile(ctx context.Context, filePath string) (*spec.UDSBundle, error)

ParseBundleFile reads and parses an HCL bundle file with locals support. ctx is accepted for cancellation/propagation; HCL parsing does not use it, and diagnostics are written via p.streams.

type KeylessVerification

type KeylessVerification struct {
	CertificateIdentity         string `hcl:"certificate_identity,optional"`
	CertificateIdentityRegexp   string `hcl:"certificate_identity_regexp,optional"`
	CertificateOIDCIssuer       string `hcl:"certificate_oidc_issuer,optional"`
	CertificateOIDCIssuerRegexp string `hcl:"certificate_oidc_issuer_regexp,optional"`
	TrustedRoot                 string `hcl:"trusted_root,optional"`
}

KeylessVerification holds keyless trust constraints from config.uds.hcl.

type LocalEvaluationError

type LocalEvaluationError struct {
	Name  string
	Diags hcl.Diagnostics
}

LocalEvaluationError occurs when a local variable is unable to be evaluated such as a call to file() with a file that does not exist

func (*LocalEvaluationError) Error

func (e *LocalEvaluationError) Error() string

func (*LocalEvaluationError) Unwrap

func (e *LocalEvaluationError) Unwrap() error

type LocalsAttributeError

type LocalsAttributeError struct {
	Diags hcl.Diagnostics
}

LocalsAttributeError occurs when there is a syntactical error and a local variable is unable to be parsed

func (*LocalsAttributeError) Error

func (e *LocalsAttributeError) Error() string

func (*LocalsAttributeError) Unwrap

func (e *LocalsAttributeError) Unwrap() error

type LocalsBlockError

type LocalsBlockError struct {
	Diags hcl.Diagnostics
}

LocalsBlockError is a generic error when unable to parse the locals blocks

func (*LocalsBlockError) Error

func (e *LocalsBlockError) Error() string

func (*LocalsBlockError) Unwrap

func (e *LocalsBlockError) Unwrap() error

type PackageTraversal

type PackageTraversal struct {
	Package   *spec.Package
	Traversal hcl.Traversal
}

PackageTraversal wraps a package with its HCL traversal. Bundle dependency expressions are represented as traversals so dependency resolution retains type and source-location context.

type Parser

type Parser interface {
	// ParseBundleFile reads and parses a bundle.uds.hcl file with locals support.
	ParseBundleFile(ctx context.Context, filePath string) (*spec.UDSBundle, error)
	// ParseBundleBytes parses in-memory bundle HCL without permitting file().
	ParseBundleBytes(ctx context.Context, src []byte) (*spec.UDSBundle, error)
	// ParseBundleConfig reads and parses a config.uds.hcl file.
	ParseBundleConfig(ctx context.Context, filePath string) (*UDSBundleConfig, error)
}

Parser defines the interface for parsing bundle definitions and configuration files.

type SignatureVerification

type SignatureVerification struct {
	PublicKey string               `hcl:"public_key,optional"`
	Keyless   *KeylessVerification `hcl:"keyless,block"`
}

SignatureVerification holds consumer-owned bundle signature trust material.

type UDSBundleConfig

type UDSBundleConfig struct {
	Options               *ConfigOptions         `hcl:"options,block"`
	SignatureVerification *SignatureVerification `hcl:"signature_verification,block"`
	Variables             Variables              // populated after decode from Remain
	Remain                hcl.Body               `hcl:",remain"` // captures variables and any other unstructured top-level attributes
}

UDSBundleConfig represents the parsed content of config.uds.hcl. Variables are decoded from the remaining free-form HCL body after structured blocks.

type UndefinedLocalDependencyError

type UndefinedLocalDependencyError struct {
	Name string
	// Position the missing variable was referenced at
	Position hcl.Range
}

UndefinedLocalDependencyError occurs when an undefined local variable is referenced

func (*UndefinedLocalDependencyError) Error

type Variables

type Variables map[string]any

Variables contains user-defined configuration values. Nested objects are represented as Variables and list-like values as []any.

func MergeVariables

func MergeVariables(base, overrides Variables) Variables

MergeVariables deep-merges variables from overrides into base, returning a new Variables map. The base is deep-copied so callers can mutate the result without affecting their inputs. Nested Variables are deep-merged; everything else (scalars, lists) is replaced wholesale, matching Helm overlay conventions.

func ParseDefaults

func ParseDefaults(_ context.Context, path string) (Variables, error)

ParseDefaults reads a defaults file from disk and validates it. A valid defaults file contains at most one top-level attribute named "variables" and no blocks. Returns the parsed Variables, or nil if the file has no variables. The context parameter is currently unused as none of the HCL parsing methods supports cancellation.

func ParseDefaultsBytes

func ParseDefaultsBytes(_ context.Context, src []byte) (Variables, error)

ParseDefaultsBytes parses defaults HCL without enabling file-backed expressions.

func (Variables) Flatten

func (v Variables) Flatten() map[string]string

Flatten returns the top-level values as an uppercased string map suitable for Zarf's SetVariables passthrough. Only scalar types (string, float64, bool) are included; non-scalar values (lists, nested Variables, other types) are silently skipped and must be passed to Zarf via values_files instead.

Complex types are excluded because values_files is the proper channel for them. Templates already handle nested Variables natively, and skipping non-scalars steers authors toward the values_files path rather than introducing a footgun where chart authors would need to JSON-decode Zarf var tokens.

Example:

Input:  Variables{"domain": "uds.dev", "ports": []any{1.0, 2.0}, "nested": Variables{"key": "val"}}
Output: map[string]string{"DOMAIN": "uds.dev"}  (ports and nested skipped)

Jump to

Keyboard shortcuts

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