Skip to content

Binding Controllers

The ClusterBinding and Binding controllers watch their respective binding objects and referenced Connections in the consumer cluster. Both use the reconciliation logic in engine/binding.

They are responsible for:

  • Resolving a Ready Connection and validating the requested exported APIs.
  • Installing or reading consumer schemas according to the Connection's policies.
  • Synchronizing selected related Secrets and ConfigMaps.
  • Reporting bound APIs, schema hashes, conflict counts, and readiness.
  • Cleaning up instances and related-resource copies when a binding is deleted.

A ClusterBinding operates cluster-wide. A Binding limits instance selection and related-resource processing to its namespace, but does not change CRD scope or make the dynamic instance informers namespace-scoped.

Overview

The chart shows normal reconciliation after the cleanup finalizer has been added. Binding and Connection events trigger reconciliation, with a 30-second periodic refresh for related resources and conflict counts.

flowchart TD
    start(["Binding or Connection event<br/>or periodic reconciliation"])
    get_binding(["Get binding"])
    exists{"Binding<br/>exists?"}
    connection{"Referenced Connection<br/>exists and is Ready?"}
    pending(["Set Ready=False<br/>with Pending"])
    provider(["Build provider client<br/>from Connection credentials"])
    schemas(["For each requested API<br/>Check export and process its schema"])
    counts(["Record boundAPIs and schema hashes<br/>Count instance conflict annotations"])
    missing{"Any APIs<br/>not exported?"}
    not_exported(["Set Ready=False<br/>with APINotExported"])
    waiting{"Waiting for an OpenAPI-source<br/>consumer CRD?"}
    related(["Sync selected related Secrets and ConfigMaps<br/>Remove copies that no longer match"])
    conditions(["Update Conflicts<br/>Set Synced=True and Ready=True"])
    status(["Persist changed status<br/>Requeue after 30 seconds"])
    stop(["Stop"])

    start --> get_binding
    get_binding --> exists
    exists -->|no| stop
    exists -->|yes| connection
    connection -->|no| pending
    connection -->|yes| provider
    provider --> schemas
    schemas --> counts
    counts --> missing
    missing -->|yes| not_exported
    missing -->|no| waiting
    waiting -->|yes| pending
    waiting -->|no| related
    related --> conditions
    pending --> status
    not_exported --> status
    conditions --> status
    status --> stop

For CRD, reconciliation pulls each exported provider schema with creation and updates controlled by pullPolicy and updatePolicy. For OpenAPI, it synthesizes a missing consumer CRD unless pullPolicy: None. An existing OpenAPI-source CRD is read, not refreshed by this controller.

A forbidden CRD pull sets PermissionDenied=True and Ready=False. Other API errors are returned for retry. Conflicts can coexist with Ready=True, and Synced=True describes setup, not proof that every instance has reached the provider. Instance synchronization runs separately.

Deletion

flowchart TD
    start(["Deleting binding with cleanup finalizer"])
    provider(["Try to obtain provider credentials"])
    next_api{"More APIs<br/>to clean up?"}
    covered{"Another non-deleting binding<br/>lists this API?"}
    schema{"Consumer CRD provides<br/>a usable API version?"}
    drain(["Drain instances in scope<br/>Release their syncer finalizers"])
    cluster_binding{"ClusterBinding?"}
    remove_crd(["Delete consumer CRD"])
    related(["Delete related-resource copies<br/>owned by this binding"])
    release(["Remove binding cleanup finalizer"])
    stop(["Stop"])

    start --> provider
    provider --> next_api
    next_api -->|yes| covered
    covered -->|yes| next_api
    covered -->|no| schema
    schema -->|no| next_api
    schema -->|yes| drain
    drain --> cluster_binding
    cluster_binding -->|yes| remove_crd
    cluster_binding -->|no| next_api
    remove_crd --> next_api
    next_api -->|no| related
    related --> release
    release --> stop

Draining requests deletion only for owned provider copies, unless an instance uses deletion-policy: Orphan. Provider-client and provider-read failures can leave copies behind, but actual provider-delete errors can block cleanup. Binding cleanup does not wait for provider finalizers as normal instance deletion does.

The overlap check is API-wide, not a comparison of namespaces or Connections. A last ClusterBinding can delete the consumer CRD even when it was externally installed. A namespaced Binding keeps the shared CRD. Read the cleanup limits before changing overlapping bindings or relying on automatic pruning.

Implementation: binding reconcilers, related resources, and cleanup.