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.

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:

  • 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.
  • Insert: Inserts the configuration data of the upstream unit (optionally first transformed by a TransformInvocation) at a specific path in the downstream unit, identified by a single Binding. The Binding's DataType selects whether it is inserted as a string value — useful for embedding a document such as a JSON policy into a field of a resource in a different format — or inlined as configuration data, which makes the upstream unit a sub-tree of the downstream unit's data. The two units' formats only need to be compatible, not identical: the upstream unit is converted before insertion. See Insert links.
  • 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. Its WhereMutation is empty: what an upgrade must not overwrite is decided by the Protected flags stored per path on the clone, which is what preserves local customizations.

Resolution

Configuration data can be updated in two ways:

  • Automatic: When AutoUpdate is true, the downstream unit is automatically updated when the upstream unit changes.
  • Manual: The downstream unit is updated with a resolve parameter specifying the Link or Links to resolve.

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

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. DataType is optional and selects how it is inserted: string (the default) inserts it as a string value, and the downstream unit's own configuration format (YAML, JSON, TOML, INI, Properties, Env, or Text, matching the downstream unit's toolchain) inlines it as configuration data. Any other DataType is rejected. ProvidedResource and ProvidedPath must not be specified. AutoUpdate in the Binding must be false.
  • MergeUnits, UpgradeUnit, Upsert, TransformPaths: 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: 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.
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 selecting downstream mutations whose paths a merge must not overwrite, unioned with the stored Protected flags. Empty by default. 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 it is inserted into or upserted into the downstream Unit. Only valid when UpdateType is Insert or Upsert. The Invocation's ToolchainType must match the upstream Unit, the function must be non-mutating, and its OutputType must be YAML. For Upsert, the output must also match the downstream Unit's toolchain, which currently limits it to Kubernetes/YAML.
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.