Skip to content

builder

Overview

The main role of Builder is:

  1. Create a Controller from the given Reconciler
  2. Configure target resources for the controller
  3. Register the controller to the Manager

About how the registered controllers are triggered, you can study in Manager. The controller registered to the manager by Builder will be in runnables.Others in Manager object, which will be started by Manager.Start().

Types

Builder

// Builder builds a Controller.
type Builder struct {
    forInput         ForInput
    ownsInput        []OwnsInput
    watchesInput     []WatchesInput
    mgr              manager.Manager
    globalPredicates []predicate.Predicate
    ctrl             controller.Controller
    ctrlOptions      controller.Options
    name             string
}

ControllerManagedBy: Initialize Builder with a Manager

Initialize a Builder with the specified manager.

func ControllerManagedBy(m manager.Manager) *Builder {
 return &Builder{mgr: m}
}

For, Owns, and Watches: Define what object to watch

  1. For(object client.Object, opts ...ForOption) *Builder: only one resource can be configured. Same as
    Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
    
  2. Owns(object client.Object, opts ...OwnsOption) *Builder: 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. Same as the following code:
    Watches(object, handler.EnqueueRequestForOwner([...], ownerType, OnlyControllerOwner()))
    
    EnqueueRequestForOwner: Extract owner object from ownerReferences and enqueue it to the queue.
  3. Watches(src source.Source, eventhandler handler.EventHandler, opts ...WatchesOption) *Builder: Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using Owns or For instead of Watches directly.

Example:

err = builder.
  ControllerManagedBy(mgr).  // Create the ControllerManagedBy
  For(&alpha1v1.Foo{}). // Foo is the Application API
  Owns(&corev1.Pod{}).       // Foo owns Pods created by it
  Complete(&FooReconciler{})

Complete: Receive reconciler and build a controller

Receive reconcile.Reconciler and call Build:

// Complete builds the Application Controller.
func (blder *Builder) Complete(r reconcile.Reconciler) error {
    _, err := blder.Build(r)
    return err
}

Build: Create a controller and return the controller.

func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, error) {
    ...
    // Set the ControllerManagedBy
    if err := blder.doController(r); err != nil {
        return nil, err
    }

    // Set the Watch
    if err := blder.doWatch(); err != nil {
        return nil, err
    }
    return blder.ctrl, nil
}
  1. bldr.doController to register the controler to the buidler
    1. Create a new controller.
      blder.ctrl, err = newController(controllerName, blder.mgr, ctrlOptions)
      
    2. the controller is added to manager.runnables.Others by Manager.Add(Runnable) in newController. (controller)
  2. bldr.doWatch to start watching the target resources configured by For, Owns, and Watches.
    1. The actual implementation of Watch function is in the controller

Convert client.Object to Source

  1. Controller.Watch needs Source as the first argument.
    Watch(src source.Source, eventhandler handler.EventHandler, predicates ...predicate.Predicate) error
    
  2. client.Object is set in ForInput, OwnsInput, and WatchesInput for For, Owns, and Watches respectively.
  3. Before calling Controller.Watch, the client.Object needs to be projected into Source based on objectProjection:

    1. projectAsNormal: Use the object as it is. (In most cases)
    2. projectAsMetadata: Extract only metadata.
    typeForSrc, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
    if err != nil {
        return err
    }
    src := &source.Kind{Type: typeForSrc}
    

    Kind implements the Source interface.

    type Kind struct {
        // Type is the type of object to watch.  e.g. &v1.Pod{}
        Type client.Object
    
        // cache used to watch APIs
        cache cache.Cache
    
        // started may contain an error if one was encountered during startup. If its closed and does not
        // contain an error, startup and syncing finished.
        started     chan error
        startCancel func()
    }