Skip to content

Link

Infrastructure resources often have dependencies on other resources in the sense that information about one resource is necessary in order to configure another resource. For example an AWS load balancer needs to know the subnet IDs of the VPC it should route traffic to. When dependent resources are kept together in a single Config Unit, the infrastructure can usually resolve these dependencies by itself. But sometimes you want to modularize and keep them separate. ConfigHub supports this by using multiple Config Units and you use Links to express these dependencies between Config Units.

In general, Links indicate that configuration data should be propagated from the target of the Link, called the upstream unit, to the source of the Link, called the downstream unit -- the Link direction is the opposite of the direction that data flows. Downstream units link to upstream units that they depend upon. The dependency also sequences apply and destroy actions.

In Kubernetes, data propagation manifests mainly in the form of static resource references. For example, for Units containing Kubernetes Namespace-scoped resources such as Deployment and Service, the metadata.namespace fields of those resources can be set via the needs/provides mechanism by linking their Unit to a Unit containing a Kubernetes Namespace.

The UpdateType field determines the operation performed using the Link:

  • None: No data propagation is performed. Used solely to express a dependency for sequencing apply and destroy actions. AutoUpdate must be false.
  • UpgradeUnit: Merges upstream configuration data into the downstream unit and keeps the downstream unit's UpstreamUnitID and UpstreamRevisionNum in sync with the Link. This type is automatically created when cloning a unit and is used by the upgrade mechanism. A unit may have at most one outgoing UpgradeUnit Link.
  • MergeUnits: Like UpgradeUnit, but without updating the unit's UpstreamUnitID and UpstreamRevisionNum. More flexible: a unit may have any number of incoming and outgoing MergeUnits Links. Can pull from LiveState when UseLiveState is true.
  • Insert: Inserts the entire configuration data of the upstream unit as a string value at a specific path in the downstream unit, identified by a single Binding. Useful for embedding structured data (such as JSON policies) into a field of a resource in a different format.
  • Upsert: Pulls one or more resources produced by the upstream unit (optionally first transformed by a TransformInvocation) and inserts or replaces each of them in the downstream unit. Used to render configuration produced by one unit (e.g. an AppConfig/* file) into resources of a different toolchain (e.g. a Kubernetes ConfigMap). Bindings must be empty; the downstream unit must currently be Kubernetes/YAML.
  • TransformPaths: Reads named values from specified paths in the upstream unit (and optionally from UpstreamGetters function invocations) and writes Go template or CEL expression results to specified paths in the downstream unit (and optionally via DownstreamSetters function invocations). Useful when you want to compute downstream values from one or more upstream values combined with Space/Unit metadata, or to drive a mutating function with templated arguments. See UpstreamPaths, UpstreamGetters, DownstreamPaths, and DownstreamSetters.
  • NeedsProvides (default): Needed values in the downstream unit are matched with provided values from the upstream unit. See needs/provides for details. If UpdateType is empty, it defaults to NeedsProvides.

Automatic creation

When a unit is cloned, ConfigHub automatically creates an UpgradeUnit Link from the new clone (downstream) to the original unit (upstream). This Link records which upstream revision was last merged and includes a WhereMutation filter so that upgrade operations only overwrite mutations that originated from the upstream, preserving local customizations.

Resolution

For all Link types that propagate data (all except None), configuration data can be updated in two ways:

  • Automatic: When AutoUpdate is true, the downstream unit is automatically updated when the upstream unit changes. AutoUpdate must be false for UpdateType None.
  • Manual: The downstream unit is updated with a resolve parameter specifying the Link or Links to resolve. When resolving all Links (Link:*), None Links are skipped.

For UpgradeUnit Links, resolution can also be triggered via the --upgrade flag, which uses the Link's WhereMutation filter if one is present.

To preview what a resolve operation will do before committing the change, use --dry-run.

Bindings

Bindings record which values are propagated by a Link. Their structure and requirements vary by Link type:

  • NeedsProvides: After resolution (automatic or manual), Bindings are stored on the Link recording which needed attributes in the downstream unit were matched to provided attributes in the upstream unit. Bindings can also be specified manually with AutoUpdate false to propagate values to or from non-standard locations. Each Binding specifies DataType (string, int, or bool), ProvidedResource, ProvidedPath, NeededResource, and NeededPath.
  • Insert: Requires exactly one Binding specifying NeededResource.ResourceName, NeededResource.ResourceType, and NeededPath to identify where the upstream data is inserted. ProvidedResource and ProvidedPath must not be specified. AutoUpdate in the Binding must be false.
  • MergeUnits, UpgradeUnit, Upsert, TransformPaths, None: Must not have Bindings.

UpstreamPaths, UpstreamGetters, DownstreamPaths, and DownstreamSetters

For TransformPaths Links, four lists on the Link declare what to read from the upstream unit and what to write to the downstream unit.

Upstream phase: read named values

  • UpstreamPaths is a list of NamedPath. Each entry has a Name (a legal Go and CEL identifier, used to reference the value from expressions), a Resource identifying a resource in the upstream unit, and a Path within that resource. Values are read via the get-paths function and are filtered by the Link's WhereResource before lookup.
  • UpstreamGetters is a list of NamedFunctionResult. Each entry has a Name (same identifier rules as UpstreamPaths) and a FunctionInvocation for a non-mutating ConfigHub function whose OutputType is AttributeValueList. The Value of the first returned AttributeValue is bound to Name. Use a getter when the upstream value cannot be addressed by a single literal path — for example, when it has to be derived via a CEL expression over the upstream resources. FunctionInvocation.WhereResource is AND-combined with the Link's WhereResource to give each getter its own resource scope. Worker functions are not supported.

UpstreamPaths and UpstreamGetters Names share a single namespace and must all be unique. All upstream reads — get-paths plus the getter functions — run in one invocation on the upstream unit.

Downstream phase: write values and run mutating functions

  • DownstreamPaths is a list of PathExpression. Each entry has a Resource and Path identifying where to write in the downstream unit; an Expression (a Go template or CEL expression, per Evaluator); a Parameters list naming the upstream values the expression references (matching UpstreamPaths or UpstreamGetters Names); and a DataType (string, int, or bool). The expression renders to a string and is coerced to DataType (strconv.Atoi for int, strconv.ParseBool for bool). Values are written via the set-attributes function in a single call.
  • DownstreamSetters is a list of ParameterizedFunction. Each entry has a Parameters list (matching upstream Names that the setter is allowed to reference) and a FunctionInvocation of a mutating ConfigHub function. String arguments whose Evaluator is set are template-expanded client-side using the same scope as DownstreamPaths expressions, narrowed to the setter's Parameters list. Non-string arguments and arguments without Evaluator are passed through unchanged. FunctionInvocation.WhereResource is AND-combined with the Link's WhereResource. Worker functions are not supported.

DownstreamSetters run first (in one invocation on the downstream unit), then DownstreamPaths run via set-attributes (in one invocation). The two mutation summaries are merged into the resolve's FunctionMutationSummary.

Expression scope

Evaluator FunctionContext fields Upstream values
template (Go templates) Top-level (e.g. {{.UnitSlug}}, {{.SpaceSlug}}) {{.Params.<name>}}
cel (Common Expression Language) functionContext.UnitSlug, functionContext.SpaceSlug, … params.<name>

The FunctionContext fields are those of the downstream unit. Field names match the function handler: Go template field names for templates, JSON-shaped names for CEL.

Abort semantics

If any UpstreamPath or UpstreamGetter produces no value (e.g. the path does not exist in any matching resource, or the getter function returns an empty list), the resolve is aborted: no values are written, and the explicit (manual) resolve path returns an error. The auto-update path logs the failure and continues with subsequent Links.

In addition to common metadata fields (DisplayName, Slug, Labels, Annotations), Links have the following fields:

Field Description
FromUnitID The downstream (consumer) unit. The Link must be in the same space as this unit.
ToUnitID The upstream (producer) unit.
ToSpaceID The space of the upstream unit. May differ from the Link's space for cross-space links.
UpdateType The operation type: None, UpgradeUnit, MergeUnits, Insert, Upsert, TransformPaths, or NeedsProvides. Defaults to NeedsProvides if empty.
AutoUpdate If true, automatically update the downstream unit when the upstream unit changes. Must be false for None.
UseLiveState If true, use the LiveState of the upstream unit rather than Data as the source.
UpstreamLastMergedRevisionNum The revision (or UnitAction) number of the last merged upstream change.
DownstreamLastMergedRevisionNum The revision number of the downstream unit created by the last merge.
WhereMutation A filter expression that controls which downstream mutations can be overwritten during merge operations. Only for MergeUnits and UpgradeUnit.
WhereResource A filter expression that selects which upstream resources are eligible for propagation.
Bindings The attribute bindings for NeedsProvides and Insert Links. See Bindings.
TransformInvocationID Identifier of an Invocation whose function runs on the upstream data before being upserted into the downstream Unit. Only valid when UpdateType is Upsert. The Invocation's ToolchainType must match the upstream Unit, the function must be non-mutating, and its OutputType must match the downstream Unit's toolchain (currently only Kubernetes/YAML / YAML output).
UpstreamPaths List of NamedPath declaring the values to read from the upstream unit via get-paths. Only valid when UpdateType is TransformPaths. See UpstreamPaths, UpstreamGetters, DownstreamPaths, and DownstreamSetters.
UpstreamGetters List of NamedFunctionResult declaring non-mutating function invocations whose first AttributeValue value is bound to a Name and exposed to expressions alongside UpstreamPaths. Only valid when UpdateType is TransformPaths.
DownstreamPaths List of PathExpression declaring the values to write to the downstream unit via set-attributes. Only valid when UpdateType is TransformPaths.
DownstreamSetters List of ParameterizedFunction declaring mutating function invocations to run on the downstream unit. String arguments are template-expanded using the upstream values listed in Parameters. Only valid when UpdateType is TransformPaths.