controller-runtimesigs.k8s.io/controller-runtime/pkg/builder Index | Examples | Files

package builder

import "sigs.k8s.io/controller-runtime/pkg/builder"

Package builder wraps other controller-runtime libraries and exposes simple patterns for building common Controllers.

Projects built with the builder package can trivially be rebased on top of the underlying packages if the project requires more customized behavior in the future.

Index

Examples

Variables

var MatchEveryOwner = &matchEveryOwner{}

MatchEveryOwner determines whether the watch should be filtered based on controller ownership. As in, when the OwnerReference.Controller field is set.

If passed as an option, the handler receives notification for every owner of the object with the given type. If unset (default), the handler receives notification only for the first OwnerReference with `Controller: true`.

var (
	// OnlyMetadata tells the controller to *only* cache metadata, and to watch
	// the API server in metadata-only form. This is useful when watching
	// lots of objects, really big objects, or objects for which you only know
	// the GVK, but not the structure. You'll need to pass
	// metav1.PartialObjectMetadata to the client when fetching objects in your
	// reconciler, otherwise you'll end up with a duplicate structured or
	// unstructured cache.
	//
	// When watching a resource with OnlyMetadata, for example the v1.Pod, you
	// should not Get and List using the v1.Pod type. Instead, you should use
	// the special metav1.PartialObjectMetadata type.
	//
	// ❌ Incorrect:
	//
	//   pod := &v1.Pod{}
	//   mgr.GetClient().Get(ctx, nsAndName, pod)
	//
	// ✅ Correct:
	//
	//   pod := &metav1.PartialObjectMetadata{}
	//   pod.SetGroupVersionKind(schema.GroupVersionKind{
	//       Group:   "",
	//       Version: "v1",
	//       Kind:    "Pod",
	//   })
	//   mgr.GetClient().Get(ctx, nsAndName, pod)
	//
	// In the first case, controller-runtime will create another cache for the
	// concrete type on top of the metadata cache; this increases memory
	// consumption and leads to race conditions as caches are not in sync.
	OnlyMetadata = projectAs(projectAsMetadata)
)

Types

type Builder

type Builder = TypedBuilder[reconcile.Request]

Builder builds a Controller.

Example

This example creates a simple application ControllerManagedBy that is configured for ReplicaSets and Pods.

* Create a new application for ReplicaSets that manages Pods owned by the ReplicaSet and calls into ReplicaSetReconciler.

* Start the application.

Code:play 

package main

import (
	"context"
	"fmt"
	"os"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"

	"sigs.k8s.io/controller-runtime/pkg/builder"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/config"

	logf "sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/log/zap"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/manager/signals"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func main() {
	logf.SetLogger(zap.New())

	log := logf.Log.WithName("builder-examples")

	mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{})
	if err != nil {
		log.Error(err, "could not create manager")
		os.Exit(1)
	}

	err = builder.
		ControllerManagedBy(mgr).  // Create the ControllerManagedBy
		For(&appsv1.ReplicaSet{}). // ReplicaSet is the Application API
		Owns(&corev1.Pod{}).       // ReplicaSet owns Pods created by it
		Complete(&ReplicaSetReconciler{
			Client: mgr.GetClient(),
		})
	if err != nil {
		log.Error(err, "could not create controller")
		os.Exit(1)
	}

	if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
		log.Error(err, "could not start manager")
		os.Exit(1)
	}
}

// ReplicaSetReconciler is a simple ControllerManagedBy example implementation.
type ReplicaSetReconciler struct {
	client.Client
}

