Skip to content

Creating and managing variants

A common task is to manage variants of the same configuration, for example, development, staging, and production variants.

Creating variants

In ConfigHub you can copy (known as clone in the UI) config units in a way that tracks their relationship in order to help you keep them in sync. The new copied unit is said to be downstream from the original upstream unit.

Different deployment environments would have different spaces corresponding to them, so typically one would clone a unit to one or more other spaces.

You can copy one unit to another space with the CLI like this:

cub unit create --space prod --upstream-space dev --upstream-unit backend

You could copy multiple units to multiple spaces also. To copy all units from dev into multiple prod spaces (e.g., corresponding to multiple clusters or regions), you could do something like:

cub unit create --space dev --where-space "Labels.Environment = 'prod'"

Keeping variants in sync

The units and the copies (clones) can all be modified independently, but if you want to propagate a change from the upstream unit to the downstream units, ConfigHub keeps track of the upstream/downstream relationship and the revision of the upstream unit that the downstream unit last upgraded from.

When a unit is cloned, ConfigHub automatically creates an UpgradeUnit link from the clone to its upstream unit. This link tracks which upstream revision was last merged, and includes a mutation filter that ensures upgrade only overwrites changes that originated from the upstream -- local customizations are preserved.

To upgrade and copy more recent changes from the upstream unit, you can execute a command like this:

cub unit update --space "*" --patch --upgrade --where "UpstreamUnit.Slug = 'backend' AND Space.Labels.Environment = 'prod'"

Alternatively, since the relationship is represented as a Link, you can also upgrade via the resolve mechanism:

cub unit update --space prod --patch --resolve "Link:*" backend-clone

Both approaches produce the same result. The --upgrade flag uses the UpgradeUnit link's mutation filter if one exists. The --resolve approach works with any Link of type UpgradeUnit or MergeUnits.

Note that upgrading the units modifies the configuration data but does not automatically apply the changes. If you want to preview the changes first, use the --dry-run flag.

Merging upstream changes in this way preserves changes made independently in the downstream unit -- they are treated as overrides. ConfigHub supports this by keeping track of the sources of changes via mutation metadata.

Upgrade propagates changes from the upstream unit to selected downstream units, but it's also possible to merge sets of changes in the other direction, across sibling variants, and between any similar units. However to do that you need to provide the information about which changes to merge. That can be done like this:

cub unit update --patch --merge-source prod-east/backend --merge-base Before:ChangeSet:prod-east/prodfix42 --merge-end ChangeSet:prod-east/prodfix42 --space prod-west backend

If you deploy new releases using functions like set-image-reference, you can invoke those functions on all variants either all at the same time or individually (for progressive rollouts). That will cause the affected fields to not be upgradable, but that's probably what you want in the case that upgrade rollout sequences are different from release rollouts.

Base units

You may choose to maintain base units which you copy to create all of the variants you plan to deploy. Base units are similar to abstract classes in that they aren't intended to be deployed. For example, they need not have attached deployment targets. You can use labels to skip these in bulk applies, or just filter out units without targets (where TargetID IS NOT NULL).

This approach enables a clearer separation of common attributes and custom attributes.

Sample units

A sample unit is similar to a base unit in that it isn't intended to be deployed, but it is meant as a starting point for other units rather than as a mechanism for driving changes to a specific set of variants derived from it.

Placeholders for undetermined values

Base units, and sometimes other new config units, contain default values for most fields, but some may require specific values that cannot yet be provided. This is indicated by placeholders.

A placeholder is a special string or number in the config data which indicates that you must replace it with a real value before the configuration is valid. ConfigHub uses the string "confighubplaceholder" to indicate where a string field needs to be supplied with a value and 9 9s (999999999) for integer fields.

The vet-placeholders function can be installed as a trigger to prevent applying configuration units with unreplaced placeholders, and get-placeholders can return a list of field paths containing placeholders.

Triggers are good for setting general properties of an environment: scale/cost (e.g., replicated vs not, backups vs not), privacy (e.g., public vs not, data with real PII vs not), security (e.g. must be encrypted vs not, can't run as root), and the like.

Links better address more specific resource properties like hostnames, IP addresses, etc.

See managing dependencies for guidance regarding how to replace placeholders with variant-specific values.