bundleinfo

package
v0.22.0 Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package bundleinfo defines bundle-info.yaml, the bundle-root build record written at the root of every generated deployment bundle.

It answers three questions a bundle cannot answer for itself: which deployer built it, which aicr binary built it, and which release landed in which directory. Write is called unconditionally by all five deployers (helm, argocd, argocd-helm, flux, helmfile) — there is no flag that suppresses it, so a bundle's absence of bundle-info.yaml is itself a meaningful signal: the bundle predates this artifact.

The record carries no wall-clock timestamp. bundle-info.yaml feeds checksums.txt, which is the subject of the bundle attestation, so a timestamp field would make every bundle irreproducible: the same recipe and settings would never hash to the same bytes twice.

It indexes rather than duplicates. recipe.yaml sits beside it and remains the source of truth for component inventory; bundle-info.yaml does not restate what the recipe resolved to, only which deployer and settings produced this bundle and where each release ended up on disk.

Index

Constants

View Source
const FileName = "bundle-info.yaml"

FileName is the on-disk name at the bundle root. Exported so consumers reference the same name.

Not dot-prefixed on purpose: Helm's default ignore rules drop "."-prefixed files, so a hidden name would be silently absent from the packaged argocd-helm chart while recipe.yaml survives.

Variables

This section is empty.

Functions

func Write

func Write(ctx context.Context, dir string, info *BundleInfo) (int64, error)

Write serializes info deterministically to dir/bundle-info.yaml and returns the byte count. Mode 0600 matches the bundler's recipe.yaml write.

It overwrites info's APIVersion and Kind with the values this package emits, which is what keeps the header out of a caller's hands.

Types

type Build

type Build struct {
	Deployer string   `json:"deployer" yaml:"deployer"`
	Recipe   Recipe   `json:"recipe" yaml:"recipe"`
	Settings Settings `json:"settings" yaml:"settings"`
}

Build records how the bundle was produced.

type BundleInfo

type BundleInfo struct {
	APIVersion string   `json:"apiVersion" yaml:"apiVersion"`
	Kind       string   `json:"kind" yaml:"kind"`
	Metadata   Metadata `json:"metadata" yaml:"metadata"`
	Build      Build    `json:"build" yaml:"build"`
	Layout     Layout   `json:"layout" yaml:"layout"`
}

BundleInfo is the bundle-root build record. Every bundle carries one, for every deployer, unconditionally.

It answers three questions a bundle could not answer for itself: which deployer produced it, which aicr binary produced it, and which release landed in which directory. It deliberately does NOT restate component inventory — recipe.yaml sits beside it and is the source of truth for what the recipe resolved to.

func Read

func Read(ctx context.Context, dir string) (*BundleInfo, error)

Read loads and validates dir/bundle-info.yaml.

Fails closed in every direction: the filesystem entry, the size, the header, the required fields, any content trailing the first YAML document, and every path the record carries. A bundle produced before this artifact shipped has no file at all, and that returns ErrCodeNotFound naming the reason: a consumer must treat it as a real state and ask the operator for the deployer, never fall back to guessing one from the directory layout.

type Layout

type Layout struct {
	Entrypoint string    `json:"entrypoint" yaml:"entrypoint"`
	Provenance string    `json:"provenance,omitempty" yaml:"provenance,omitempty"`
	Releases   []Release `json:"releases" yaml:"releases"`
}

Layout indexes what the bundle emitted and where, so automation reads one key instead of branching on the deployer. Entrypoint is the file a consumer invokes or applies: deploy.sh, helmfile.yaml, app-of-apps.yaml, Chart.yaml, or kustomization.yaml.

type Metadata

type Metadata struct {
	Version string `json:"version,omitempty" yaml:"version,omitempty"`
}

Metadata carries the identity of the build itself. Version is the aicr binary that ran `bundle`, which is not necessarily the one that resolved the recipe — see Recipe.Version.

There is deliberately no timestamp. The record feeds checksums.txt, which is the subject of the bundle attestation, so a wall-clock field would make every bundle irreproducible.

type NodeScheduling

type NodeScheduling struct {
	System      *Scheduling `json:"system,omitempty" yaml:"system,omitempty"`
	Accelerated *Scheduling `json:"accelerated,omitempty" yaml:"accelerated,omitempty"`
}

NodeScheduling groups the two placement classes the bundler pins.

type Recipe

