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 ¶
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 ¶
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"`
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.