// Implement the business logic:
// This function will be called when there is a change to a ReplicaSet or a Pod with an OwnerReference
// to a ReplicaSet.
//
// * Read the ReplicaSet
// * Read the Pods
// * Set a Label on the ReplicaSet with the Pod count.
func (a *ReplicaSetReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {

	rs := &appsv1.ReplicaSet{}
	err := a.Get(ctx, req.NamespacedName, rs)
	if err != nil {
		return reconcile.Result{}, err
	}

	pods := &corev1.PodList{}
	err = a.List(ctx, pods, client.InNamespace(req.Namespace), client.MatchingLabels(rs.Spec.Template.Labels))
	if err != nil {
		return reconcile.Result{}, err
	}

	rs.Labels["pod-count"] = fmt.Sprintf("%v", len(pods.Items))
	err = a.Update(ctx, rs)
	if err != nil {
		return reconcile.Result{}, err
	}

	return reconcile.Result{}, nil
}
Example (Metadata_only)

Code:play 

package main

import (
	"context"
	"fmt"
	"os"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"sigs.k8s.io/controller-runtime/pkg/builder"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/config"

	logf "sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/log/zap"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/manager/signals"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func main() {
	logf.SetLogger(zap.New())

	log := logf.Log.WithName("builder-examples")

	mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{})
	if err != nil {
		log.Error(err, "could not create manager")
		os.Exit(1)
	}

	cl := mgr.GetClient()
	err = builder.
		ControllerManagedBy(mgr).                  // Create the ControllerManagedBy
		For(&appsv1.ReplicaSet{}).                 // ReplicaSet is the Application API
		Owns(&corev1.Pod{}, builder.OnlyMetadata). // ReplicaSet owns Pods created by it, and caches them as metadata only
		Complete(reconcile.Func(func(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
			// Read the ReplicaSet
			rs := &appsv1.ReplicaSet{}
			err := cl.Get(ctx, req.NamespacedName, rs)
			if err != nil {
				return reconcile.Result{}, client.IgnoreNotFound(err)
			}

			// List the Pods matching the PodTemplate Labels, but only their metadata
			var podsMeta metav1.PartialObjectMetadataList
			err = cl.List(ctx, &podsMeta, client.InNamespace(req.Namespace), client.MatchingLabels(rs.Spec.Template.Labels))
			if err != nil {
				return reconcile.Result{}, client.IgnoreNotFound(err)
			}

			// Update the ReplicaSet
			rs.Labels["pod-count"] = fmt.Sprintf("%v", len(podsMeta.Items))
			err = cl.Update(ctx, rs)
			if err != nil {
				return reconcile.Result{}, err
			}

			return reconcile.Result{}, nil
		}))
	if err != nil {
		log.Error(err, "could not create controller")
		os.Exit(1)
	}

	if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
		log.Error(err, "could not start manager")
		os.Exit(1)
	}
}

func ControllerManagedBy

func ControllerManagedBy(m manager.Manager) *Builder

ControllerManagedBy returns a new controller builder that will be started by the provided Manager.

type ForInput

type ForInput struct {
	// contains filtered or unexported fields
}

ForInput represents the information set by the For method.

type ForOption

type ForOption interface {
	// ApplyToFor applies this configuration to the given for input.
	ApplyToFor(*ForInput)
}

ForOption is some configuration that modifies options for a For request.

type OwnsInput

type OwnsInput struct {
	// contains filtered or unexported fields
}

OwnsInput represents the information set by Owns method.

type OwnsOption

type OwnsOption interface {
	// ApplyToOwns applies this configuration to the given owns input.
	ApplyToOwns(*OwnsInput)
}

OwnsOption is some configuration that modifies options for an owns request.

type Predicates

type Predicates struct {
	// contains filtered or unexported fields
}

Predicates filters events before enqueuing the keys.

func WithPredicates

func WithPredicates(predicates ...predicate.Predicate) Predicates

WithPredicates sets the given predicates list.

func (Predicates) ApplyToFor

func (w Predicates) ApplyToFor(opts *ForInput)

ApplyToFor applies this configuration to the given ForInput options.

func (Predicates) ApplyToOwns

func (w Predicates) ApplyToOwns(opts *OwnsInput)

ApplyToOwns applies this configuration to the given OwnsInput options.

func (Predicates) ApplyToWatches

func (w Predicates) ApplyToWatches(opts untypedWatchesInput)

ApplyToWatches applies this configuration to the given WatchesInput options.

type TypedBuilder

type TypedBuilder[request comparable] struct {
	// contains filtered or unexported fields
}

TypedBuilder builds a Controller. The request is the request type that is passed to the workqueue and then to the Reconciler. The workqueue de-duplicates identical requests.

func TypedControllerManagedBy

func TypedControllerManagedBy[request comparable](m manager.Manager) *TypedBuilder[request]

TypedControllerManagedBy returns a new typed controller builder that will be started by the provided Manager.

func (*TypedBuilder[request]) Build

func (blder *TypedBuilder[request]) Build(r reconcile.TypedReconciler[request]) (controller.TypedController[request], error)

Build builds the Application Controller and returns the Controller it created.

func (*TypedBuilder[request]) Complete

