Skip to content

Unit

The (Config) Unit is the core unit of configuration in ConfigHub.

Config Data

Config Units store configuration data. Configuration data may originate from a variety of sources: other Units, previous revisions, live state, documentation samples, AI chatbots, and so on, but ConfigHub becomes the source of record for the data.

Revisions

A Config Unit maintains a sequential list of revisions of the config data. Every time the data is mutated, ConfigHub records a new revision along with who made the change, a description of the change, and other metadata.

Toolchain Type

A Config Unit (or just Unit) contains configuration data ("config data") in a single format applied by a single tool, known as the ToolchainType.

One or more "atomic" resources or other configuration element can be specified in a Unit. For example, it can contain a list of Kubernetes YAML resources (YAML documents), a list of OpenTofu resources and/or data sources, or application properties files. A Unit is always applied in a single operation. So if it contains multiple resources/elements, it is expected that the infrastructure or bridge will be able to resolve references between resources and deal with ordering of actions.

A single Config Unit cannot contain resources belonging to different infrastructure control planes and/or configured using different tools. For example, you can't provision a Kubernetes Deployment in a Kind cluster and also an RDS database in AWS using a single Config Unit. A single Unit also cannot contain resources or other elements configured using, for example, both Kubernetes YAML and CloudFormation. This is because, while EKS and CloudFormation are both AWS services, they provide different control planes using different configuration formats.

Here are some examples of Config Units:

  • A list of Kubernetes resources serialized as YAML documents (fully supported now)
  • A single Java properties file (preliminary support)
  • A single OpenTofu root module for one service provider (preliminary support for AWS)

Live State

ConfigHub maintains a one-to-one correspondence between a Config Unit and the associated live resource. For example, if you have a Kubernetes Deployment YAML and you use kubectl to apply it in two different namespaces, then you get two instances of the Deployment. ConfigHub maintains a separate Config Unit for each of these instances.

When a Unit is applied, refreshed, or imported, the corresponding live state is stored with the unit.

Unit Apply Workflow

After making changes to configuration data of a Unit, at some point you will want to synchronize those changes to the Live resources. That is achieved via the Apply operation, similar to kubectl apply or tofu apply.

Apply has some prerequisites:

  1. The Unit must have an attached Target of a matching ToolchainType.
  2. That Target must be associated with an active Bridge Worker.
  3. The Unit must not have any Apply Gates, such as due to lacking required approvals, remaining placeholder values, or other failed validation function triggers.

Once these prerequisites are satisfied, the apply action may be invoked.

This is a slightly simplified workflow compared to GitOps, where:

  1. configuration changes would be committed and pushed,
  2. then a pull/merge request would be opened, validated, reviewed/approved, and merged, and then
  3. the GitOps operator would eventually pull and apply the changes.

In that case, the PR merge is equivalent to the apply action. In ConfigHub any configuration changes made after the last apply (tracked by the live revision) are analogous to commits pushed to an open pull request. Also, in ConfigHub there's no "apply before merge" vs. "apply after merge" quandry. The head revision is effectively always merged.

Approval policy can be customized via a function trigger. A simple is-approved function is provided as an example. It takes the number of required approvers as an argument. Only organization members with permission to approve the unit can successfully invoke the apply operation. Note that any changes made to a unit invalidate any prior approvals.

See the change-and-apply guide for more details.

Unit Actuation Lifecycle

Actuation refers to the process of synchronizing changes in the Unit to its corresponding Live resources, or vice versa, and it breaks down to an Apply (described above), Destroy, Refresh, or Import operation (also known as "actions").

A Unit transitions through different states via these operations:

NotLive: In this state, the Unit does not yet represent any "live" instantiation, such as Kubernetes resources or infrastructure resources. When a Unit is created, it starts in this state. It also transitions to this state when all live resources have been successfully destroyed.

Progressing: The operations are asynchronous, long-running processes. Therefore, after an operation has been executed, the Unit will move to this state while the operation is under way.

Degraded: If an operation fails or is canceled, the Unit will be left in this state until it is repaired to a known state. Hopefully the problem can be identified by looking at status events. If not, it may be necessary to inspect worker logs or other infrastructure diagnostics data. In many cases, the problem can be addressed by fixing bugs in the config data or by supplying missing dependencies. But in some cases, the problem may not be solvable from within ConfigHub.

Ready: The Unit moves into this state when the Bridge responsible for actuation has determined (to the best of its abilities) that an Apply, Refresh, or Import operation has succeeded and the live resources are ready.

Detached: The Unit moves into this state when a Target is removed from a Unit that had previously had Apply or Import operations invoked on it.

Unknown: The Unit should not normally be in this state. It possibly indicates a problem with the Bridge worker.

Operations themselves have states also:

None: No operation has been performed.

Progressing: An operation has been started.

Completed: The operation has completed successfully.

Failed: The operation has failed.

Pending, Submitted, Canceled: These states currently aren't possible, but are reserved for the future.

Unit Granularity

We recommend that you group related resources in a Unit, but that they should be fairly granular and narrowly scoped in order to separate roles and privileges, contain the blast radius of changes and actions, and simplify function arguments targeting specific parts of the configuration.

For example, different components belong in different Units and instances of a component in different environments or regions belong in different Units in different Spaces.

Some more detailed guidelines, for now focused on Kubernetes specifically:

  • Cluster-scoped resources require cluster admin privileges. Therefore, cluster-scoped resources generally should be in separate units from namespace-scoped resources, though there may be some exceptions to that, such as grouping MutatingWebhookConfiguration, ValidatingWebhookConfiguration, and APIService with the workload resources running those services. If we all of the webhook and API service configurations were put together it would be difficult for links to disambiguate which workloads corresponded to which hooks/services.
  • Privileged RBAC resources like ClusterRole and ClusterRoleBinding resources should go into units separate from other resources to make them easier to generate, manage, and review.
  • Namespace-scoped policy resources like ServiceAccount, Role, RoleBinding, ResourceQuota, LimitRange, NetworkPolicy, etc. belong in the same units as their corresponding namespaces. Namespaces and corresponding policies generally require cluster admin privileges to create or change, and also these groupings are likely to be diff'ed and cloned.
  • Workload resources corresponding to different namespaces should be in separate units. Different namespaces may have different permissions and different tenants.
  • Distinct workloads (e.g., Deployment + Service + Ingress + HorizontalPodAutoscaler) should be stored in separate units.
  • ConfigMaps contain embedded configuration. Eventually we intend to support application configuration in separate units in its native formats, such as Java Properties, Env, TOML, INI, etc. For now, small ConfigMaps could be grouped with the workloads that reference them, but large ConfigMaps could be stored in units of their own, and a ConfigMap referenced by multiple workloads should be placed into its own unit.
  • Secrets shouldn't be stored in units. See recommendations in this blog post.

Ingested helm charts do not currently follow these guidelines in order to simplify the process of upgrading them from the charts.