bridge-impl

module
v0.1.27 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 7, 2026 License: MIT

README

Bridge Workers

Bridge Workers are specialized components designed to handle configuration management and deployment automation tasks. These workers interact with ConfigHub to perform operations such as applying configurations, refreshing states, importing resources, and destroying configurations. They support multiple toolchains and providers, including Kubernetes.

Documentation

For detailed information about specific components and workflows, refer to documents in the docs folder.

Custom Bridge Example

For a working hello-world bridge example, see hello-world-bridge in the examples repo.

This guide explains how to create custom bridges for ConfigHub. Bridges are adapters that connect ConfigHub to external systems, allowing you to apply, refresh, import, and destroy configuration resources on various targets.

Quick Start

Log into ConfigHub with the CLI:

cub auth login

Build the example in this directory:

go build
Running locally with cub worker run

The simplest way to run the example is with cub worker run, which automatically creates the worker and sets up the environment:

cub worker run --space $SPACE --executable ./hello-world-bridge hello-bridge

This will create the worker if it doesn't exist, set the required environment variables (CONFIGHUB_WORKER_ID, CONFIGHUB_WORKER_SECRET, CONFIGHUB_URL), and start the executable.

Running directly with environment variables

Alternatively, you can set up the environment manually:

eval "$(cub worker get-envs --space $SPACE hello-bridge)"
export EXAMPLE_BRIDGE_DIR=/tmp/confighub-example-bridge  # Optional: defaults to /tmp/confighub-example-bridge
./hello-world-bridge
Installing in a Kubernetes cluster

To deploy the worker in a Kubernetes cluster, first build and push a container image:

docker build -f Dockerfile -t my-registry/hello-world-bridge:latest .
docker push my-registry/hello-world-bridge:latest

Then install using cub worker install:

# Create the worker unit in ConfigHub
cub worker install --space $SPACE \
  --unit hello-bridge-unit \
  --target $TARGET \
  --image my-registry/hello-world-bridge:latest \
  hello-bridge

# Apply the worker unit to the cluster
cub unit apply --space $SPACE hello-bridge-unit

# Wait for the namespace and deployment, then install the secret
kubectl -n confighub wait --for=create deployment/hello-bridge --timeout=120s
cub worker install --space $SPACE \
  --export-secret-only \
  -n confighub \
  hello-bridge 2>/dev/null | kubectl apply -f -

# Wait for the worker to be ready
kubectl -n confighub rollout status deployment/hello-bridge --timeout=120s
Verifying the worker

Create a the base directory and a test subdirectory (which will become a target):

mkdir -p /tmp/confighub-example-bridge/dev

It should connect to ConfigHub and display:

[INFO] Starting hello-world-bridge example...
[INFO] Using base directory: /tmp/confighub-example-bridge
[INFO] Starting connector...

Note: You may see warnings about uninitialized loggers - these can be safely ignored.

Create a unit with some Kubernetes compliant YAML content:

cub unit create myapp test_input.yaml --target example-bridge-filesystem-kubernetes-yaml-dev

Apply the unit to your bridge target:

cub unit apply myapp

The bridge will write the configuration to /tmp/confighub-example-bridge/dev/myapp.yaml.

Bridge Operations

Bridges implement five core operation. An operation is always performed on a config unit. The following properties apply:

  • The unit in question must first be associated with a target made available by the bridge
  • The unit and the target must have the same toolchain. E.g. you cannot create a properties file as a unit and associate it with a kubernetes/yaml target.
1. Apply

Applies configuration to the target system. In this example, it creates/updates files on the filesystem.

cub unit apply myapp
2. Refresh

Reads the current state from the target and detects drift. Returns whether the live state matches the desired state.

cub unit refresh myapp
3. Import

Discover existing resources in the target system. In this example, it can theoretically discover files in a target subdir that it doesn't yet know about, but this feature is not implemented right now for this example.

4. Destroy

Removes configuration from the target system. In this example, it deletes files.

cub unit destroy myapp
5. Finalize

Performs cleanup operations after other actions. Implementation-specific.

Core Concepts

Bridge Interface

Every bridge must implement the Bridge interface:

type Bridge interface {
    Info(opts InfoOptions) BridgeInfo
    Apply(ctx BridgeContext, payload BridgePayload) error
    Refresh(ctx BridgeContext, payload BridgePayload) error
    Import(ctx BridgeContext, payload BridgePayload) error
    Destroy(ctx BridgeContext, payload BridgePayload) error
    Finalize(ctx BridgeContext, payload BridgePayload) error
}
Target Discovery

The Info() method returns available targets. In this example it treats a sub-directory as a target. In other (more realistic) use cases, a target may be a Kubernetes cluster represented by a kubecontext, it may be a namespace in a kube cluster or it may be an IaaS cloud identity.

Status Reporting

Bridges report operation progress using SendStatus(). For example:

startTime := time.Now()
// Send initial status
if err := ctx.SendStatus(&api.ActionResult{
    UnitID:            payload.UnitID,
    SpaceID:           payload.SpaceID,
    QueuedOperationID: payload.QueuedOperationID,
    ActionResultBaseMeta: api.ActionResultMeta{
        Action:    api.ActionApply,
        Result:    api.ActionResultNone,
        Status:    api.ActionStatusProgressing,
        Message:   fmt.Sprintf("Starting apply operation for %s", eb.name),
        StartedAt: startTime,
    },
}); err != nil {
    return err
}