type Recipe struct {
	Path    string `json:"path" yaml:"path"`
	Digest  string `json:"digest" yaml:"digest"`
	Version string `json:"version,omitempty" yaml:"version,omitempty"`
}

Recipe binds this record to the recipe.yaml beside it. Version is the aicr binary that resolved that recipe, read from its metadata.version; it is stamped by the recipe builder and is not restamped at bundle time.

type Release

type Release struct {
	Name      string `json:"name" yaml:"name"`
	Component string `json:"component" yaml:"component"`
	Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
	Path      string `json:"path" yaml:"path"`
	Manifest  string `json:"manifest,omitempty" yaml:"manifest,omitempty"`
}

Release is one Helm release the bundle installs.

The index is keyed on releases rather than components because the injected -pre, -post and -readiness folders are releases with no component of their own; each names its parent in Component.

Component is whatever the deployer reports, and it is undefined for a recipe that declares a component whose name ends in one of those reserved suffixes alongside the matching base name: the deployers disagree on whether such a folder is its own component or an injected one, so the same recipe yields different values and a consumer cannot tell which it got. No name in the shipped registry ends in a reserved suffix, which is why an aicr.run/v1 schema can carry the ambiguity: resolving it is a reserved additive change, and a later minor may add an explicit discriminator separating a primary release from an injected one. Until then a consumer must not treat the Component value for that collision as stable.

Releases is emitted in deployment order and that ordering is normative: consumers read sequence from list position. There is deliberately no ordinal field — it would restate list position, restate the NNN- path prefix on the four deployers that have one, and imply a sequencing flux does not perform (flux orders by dependsOn, a graph rather than a line).

type Scheduling

type Scheduling struct {
	Selector    map[string]string `json:"selector,omitempty" yaml:"selector,omitempty"`
	Tolerations []Toleration      `json:"tolerations,omitempty" yaml:"tolerations,omitempty"`
}

Scheduling is one placement class.

type Settings

type Settings struct {
	Checksums          bool            `json:"checksums" yaml:"checksums"`
	Attested           bool            `json:"attested" yaml:"attested"`
	VendorCharts       bool            `json:"vendorCharts" yaml:"vendorCharts"`
	ReadinessHooks     bool            `json:"readinessHooks" yaml:"readinessHooks"`
	Serial             bool            `json:"serial" yaml:"serial"`
	Components         []string        `json:"components,omitempty" yaml:"components,omitempty"`
	RepoURL            string          `json:"repoURL,omitempty" yaml:"repoURL,omitempty"`
	TargetRevision     string          `json:"targetRevision,omitempty" yaml:"targetRevision,omitempty"`
	AppName            string          `json:"appName,omitempty" yaml:"appName,omitempty"`
	StorageClass       string          `json:"storageClass,omitempty" yaml:"storageClass,omitempty"`
	SharedStorageClass string          `json:"sharedStorageClass,omitempty" yaml:"sharedStorageClass,omitempty"`
	NodeScheduling     *NodeScheduling `json:"nodeScheduling,omitempty" yaml:"nodeScheduling,omitempty"`
}

Settings records resolved effective bundler settings, admitted by one rule: a setting appears here only when its effect is already observable in the bundle's own files. Endpoints and security posture that never shape bundle content — Fulcio and Rekor URLs, the certificate identity pattern, registry TLS posture, the output target, the --config path — are excluded by construction. Free-form --set overrides are excluded too: values.yaml already carries their effect, and they are the one input that could be anything.

Components is the positive component-name filter, reachable only through the REST ?bundlers= query parameter. It is named for what it holds rather than for Config.Bundlers behind it. It has the strongest claim of anything here: the bundle's recipe.yaml is written POST-filter, so without this a subset bundle is indistinguishable from an unfiltered bundle of a smaller recipe.

type Toleration

type Toleration struct {
	Key               string `json:"key,omitempty" yaml:"key,omitempty"`
	Operator          string `json:"operator,omitempty" yaml:"operator,omitempty"`
	Value             string `json:"value,omitempty" yaml:"value,omitempty"`
	Effect            string `json:"effect,omitempty" yaml:"effect,omitempty"`
	TolerationSeconds *int64 `json:"tolerationSeconds,omitempty" yaml:"tolerationSeconds,omitempty"`
}

Toleration mirrors the fields of corev1.Toleration that the bundler sets. It is declared here rather than reused because corev1 types carry json tags only, and this artifact is serialized by yaml.v3, which reads yaml tags — reusing corev1 would emit Go field names into the record.

Jump to

Keyboard shortcuts

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