func (blder *TypedBuilder[request]) Complete(r reconcile.TypedReconciler[request]) error

Complete builds the Application Controller.

func (*TypedBuilder[request]) For

func (blder *TypedBuilder[request]) For(object client.Object, opts ...ForOption) *TypedBuilder[request]

For defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete / update events by *reconciling the object*.

This is the equivalent of calling Watches(source.Kind(cache, &Type{}, &handler.EnqueueRequestForObject{})).

func (*TypedBuilder[request]) Named

func (blder *TypedBuilder[request]) Named(name string) *TypedBuilder[request]

Named sets the name of the controller to the given name. The name shows up in metrics, among other things, and thus should be a prometheus compatible name (underscores and alphanumeric characters only).

By default, controllers are named using the lowercase version of their kind.

The name must be unique as it is used to identify the controller in metrics and logs.

func (*TypedBuilder[request]) Owns

func (blder *TypedBuilder[request]) Owns(object client.Object, opts ...OwnsOption) *TypedBuilder[request]

Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to create / delete / update events by *reconciling the owner object*.

The default behavior reconciles only the first controller-type OwnerReference of the given type. Use Owns(object, builder.MatchEveryOwner) to reconcile all owners.

By default, this is the equivalent of calling Watches(source.Kind(cache, &Type{}, handler.EnqueueRequestForOwner([...], &OwnerType{}, OnlyControllerOwner()))).

func (*TypedBuilder[request]) Watches

func (blder *TypedBuilder[request]) Watches(
	object client.Object,
	eventHandler handler.TypedEventHandler[client.Object, request],
	opts ...WatchesOption,
) *TypedBuilder[request]

Watches defines the type of Object to watch, and configures the ControllerManagedBy to respond to create / delete / update events by *reconciling the object* with the given EventHandler.

This is the equivalent of calling WatchesRawSource(source.Kind(cache, object, eventHandler, predicates...)).

func (*TypedBuilder[request]) WatchesMetadata

func (blder *TypedBuilder[request]) WatchesMetadata(
	object client.Object,
	eventHandler handler.TypedEventHandler[client.Object, request],
	opts ...WatchesOption,
) *TypedBuilder[request]

WatchesMetadata is the same as Watches, but forces the internal cache to only watch PartialObjectMetadata.

This is useful when watching lots of objects, really big objects, or objects for which you only know the GVK, but not the structure. You'll need to pass metav1.PartialObjectMetadata to the client when fetching objects in your reconciler, otherwise you'll end up with a duplicate structured or unstructured cache.

When watching a resource with metadata only, for example the v1.Pod, you should not Get and List using the v1.Pod type. Instead, you should use the special metav1.PartialObjectMetadata type.

❌ Incorrect:

pod := &v1.Pod{}
mgr.GetClient().Get(ctx, nsAndName, pod)

✅ Correct:

pod := &metav1.PartialObjectMetadata{}
pod.SetGroupVersionKind(schema.GroupVersionKind{
    Group:   "",
    Version: "v1",
    Kind:    "Pod",
})
mgr.GetClient().Get(ctx, nsAndName, pod)

In the first case, controller-runtime will create another cache for the concrete type on top of the metadata cache; this increases memory consumption and leads to race conditions as caches are not in sync.

func (*TypedBuilder[request]) WatchesRawSource

func (blder *TypedBuilder[request]) WatchesRawSource(src source.TypedSource[request]) *TypedBuilder[request]

WatchesRawSource exposes the lower-level ControllerManagedBy Watches functions through the builder.

WatchesRawSource does not respect predicates configured through WithEventFilter.

WatchesRawSource makes it possible to use typed handlers and predicates with `source.Kind` as well as custom source implementations.

func (*TypedBuilder[request]) WithEventFilter

func (blder *TypedBuilder[request]) WithEventFilter(p predicate.Predicate) *TypedBuilder[request]

WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually trigger reconciliations. For example, filtering on whether the resource version has changed. Given predicate is added for all watched objects and thus must be able to deal with the type of all watched objects.

Defaults to the empty list.

func (*TypedBuilder[request]) WithLogConstructor

func (blder *TypedBuilder[request]) WithLogConstructor(logConstructor func(*request) logr.Logger) *TypedBuilder[request]

WithLogConstructor overrides the controller options's LogConstructor.

func (*TypedBuilder[request]) WithOptions