// ... perform operation ...

terminatedAt := time.Now()
// Send completion status
return ctx.SendStatus(&api.ActionResult{
    UnitID:            payload.UnitID,
    SpaceID:           payload.SpaceID,
    QueuedOperationID: payload.QueuedOperationID,
    ActionResultBaseMeta: api.ActionResultMeta{
        Action:       api.ActionApply,
        Result:       api.ActionResultApplyCompleted,
        Status:       api.ActionStatusCompleted,
        Message:      fmt.Sprintf("Successfully wrote configuration to %s at %s", filepath, time.Now().Format(time.RFC3339)),
        StartedAt:    startTime,
        TerminatedAt: &terminatedAt,
    },
    Data:      payload.Data,
    LiveData:  payload.Data,
})
Drift Detection

The Refresh operation compares the live state as expected by ConfigHub with the actual live state in the infrastructure. In this example, it simply does a byte comparison between the unit data in ConfigHub and the file contents in the corresponding file.

Bridge Registration

Register your bridge with the dispatcher and pass the dispatcher to ConfighubConnector:

bridgeDispatcher := worker.NewBridgeDispatcher()
bridgeDispatcher.RegisterBridge(NewExampleBridge("example-bridge", baseDir))

connector, err := worker.NewConnector(worker.ConnectorOptions{
    WorkerID:         os.Getenv("CONFIGHUB_WORKER_ID"),
    WorkerSecret:     os.Getenv("CONFIGHUB_WORKER_SECRET"),
    ConfigHubURL:     os.Getenv("CONFIGHUB_URL"),
    BridgeDispatcher: &bridgeDispatcher,
})

Target Parameters

Bridges can accept parameters from targets. This example uses dir_name to determine which subdirectory to use:

func parseTargetParams(payload api.BridgeWorkerPayload) (string, error) {
    var params map[string]interface{}
    if len(payload.TargetParams) > 0 {
        if err := json.Unmarshal(payload.TargetParams, &params); err != nil {
            return "", fmt.Errorf("failed to parse target params: %v", err)
        }
    }

    // Get directory name from the parameter I set in Info()
    if dirName, ok := params["dir_name"].(string); ok && dirName != "" {
        return dirName, nil
    }

    // Default to "default" if no directory name found
    return "default", nil
}

Data Types

BridgePayload

Contains all information about the operation:

  • UnitID, SpaceID, QueuedOperationID - Identifiers
  • UnitSlug - Human-readable unit name
  • Data - Desired configuration (YAML/JSON)
  • LiveData - Last live configuration from previous operations
  • TargetParams - Target-specific parameters
  • ToolchainType, ProviderType - Configuration type info
ActionResult

Reports operation status and results:

  • ActionResultBaseMeta - Action type, status, messages, timing
  • Data - Updated configuration (for Import)
  • LiveData - Current state after operation

Best Practices

Error Handling

Always report errors with appropriate status:

if err != nil {
    return fmt.Errorf("failed to write file %s: %w", filepath, err)
}

For more complex error handling, you can send status updates before returning errors:

if err != nil {
    terminatedAt := time.Now()
    ctx.SendStatus(&api.ActionResult{
        UnitID:            payload.UnitID,
        SpaceID:           payload.SpaceID,
        QueuedOperationID: payload.QueuedOperationID,
        ActionResultBaseMeta: api.ActionResultMeta{
            Action:       api.ActionApply,
            Result:       api.ActionResultApplyFailed,
            Status:       api.ActionStatusFailed,
            Message:      fmt.Sprintf("Failed to write file: %v", err),
            StartedAt:    startTime,
            TerminatedAt: &terminatedAt,
        },
    })
    return fmt.Errorf("failed to write file %s: %w", filepath, err)
}
Progress Updates

For long-running operations, send periodic status updates:

  1. Initial "progressing" status when starting
  2. Intermediate updates for multi-step operations
  3. Final "completed" or "failed" status
Target Management
  • Targets are owned by the bridge and advertised to ConfigHub
  • Targets can in some cases be manually created in ConfigHub
  • Right now, the bridge is responsible for given each target a unique name within a space. This is not ideal and will be reconsidered.

Example Implementations

This filesystem bridge demonstrates basic concepts. Examples of real-world bridges might be:

  • Kubernetes Bridge: Apply YAML manifests to clusters
  • AWS Bridge: Manage cloud resources via APIs
  • Database Bridge: Execute schema migrations

Directories

Path Synopsis
Package argocdrenderer provides functionality for rendering ArgoCD Application resources to Kubernetes manifests by calling the ArgoCD API.
Package argocdrenderer provides functionality for rendering ArgoCD Application resources to Kubernetes manifests by calling the ArgoCD API.
Package fluxrenderer provides functionality for rendering Flux HelmRelease and Kustomization resources to Kubernetes manifests.
Package fluxrenderer provides functionality for rendering Flux HelmRelease and Kustomization resources to Kubernetes manifests.
Package ociutils provides utilities for generating OCI registry URLs for ConfigHub units and targets.
Package ociutils provides utilities for generating OCI registry URLs for ConfigHub units and targets.
third_party
fluxcd/helm-controller/loader
Package loader provides functionality for loading Helm charts from URLs.
Package loader provides functionality for loading Helm charts from URLs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL