MCPcopy Index your code
hub / github.com/reconcilerio/runtime

github.com/reconcilerio/runtime @v0.26.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.26.1 ↗ · + Follow
1,323 symbols 4,557 edges 87 files 732 documented · 55% updated 5d agov0.26.1 · 2026-04-30★ 504 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Reconciler.io runtime

CI GoDoc Go Report Card codecov

reconciler.io is an opinionated framework for authoring and testing Kubernetes reconcilers using controller-runtime project. controller-runtime provides infrastructure for creating and operating controllers, but provides little support for the business logic of implementing a reconciler within the controller. The Reconciler interface provided by controller-runtime is the primary hand-off point with reconciler.io.

Within an existing Kubebuilder or controller-runtime project, reconcilers.io may be adopted incrementally without disrupting existing controllers. A common approach for adopting reconciler.io runtime to use the testing support to test existing reconcilers in a project. If there is a use case runtime does not handle well, you may drop down to the controller-runtime APIs directly, or use any other library that is compatible with controller-runtime.

Reconcilers

Reconcilers can operate on three different types of objects: - structured types (e.g. corev1.Pod) - unstructured types (e.g. unstructured.Unstructured) - semi-structured duck types (e.g. PodSpecable, ProvisionedService)

Structured types are often the best choice as they allow easy interaction with the full object and have full client support. The type must be registered with the Scheme. The type must be pre-defined and compiled into the controller.

Unstructured types are useful when the resources are not known at compile time and full access to the resource and client methods is desired. Since the type is not known in advance, it cannot be registered with the scheme. Interacting with the object is difficult as traversing the object requires lots of casts or reflection. The TypeMeta APIVersion and Kind fields must be defined for the client to operate on the object.

Semi-structured duck types offer a middle ground. They are strongly typed, but only cover a subset of the full object. They are intended to facilitate normalized operations across a number of concrete types that share a common subset of their own schema. The concrete objects compatible with this type are not required to be known at compile time. Because duck types are not full objects, client operations for Create and Update are disallowed (Patch and Apply are available). Like unstructured objects, the duck type should not be registered in the scheme, and the TypeMeta APIVersion and Kind fields must be defined for the client to operate on the object.

The controller-runtime client is able to work with structured and unstructured objects natively, reconciler.io runtime adds support for duck typed objects via the duck.NewDuckAwareClientWrapper.

ResourceReconciler

A ResourceReconciler (formerly ParentReconciler) is responsible for orchestrating the reconciliation of a single resource. The reconciler delegates the manipulation of other resources to SubReconcilers.

The resource reconciler is responsible for: - fetching the resource being reconciled - creating a stash to pass state between sub reconcilers - passing the resource to each sub reconciler in turn - initialize conditions on the status by calling status.InitializeConditions() if defined (not available for Unstructured resources) - normalizing the .status.conditions[].lastTransitionTime for status conditions that are metav1.Condition (the previous timestamp is preserved if the condition is otherwise unchanged) (not available for Unstructured resources) - reflects the observed generation on the status (not available for Unstructured resources) - updates the resource status if it was modified - logging the reconcilers activities - records events for mutations and errors

The implementor is responsible for: - defining the set of sub reconcilers

The processing of a specific request or resource may be skipped by implementing and returning true from either SkipRequest, or SkipResource respectively.

Example:

Resource reconcilers tend to be quite simple, as they delegate their work to sub reconcilers. We'll use an example from projectriff of the Function resource, which uses Kpack to build images from a git repo. In this case the FunctionTargetImageReconciler resolves the target image for the function, and FunctionChildImageReconciler creates a child Kpack Image resource based on the resolve value.

func FunctionReconciler(c reconcilers.Config) *reconcilers.ResourceReconciler[*buildv1alpha1.Function] {
    return &reconcilers.ResourceReconciler[*buildv1alpha1.Function]{
        Name: "Function",
        Reconciler: reconcilers.Sequence[*buildv1alpha1.Function]{
            FunctionTargetImageReconciler(c),
            FunctionChildImageReconciler(c),
        },
        Config: c,
    }
}

full source

Recommended RBAC:

Replace <group> and <resource> with values for the reconciled resource type.

// +kubebuilder:rbac:groups=<group>,resources=<resource>,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=<group>,resources=<resource>/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=core;events.k8s.io,resources=events,verbs=get;list;watch;create;update;patch;delete