func (blder *TypedBuilder[request]) WithOptions(options controller.TypedOptions[request]) *TypedBuilder[request]

WithOptions overrides the controller options used in doController. Defaults to empty.

type WatchesInput

type WatchesInput[request comparable] struct {
	// contains filtered or unexported fields
}

WatchesInput represents the information set by Watches method.

type WatchesOption

type WatchesOption interface {
	// ApplyToWatches applies this configuration to the given watches options.
	ApplyToWatches(untypedWatchesInput)
}

WatchesOption is some configuration that modifies options for a watches request.

type WebhookBuilder

type WebhookBuilder struct {
	// contains filtered or unexported fields
}

WebhookBuilder builds a Webhook.

Example

This example use webhook builder to create a simple webhook that is managed by a manager for CRD ChaosPod. And then start the manager.

Code:play 

package main

import (
	"os"

	"sigs.k8s.io/controller-runtime/pkg/builder"
	"sigs.k8s.io/controller-runtime/pkg/client/config"

	logf "sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/manager/signals"

	examplegroup "sigs.k8s.io/controller-runtime/examples/crd/pkg"
)

func main() {
	var log = logf.Log.WithName("webhookbuilder-example")

	mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{})
	if err != nil {
		log.Error(err, "could not create manager")
		os.Exit(1)
	}

	err = builder.
		WebhookManagedBy(mgr).         // Create the WebhookManagedBy
		For(&examplegroup.ChaosPod{}). // ChaosPod is a CRD.
		Complete()
	if err != nil {
		log.Error(err, "could not create webhook")
		os.Exit(1)
	}

	if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
		log.Error(err, "could not start manager")
		os.Exit(1)
	}
}

func WebhookManagedBy

func WebhookManagedBy(m manager.Manager) *WebhookBuilder

WebhookManagedBy returns a new webhook builder.

func (*WebhookBuilder) Complete

func (blder *WebhookBuilder) Complete() error

Complete builds the webhook.

func (*WebhookBuilder) For

func (blder *WebhookBuilder) For(apiType runtime.Object) *WebhookBuilder

For takes a runtime.Object which should be a CR. If the given object implements the admission.Defaulter interface, a MutatingWebhook will be wired for this type. If the given object implements the admission.Validator interface, a ValidatingWebhook will be wired for this type.

func (*WebhookBuilder) RecoverPanic

func (blder *WebhookBuilder) RecoverPanic(recoverPanic bool) *WebhookBuilder

RecoverPanic indicates whether panics caused by the webhook should be recovered. Defaults to true.

func (*WebhookBuilder) WithCustomPath

func (blder *WebhookBuilder) WithCustomPath(customPath string) *WebhookBuilder

WithCustomPath overrides the webhook's default path by the customPath Deprecated: WithCustomPath should not be used anymore. Please use WithValidatorCustomPath or WithDefaulterCustomPath instead.

func (*WebhookBuilder) WithDefaulter

func (blder *WebhookBuilder) WithDefaulter(defaulter admission.CustomDefaulter, opts ...admission.DefaulterOption) *WebhookBuilder

WithDefaulter takes an admission.CustomDefaulter interface, a MutatingWebhook with the provided opts (admission.DefaulterOption) will be wired for this type.

func (*WebhookBuilder) WithDefaulterCustomPath

func (blder *WebhookBuilder) WithDefaulterCustomPath(customPath string) *WebhookBuilder

WithDefaulterCustomPath overrides the path of the Defaulter.

func (*WebhookBuilder) WithLogConstructor

func (blder *WebhookBuilder) WithLogConstructor(logConstructor func(base logr.Logger, req *admission.Request) logr.Logger) *WebhookBuilder

WithLogConstructor overrides the webhook's LogConstructor.

func (*WebhookBuilder) WithValidator

func (blder *WebhookBuilder) WithValidator(validator admission.CustomValidator) *WebhookBuilder

WithValidator takes a admission.CustomValidator interface, a ValidatingWebhook will be wired for this type.

func (*WebhookBuilder) WithValidatorCustomPath

func (blder *WebhookBuilder) WithValidatorCustomPath(customPath string) *WebhookBuilder

WithValidatorCustomPath overrides the path of the Validator.

Source Files

controller.go doc.go options.go webhook.go

Version
v0.21.0 (latest)
Published
May 20, 2025
Platform
linux/amd64
Imports
24 packages
Last checked
24 minutes ago

Tools for package owners.