Skip to content

Tracing where configuration values came from

cub unit blame answers "who set this, when, and why" for every field of a unit's configuration. It is the same question git blame answers for lines of a file, asked of fields instead: ConfigHub treats configuration as data, so it attributes each value at a path such as spec.replicas or a container's image, not a line of text. The answer comes from the unit's MutationSources, joined with the Mutation that made each change and the Revision it is in, and followed upstream to where the value was actually set. cub variant blame asks it of every unit of a variant, and cub unit path shows everything recorded about one field.

Blaming a unit

cub unit blame --space apptique-prod deployment-frontend

prints one line per field, grouped by resource:

Resource: apps/v1/Deployment apptique/frontend
REF        FLAGS    PATH                                                 SET BY                     USER                 WHERE              REV    WHEN
3f9a2c1    UC       spec.replicas                                        set-replicas               alice@example.com    apptique-base      12     3d ago
070f84b    PXL      spec.template.spec.containers.?name=server.image     UpdateUnit                 registrybot          apptique-images    81     2h ago
b21c9e0             spec.template.spec.containers.?name=server.name      helm template frontend     bob@example.com      apptique-base      2      40d ago
  • REF is the field's short reference, which cub unit path takes in place of the resource and path (see below). It is a hash of the resource type without its API version, the resource name without its namespace, and the path, so the same field has the same REF in every revision of the unit and in every variant of it.
  • FLAGS says in one letter each what else there is to know about the field. See Flags.
  • PATH is the field, in the path syntax functions and links use: a container is addressed by its name (?name=server), not its position.
  • SET BY is what made the change that set the value: the link or trigger that made it, the function a user ran, the external source the data came from (the file a unit was created from, or the command a cub variant upload rendered, such as helm template frontend), or otherwise the operation that made the revision, such as UpdateUnit.
  • USER, WHERE, REV, and WHEN are who made that change, the space it was made in, that unit's revision number, and how long ago. A change made by no user, such as one a link's automatic resolve made, reads automation.

-o wide adds the value itself (VALUE), the function a link or trigger ran (FUNCTION), the mutation number (MUT), the change in this unit that brought a value set upstream, such as the upgrade link (VIA), and the ChangeOrder that made the change in this unit (CHANGE ORDER).

--path keeps the fields whose path contains a string, so --path image lists every container image; --resource keeps one resource's fields, as <type>/<name> or <name>. Fields that hold YAML comments are left out unless you pass --show-comments. --verbose prints, under each field, its guards, conflicts, link findings, and failed validation checks, and every change from this unit out to where the value was set, with its change description. -o json emits one object per field, with the whole chain.

On a terminal, the PATH column wraps at the dots between path segments to fit the window, and the other columns keep their place; -o wide and piped output do not wrap.

Following values upstream

A value a unit did not set itself is followed to the unit that did:

  • Merges. A value that arrived by clone, upgrade, or promotion is followed into the upstream unit, at the revision the merge took, and on to that unit's upstream, until it reaches the change that set it. A field a variant took from its base reads as the base's change, and a field the base took from a rendered chart reads as that chart, so "did the chart set this, or did we?" is one command.
  • Links that resolve values. A value a link wrote is followed into the link's upstream unit, at the revision the resolve read, to the field it read: the provided path of a needs/provides binding, the one upstream path of a TransformPaths link, or the same field for an Upsert link that copies the upstream's resources as they are. An image a TransformPaths link set from a registry fact therefore reads as the change that recorded the fact.
  • Where it stops. A TransformPaths link that reads more than one upstream path, or uses an UpstreamGetter, computed the value from more than one field; an Insert link, and an Upsert link that runs a transform invocation, compute what they write from the upstream document as a whole. Blame ends at the link for these, and cub unit path still shows the link, its upstream unit, and the revision it read.

--no-upstream reports only this unit's own record, and --upstream-depth limits how many units the walk follows.

Flags

