Skip to content

cub variant upload

cub variant upload

Upload rendered Kubernetes resources into a Space as Units

Synopsis

Upload already-rendered Kubernetes manifests into a ConfigHub Space.

The input is a stream of rendered resources — from the installer, "kustomize build", or "helm template" — supplied as files, directories (walked for .yaml, .yml, .json, .env, .properties, .toml, and .ini), "-" for stdin, or an "oci://" reference to a manifest bundle. This command does not render anything; it ingests what you give it.

An oci:// input is pulled by the server, which records the digest it read on the Space. The bundle is a standard OCI image artifact (a tar or tar+gzip layer of configuration files, as "cub release publish" and Flux produce, or individual file layers as "oras push" produces). The pull is anonymous, so the bundle has to be public. An oci:// input pulled by the server must be the only input. With --client-pull, cub pulls it instead, for a registry this machine can reach and the server cannot, such as one on a private network; it can then be combined with other inputs.

The server does the work: it splits the bundle into resources and makes the Space's Units, Links, and Invocations match it. Every resource becomes its own Unit, named after the resource rather than the file it came from — a workload keeps its bare name ("backend"), and everything else takes its kind as a suffix ("backend-service").

Uploading is create-or-update, always. The first upload and every later one go through the same path, so there is no separate "re-upload" mode and nothing to remember between runs:

create the resource is new, so a Unit is created for it. update the source changed since it last wrote that Unit, so the new content is 3-way merged into it. Changes made in ConfigHub since — a set- function, a hand edit, a needs/provides binding — survive, and anything the merge had to withhold is reported as a conflict. unchanged the source has not changed, so nothing is written. empty the resource is gone from the bundle, so the Unit's data is emptied. revive an emptied resource is back, so the whole resource is applied to the same Unit. adopt a Unit that already held exactly this resource is taken over, keeping its UnitID, its slug, and its history.

Nothing is ever deleted. A resource the bundle no longer contains empties its Unit instead, so the Unit keeps its identity, its links, and its history, and the next Release withdraws the object from the cluster. Emptying is what "--yes" confirms: without it, an upload whose plan empties Units asks first.

Ownership is a label. Every Unit, Link, and Invocation an upload writes is labeled with its source name (--source-name, defaulting to --component), and a Unit belonging to another source, or to no source, is never written or emptied — so one Space can hold several sources and hand-written Units side by side.

Rendered Secrets are never uploaded — apply them out-of-band. AppConfig ConfigMaps (carrying installer.confighub.com annotations) are expanded into an AppConfig data Unit, a render-configmap Invocation, a placeholder Unit, and an Upsert link. A file holding application configuration rather than Kubernetes resources — a .properties, .env, .toml, or .ini file, or YAML or JSON with no apiVersion and kind — becomes one untargeted AppConfig Unit, named by its configHub.configName or else by its path (config/app.properties becomes config-app). A YAML file mixing the two is refused.

Links between Units are inferred from references, label selectors, and custom-resource → CRD relationships. Because ConfigHub does not break dependency cycles, any cycle in the inferred links is broken — the weakest edge is dropped (a selector before a reference; a cross-scope reference before a same-namespace one) — and reported.

The Space is created if missing and stamped with the well-known labels from --component, --variant, --stage, --environment, --region, --layer, and --owner, and any other labels given with --space-label. --component is required; --variant defaults to "base", since an upload normally seeds the base that variants are created from. The Space slug comes from --space-pattern (a Go template over .Labels), or from --space to set it explicitly. --unit-label and --unit-annotation set labels and annotations on every written Unit.

--namespace is the release namespace, as in "helm template -n": where namespaced resources that name no namespace, and cluster-scoped resources, belong. It has no default. Charts write the namespace into places set-namespace cannot reach — ConfigMap data, flags, annotations, webhook references — so render with the real namespace rather than substituting one afterwards. --create-namespace synthesizes the Namespace resource when the bundle lacks it; it is off by default, because a bare Namespace has none of the pod-security labels, NetworkPolicy, ResourceQuota, or LimitRange that make a namespace usable, and the platform normally provisions those together.

The writes are recorded in a ChangeSet, so an entire upload can be rolled back with the "cub unit update --restore Before:ChangeSet:" command printed at the end.

Use --dry-run to see what would be created, updated, emptied, revived, or adopted without changing anything.

Examples:

  # Upload a kustomize build into a derived Space slug "web-base".
  kustomize build overlays/base | cub variant upload --component web --variant base -

  # Into an explicit Space, bound to a target.
  cub variant upload --component web --variant prod --space web-prod \
    --target web-prod/cluster ./rendered/

  # Helm output, rendered with the real namespace and uploaded into it.
  helm template myapp ./chart -n myapp | cub variant upload \
    --component myapp --variant prod --environment Prod --namespace myapp -

  # Seed a base from a published OCI manifest bundle.
  cub variant upload --component cubbychat --variant base \
    oci://ghcr.io/confighub/configs/cubbychat

  # Upload a newer bundle. Changed Units are merged, preserving edits made in
  # ConfigHub since; resources the bundle dropped have their Units emptied.
  cub variant upload --component cubbychat --variant base --yes \
    oci://ghcr.io/confighub/configs/cubbychat:v2

  # Pull from a registry only this machine can reach.
  cub variant upload --client-pull --component web --variant base \
    oci://registry.internal:5000/configs/web:1.2.0

  # Preview that upload first.
  cub variant upload --dry-run --component cubbychat --variant base \
    oci://ghcr.io/confighub/configs/cubbychat:v2
cub variant upload [flags] <file|dir|oci://ref|-> [<file|dir|oci://ref> ...]

Options

      --change-desc string        change description recorded on each written Unit
      --client-pull               pull oci:// inputs on this machine rather than having the server pull them, for a registry the server cannot reach
      --component string          value for the well-known "Component" Space label (required)
      --create-namespace          synthesize the release Namespace resource if the bundle does not contain it
      --dry-run                   report what the upload would create, update, empty, revive, or adopt, and exit without changing anything
      --environment string        value for the well-known "Environment" Space label (e.g. Prod)
  -h, --help                      help for upload
      --layer string              value for the well-known "Layer" Space label (e.g. App)
      --namespace string          the release namespace: where namespaced resources that name no namespace, and cluster-scoped resources, belong
      --owner string              value for the well-known "Owner" Space label (e.g. Engineering)
      --region string             value for the well-known "Region" Space label (e.g. us-east1)
      --source-name string        ownership name for the Units, Links, and Invocations this upload writes; defaults to --component
      --space string              explicit Space slug; overrides --space-pattern
      --space-label strings       label key=value to set on the Space (repeatable); it may not name a label another flag sets, such as Component
      --space-pattern string      Go template (prefix 'template:') for the Space slug, evaluated over .Labels (default "template:{{.Labels.Component}}-{{.Labels.Variant}}")
      --stage string              value for the well-known "Stage" Space label (e.g. Canary)
      --target string             target for the created Units, in <target-slug> or <space-slug>/<target-slug> form
      --unit-annotation strings   annotation key=value to set on every written Unit (repeatable)
      --unit-label strings        label key=value to set on every written Unit (repeatable)
      --variant string            value for the well-known "Variant" Space label (default "base")
      --yes                       do not ask for confirmation when the upload would empty Units

Options inherited from parent commands

      --context string   The context to use for this command
      --debug            Debug output

SEE ALSO