or

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: # any name that is bound to the ServiceAccount used by the client
rules:
- apiGroups: ["<group>"]
  resources: ["<resource>"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["<group>"]
  resources: ["<resource>/status"]
  verbs: ["get", "update", "patch"]
- apiGroups: ["core", "events.k8s.io"]
  resources: ["events"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

AggregateReconciler

An AggregateReconciler is responsible for synthesizing a single resource, aggregated from other state. The AggregateReconciler is a fusion of the ResourceReconciler and ChildReconciler. Instead of operating on all resources of a type, it will only operate on a specific resource identified by the type and request (namespace and name). Unlike the child reconciler, the "parent" and "child" resources are the same.

The aggregate reconciler is responsible for: - fetching the resource being reconciled - creating a stash to pass state between sub reconcilers - passing the resource to each sub reconciler in turn - creates the resource if it does not exist - updates the resource if it drifts from the desired state - deletes the resource if no longer desired - logging the reconcilers activities - records events for mutations and errors

The implementor is responsible for: - specifying the type, namespace and name of the aggregate resource - defining the desired state - merging the actual resource with the desired state (often as simple as copying the spec and labels)

Example:

Aggregate reconcilers resemble a simplified child reconciler with many of the same methods combined directly into a parent reconciler. The Reconcile method is used to collect reference data and the DesiredResource method defines the desired state. Unlike with a child reconciler, the desired resource may be a direct mutation of the argument.

In the example, we are controlling and existing ValidatingWebhookConfiguration named my-trigger (defined by Request). Based on other state in the cluster, the Reconcile method delegates to DeriveWebhookRules() to stash the rules for the webhook. Those rules are retrieved in the DesiredResource method, augmenting the ValidatingWebhookConfiguration. The MergeBeforeUpdate function is responsible for merging the desired state into the actual resource, when there is a significant change, the resource is updated on the api server.

The resulting ValidatingWebhookConfiguration will have the current desired rules defined by this reconciler, combined with existing state like the location of the webhook server, and other policies.

// AdmissionTriggerReconciler reconciles a ValidatingWebhookConfiguration object to
// dynamically be notified of resource mutations. A less reliable, but potentially more
// efficient than an informer watching each tracked resource.
func AdmissionTriggerReconciler(c reconcilers.Config) *reconcilers.AggregateReconciler[*admissionregistrationv1.ValidatingWebhookConfiguration] {
    return &reconcilers.AggregateReconciler[*admissionregistrationv1.ValidatingWebhookConfiguration]{
        Name:     "AdmissionTrigger",
        Request:  reconcilers.Request{
            NamesspacedName: types.NamesspacedName{
                // no namespace since ValidatingWebhookConfiguration is cluster scoped
                Name: "my-trigger",
            },
        },
        Reconciler: reconcilers.Sequence[*admissionregistrationv1.ValidatingWebhookConfiguration]{
            DeriveWebhookRules(),
        },
        DesiredResource: func(ctx context.Context, resource *admissionregistrationv1.ValidatingWebhookConfiguration) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
            // assumes other aspects of the webhook config are part of a preexisting
            // install, and that there is a server ready to receive the requests.
            rules := RetrieveWebhookRules(ctx)
            resource.Webhooks[0].Rules = rules
            return resource, nil
        },
        MergeBeforeUpdate: func(current, desired *admissionregistrationv1.ValidatingWebhookConfiguration) {
            current.Webhooks[0].Rules = desired.Webhooks[0].Rules
        },
        Sanitize: func(resource *admissionregistrationv1.ValidatingWebhookConfiguration) interface{} {
            return resource.Webhooks[0].Rules
        },
        Config: c,
    }
}

full source

Recommended RBAC:

Replace <group> and <resource> with values for the reconciled resource type.

// +kubebuilder:rbac:groups=<group>,resources=<resource>,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core;events.k8s.io,resources=events,verbs=get;list;watch;create;update;patch;delete

or

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: # any name that is bound to the ServiceAccount used by the client
rules:
- apiGroups: ["<group>"]
  resources: ["<resource>"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["core", "events.k8s.io"]
  resources: ["events"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

SubReconciler

The SubReconciler interface defines the contract between the host and sub reconcilers.

SyncReconciler

The SyncReconciler is the minimal type-aware sub reconciler. It is used to manage a portion of

Extension points exported contracts — how you extend this code

Defaulter (Interface)
Alternative interface similar to admission.Defaulter, but requires the object being defaulted is the receiver rather tha [9 …
validation/default.go
Tracker (Interface)
Tracker defines the interface through which an object can register that it is tracking another object by reference. [1 …
tracker/interface.go
ConditionsAccessor (Interface)
ConditionsAccessor is the interface for a Resource that implements the getter and setter for accessing a Condition colle [1 …
apis/conditionset.go
SchemeAccessor (Interface)
(no doc) [2 implementers]
duck/client.go
VerifyFunc (FuncType)
VerifyFunc is a verification function for a reconciler's result
testing/reconciler.go
Differ (Interface)
(no doc) [2 implementers]
testing/diff.go
SubReconcilerFactory (FuncType)
SubReconcilerFactory returns a Reconciler.Interface to perform reconciliation of a test case, ActionRecorderList/EventLi
testing/subreconciler.go
SubReconciler (Interface)
SubReconciler are participants in a larger reconciler request. The resource being reconciled is passed directly to the s
reconcilers/reconcilers.go

Core symbols most depended-on inside this repo

AddField
called by 151
internal/resources/dies/dies.go
Run
called by 90
testing/reconciler.go
Error
called by 70
reconcilers/flow.go
Status
called by 63
testing/client.go
Validate
called by 44
validation/validation.go
ConditionsDie
called by 42
internal/resources/dies/dies.go
GetName
called by 36
testing/client.go
RetrieveConfigOrDie
called by 33
reconcilers/reconcilers.go

Shape

Method 987
Function 191
Struct 110
Interface 15
TypeAlias 13
FuncType 7

Languages

Go100%

Modules by API surface

internal/resources/dies/zz_generated.die.go539 symbols
internal/resources/zz_generated.deepcopy.go72 symbols
testing/client.go44 symbols
duck/client.go35 symbols
apis/conditionset.go35 symbols
reconcilers/flow.go31 symbols
testing/config.go30 symbols
testing/diff.go28 symbols
stash/stash.go25 symbols
reconcilers/reconcilers.go25 symbols
internal/resources/resource_with_unexported_fields.go20 symbols
reconcilers/child.go17 symbols

For agents

$ claude mcp add runtime \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page