Flag Means
P Protected: a local override, which a merge from upstream leaves alone. See Protecting a variant's own values.
G Guarded: reasons are recorded for the value, and an operation has to be cleared for them to overwrite it. See Guarding values against every change.
X Conflict: a merge or resolve dropped an upstream change to the field. See Conflicts.
F Finding: a link's resolve could not do what it names here.
E Validation error: a validating trigger failed a check that names the field, which blocks a release. See below.
W Validation warning: a validating trigger with --warn failed a check that names the field.
U An upgrade from upstream, through an UpgradeUnit link, brought the value to this unit.
L Another link set the value in this unit, by resolving or merging.
T A trigger set the value in this unit.
C The change in this unit was made by a ChangeOrder.
N Not released: the value changed after the unit's last release, or the unit has never been released. It is not shown in a space with no release target, which has nothing to release.
n Needed: the value is to come from an upstream unit through a needs/provides link, whether or not a link has bound it yet.
p Provided: the value is offered to downstream units through needs/provides links.

P through W are about the field as it stands. U through N are about the change in this unit that put the value there, which for a value set upstream is the merge or link that brought it, not the upstream change SET BY names. n and p are lowercase because they are informational: they say what part the field plays in needs/provides links, not that anything is wrong or pending. The legend under the table lists only the letters that appear. --verbose prints the needed and provided attributes under the field, and -o json includes them, with the slug of the link that bound a needed one in BoundLink.

Everything about one field: cub unit path

cub unit path takes a unit and the REF blame printed for a field, or any prefix of at least four characters of one, or the field's path:

cub unit path --space apptique-prod deployment-frontend 070f84b
cub unit path --space apptique-prod deployment-frontend 'spec.template.spec.containers.?name=server.image'

It prints the field's value, whether the unit's last release includes it, its protection, guards and other annotations, its conflicts and findings, its failed validation checks with what each said, and the attributes it is needed or provided as: for a needed one, the link that bound it, the properties it requires and prefers of a provided value, and the placeholder it held; for a provided one, the properties it offers. Then it lists every change from this unit out to where the value was set, each with what made it (the link with its type, upstream unit, and upstream paths; the function and its arguments; the trigger or invocation; the merge and the upstream revisions it took), the revision's operation and description, who made it with what client and when, and the ChangeSet, ChangeOrders, releases, and tags it belongs to:

  apptique-prod/deployment-frontend revision 31, mutation 31
    Set by:        link frontend-image
    Link:          frontend-image (TransformPaths) from apptique-images/frontend
                   upstream tag = registrybot.confighub.com/v1alpha3 frontend streams.stable.tag
    Function:      set-image-reference-by-uri repository-uri="ghcr.io/apptique/frontend" image-reference=":v1.4.0"
    Read from:     apptique-images/frontend, revision 81
    Operation:     TransformPaths
    ChangeOrders:  apptique-base/release-v1.4.0
    Released in:   release 100, published to target prod/prod-oci, by ChangeOrder apptique-base/release-v1.4.0, 2h ago

--history adds every change this unit has recorded to the field, oldest first, the way git log -L follows a range of lines; it reads the record of every revision of the unit. A path that more than one resource has needs --resource, and a path that holds other fields, such as a container, lists the fields under it with their REFs. -o json emits the whole report, with each change's Mutation and Revision as the API returns them.

Entities in another space than the unit are named as space/slug, the form the CLI takes them in; a release is always in its unit's space, so its number is enough for cub release get.

Blaming a variant

cub variant blame apptique/prod --path image

is cub unit blame for every unit of a variant, under a header per unit, with the same columns, flags, and options. The variant is named the way cub variant diff takes it: as <component>/<variant>, or by its space. --where narrows the units with the expressions cub unit list takes, and -o json emits one object per field, each naming its unit. Each unit's record and data are read for the whole selection at once, and an upstream unit that several units merge from is read once.

Failed validation checks

A validating trigger that does not pass records its result on the revision, and the result can name the fields it failed on. vet-placeholders names each field that still holds a placeholder, and a vet-cel expression can name fields in failed_attributes. Blame puts each such check on the field it names, or on every field under a value it names, and marks it E, or W when the trigger was created with --warn. A check that failed without naming fields is about the unit as a whole, and is on no field.

--verbose and cub unit path say which check it was, as <space>/<trigger>/<function>, and what the function said, or what it found when it said nothing:

Validation:    error apptique-prod/no-placeholders/vet-placeholders: found "confighubplaceholder"

A CEL expression is free to name an array element by its position, as in spec.template.spec.containers.1.image, where blame names it by its merge key, as in spec.template.spec.containers.?name=sidecar.image. Blame matches either form.