objects

package module
v0.0.0-...-0443994 Latest Latest
Warning

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

Go to latest
Published: Jan 28, 2024 License: Apache-2.0 Imports: 24 Imported by: 0

README

objects

import "github.com/kubespress/objects"

Package objects contains utilities for managing Kubernetes objects, either individually or as a larger set. It is designed for use within Kubebuilder and reuses many of the interfaces from the controller-runtime project.

Builders

Builders and the New method are the main entrypoint of this library, they are used to define and construct Kubernetes objects using utility methods. These methods allow common fields to be set such as the namespace, name and owners references while also allowing for custom build functions to be included in order to define more bespoke object customizations.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithName("my-awesome-application").
	WithControllerReference(parentObject)
Client

An Client is a type of client the builder can create. It manages asingle Kubernetes object and provides utility methods to create, update and delete it. The namespace and name of the object are defined by the builder, the client takes this information and can determine if the object exists, and if it is up to date. Using this information utility methods like CreateIfNotExists and UpdateIfRequired are provided. The CreateOrUpdate method will be the standard method used by most reconcilers as it ensures the object exists and is up to date with a single call, this coupled with a controller reference handles most required functionality.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithName("my-awesome-application").
	WithControllerReference(parentObject).
	Finalize().Client()
SetClient

An SetClient is a type of client the builder can create. It manages "sets" of objects. When constructing a SetClient a desired replica count, SetStrategy and a label selector are provided. This allows the client to discover existing objects as well as determine if objects need to be created, updated or deleted. Using this strategy the name provided by the builder is ignored as the SetStrategy takes over this function.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithControllerReference(parentObject).
	Finalize().
	SetClient(objects.GeneratedNameSetStrategy("my-awesome-application", 5), map[string]string{"owner": parentObject.GetName()})
Update strategies

An update strategy is what is used by the package to determine if an object is out of date and requires and update. See the implementations below for more information about included strategies.

Set strategies

When dealing with sets of objects, objects can name the object using many different strategies. The naming strategy also defines what objects are deleted first during scale-down. See the implementations below for more information about included strategies.

Hooks

Hooks can be provided to the builder to allow functions to be called under certain events. This is designed to add a point that observability can be injected into the clients. For example a hook to emit a Kubernetes event when an object is create/updated/deleted is included in the library.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithName("my-awesome-application").
	WithControllerReference(parentObject).
	WithHook(EventEmitterHook(eventRecorder, parentObject))
Usage

Inlining all the builders into a Reconcile method can make it very verbose, so instead it is recommended to break out the construction of the client into a separate method that be invoked by the client

func (r *ExampleReconciler) ServiceAccount(ex *examplev1.Example) *objects.Client[*corev1.ServiceAccount] {
    // ServiceAccounts don't have any fields we want to set, so its just an object
    objects.New[*corev1.ServiceAccount](k8sClient).
        WithNamespace(ex.Namespace).
        WithName(ex.Name).
        WithControllerReference(ex).
        Finalize().Client()
}

func (r *ExampleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    ...
    // The most basic usage is using the CreateOrUpdate method to ensure the object is up to date,
    // deletion is handled by the OwnerReference.
    if err := r.ServiceAccount(&example).CreateOrUpdate(ctx); err != nil {
        return ctrl.Result{}, err
    }
    ...
}
Example (Conditional Updates)

In this example we create a `PersistentVolumeClaim`. We also update the `PersistentVolumeClaim` if the parent object is updated in order to expand volume automatically. The only field that will be updated is the requested volume size, and this will only ever get larger.

// Create client to manage persistent volume claims
pvcClient := objects.New[*corev1.PersistentVolumeClaim](k8sClient).
	WithNamespace(parentObject.GetNamespace()). // Set the namespace of the PVC
	WithName(parentObject.GetName()).           // Set the name of the PVC
	WithControllerReference(parentObject).
	WithBuilder(func(pvc *corev1.PersistentVolumeClaim, action objects.Action) error {
		// If this is a create operation, just copy the template
		if action == objects.ActionCreate {
			// Copy the template into the PVC object
			*pvc = volumeClaimTemplate
		}

		// If this is an update we only want to update the requested size, even then we only want to expand the
		// volume. If a user wants more drastic action then they should update the PVC themselves.
		desiredStorage := volumeClaimTemplate.Spec.Resources.Requests.Storage()
		if action == objects.ActionUpdate && pvc.Spec.Resources.Requests.Storage().Cmp(*desiredStorage) == -1 {
			pvc.Spec.Resources.Requests[corev1.ResourceStorage] = *desiredStorage
		}

		// Return no error
		return nil
	}).
	Finalize().Client()

// Ensure pvc exists and is up to date
if err := pvcClient.CreateOrUpdate(ctx); err != nil {
	// Handle error
}

Example (Create A Pod)

This example shows how a single pod can be managed by the builder, since pods are mostly immutable they cannot use the CreateOrUpdate method, instead out of date pods are deleted.

Pods are determined to be out of date using the "HashUpdateStrategy", a strategy that uses an annotation on the Pod to determine if the Pod needs an update.

// Create client to manage pod
podClient := objects.New[*corev1.Pod](k8sClient).
	WithNamespace("my-namespace").                                      // Set the namespace of the pod
	WithName("pod-name").                                               // Set the name of the pod
	WithControllerReference(parentObject).                              // Add a controller reference to the pod
	WithUpdateStrategy(objects.HashUpdateStrategy("example.com/hash")). // Use the "Hash" strategy to determine if the pod needs an update
	WithBuilder(func(pod *corev1.Pod, _ objects.Action) error {         // Do custom build operations
		// Add containers to pod
		pod.Spec.Containers = []corev1.Container{
			// ...
		}

		// Return no error
		return nil
	}).
	Finalize().Client()

// Create new pod if non exists
if err := podClient.CreateIfNotExists(ctx); err != nil {
	// Handle error
}

// Since pods are largely immutable, we delete the pod if it is out of date,
// the next reconcile will recreate the pod.
if err := podClient.DeleteIfUpdateRequired(ctx); err != nil {
	// Handle error
}

Example (Create A Set Of Pods)

This example manages a set of 10 pods, the pods are named using an indexed naming strategy, giving the pods a name of my-cache-0 to my-cache-9.

Provided labels are used to discover existing Pods and will be automatically injected into Pods that are created.

// Create client to manage a set of Pods.
podClient := objects.New[*corev1.Pod](k8sClient).
	WithNamespace("my-namespace").                                      // Set the namespace of the pod
	WithControllerReference(parentObject).                              // Add a controller reference to the pod
	WithUpdateStrategy(objects.HashUpdateStrategy("example.com/hash")). // Use the "Hash" strategy to determine if the pod needs an update
	WithBuilder(func(pod *corev1.Pod, _ objects.Action) error {         // Do custom build operations
		// Add containers to pod
		pod.Spec.Containers = []corev1.Container{
			// ...
		}

		// Return no error
		return nil
	}).
	Finalize().SetClient(objects.IndexedSetStrategy("my-cache", 10), map[string]string{"example.com/owner": parentObject.GetName()})

// Create any pods that are missing.
if _, err := podClient.ObjectsToCreate().CreateAll(ctx); err != nil {
	// Handle error
}

// Delete out of date pods, since pods are largely immutable the pods will be re-created next reconcile.
if _, err := podClient.ObjectsToUpdate().DeleteAll(ctx); err != nil {
	// Handle error
}

// Delete un-needed pods (due to scale down).
if _, err := podClient.ObjectsToDelete().DeleteAll(ctx); err != nil {
	// Handle error
}

Example (Using For Each Strategy)

The ForEachStrategy can be used to create an object for each of an input object.

// Create set strategy
strategy, lookup := objects.ForEachSetStrategy("example-prefix", setOfConfigMaps)

// Create set client that creates a Pod for every ConfigMap
podsClient := objects.New[*corev1.Pod](k8sClient).
	WithNamespace(parentObject.GetNamespace()).
	WithName(parentObject.GetName()).
	WithControllerReference(parentObject).
	WithBuilder(func(pod *corev1.Pod, _ objects.Action) error {
		// Get the ConfigMap for this pod
		source, _ := lookup.Source(pod)

		// Add the ConfigMap to the pod spec
		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
			Name: "configmap",
			VolumeSource: corev1.VolumeSource{
				ConfigMap: &corev1.ConfigMapVolumeSource{
					LocalObjectReference: corev1.LocalObjectReference{
						Name: source.Name,
					},
				},
			},
		})
		return nil
	}).
	Finalize().
	SetClient(strategy, map[string]string{"example.com/owner": parentObject.GetName()})

// Ensure pods exists and is up to date
if err := podsClient.Ensure(ctx); err != nil {
	// Handle error
}

Index

Variables

DefaultUpdateStrategy is the default update strategy to use. This defaults to the DeepEqualStrategy.

var DefaultUpdateStrategy = DeepEqualUpdateStrategy(equality.Semantic)

func ForEachSetStrategy

func ForEachSetStrategy[T client.Object](prefix string, objects []T) (SetStrategy, ForEachSetStrategyLookup[T])

ForEachSetStrategy is a convenience method to generate a strategy that produces an object for each input object. It also returns a lookup that can be used to obtain the source object from the name.

type Action

Action is passed to build functions to allow them to change behavior depending on if its a create or update action allowing for behaviors such as not updating immutable fields.

type Action uint8

const (
    // ActionCreate gets called when the object is being mutated prior to being created. At the point this is called,
    // the objects existence may not have been determined yet so this value does not guarantee that the eventual
    // operation will be a create.
    ActionCreate Action = iota + 1
    // ActionUpdate gets called when an object is getting mutated prior to being submitted as an update. When the
    // builder is called with this method the object has just been populated from Kubernetes.
    ActionUpdate
)

type BuildFn

BuildFn is a function used to build a given object. The action is passed in to allow the function to change behavior based on if it is creating a new instance of an object, or updating an existing object.

type BuildFn[T client.Object] func(T, Action) error

type Builder

Builder is used to construct Kubernetes objects using BuildFn functions.

type Builder[T client.Object] struct {
    // Object is an instance of the object, this is only required if the builder is operating on an unstructured object
    // as it is used to get the group/version/kind information required for interacting with Kubernetes.
    //
    // It is possible to use unstructured objects without specifying this field, however the build functions MUST set
    // the group/version/kind information.
    Object T
    // Builders are the build functions that are called to create or update a Kubernetes object.
    Builders []BuildFn[T]
    // UpdateStrategy defines the strategy used to determine if an object requires an update.
    UpdateStrategy UpdateStrategy
    // The Kubernetes client used by any constructed object/set clients.
    Client client.Client
    // Namer is used to name the object.
    Namer Namer[T]
    // contains filtered or unexported fields
}

func New
func New[T client.Object](client client.Client) Builder[T]

New returns a builder for a new object.

func (Builder[T]) Finalize
func (b Builder[T]) Finalize() *FinalizedBuilder[T]

Finalize returns a builder that is ready to be used by the client. The default values are set, and the internal slices are cloned to prevent updates to the builder instance the client is using.

func (Builder[T]) WithAnnotation
func (b Builder[T]) WithAnnotation(key, value string) Builder[T]

WithAnnotation adds the provided annotation to the object.

func (Builder[T]) WithAnnotations
func (b Builder[T]) WithAnnotations(new map[string]string) Builder[T]

WithAnnotations adds the provided annotations to the object.

func (Builder[T]) WithBuilder
func (b Builder[T]) WithBuilder(fn BuildFn[T]) Builder[T]

WithBuilder adds a build function to the builder. The function should not mutate the status of the object as these changes will be ignored.

func (Builder[T]) WithControllerReference
func (b Builder[T]) WithControllerReference(owner client.Object) Builder[T]

WithControllerReference sets controller reference of the object to the provided object. The namespaces of the owner and the object being built must match.

func (Builder[T]) WithFinalizer
func (b Builder[T]) WithFinalizer(finalizer string, fn func(T) (bool, error)) Builder[T]

WithFinalizer sets the finalizer if the provided method returns true, otherwise it removes it.

func (Builder[T]) WithGenerateName
func (b Builder[T]) WithGenerateName(prefix string) Builder[T]

WithGenerateName sets the generated name of the object, if the name is set using this method any changes made by build functions will be reverted. If the prefix provided does not end in a hyphen, then an hyphen is appended.

func (Builder[T]) WithHook
func (b Builder[T]) WithHook(hook Hook) Builder[T]

WithHook adds a hook to the given builder, a hook does nothing to change the builder behavior, but can be used to log/observe the client.

func (Builder[T]) WithLabel
func (b Builder[T]) WithLabel(key, value string) Builder[T]

WithLabel adds the provided label to the object.

func (Builder[T]) WithLabels
func (b Builder[T]) WithLabels(new map[string]string) Builder[T]

WithLabels adds the provided labels to the object.

func (Builder[T]) WithName
func (b Builder[T]) WithName(name string) Builder[T]

WithName sets the name of the object, if the name is set using this method any changes made by build functions will be reverted.

func (Builder[T]) WithNameFn
func (b Builder[T]) WithNameFn(fn func(T) error) Builder[T]

WithNameFn registers a method that will be called to set the name of the object. If the name is set using this method a Builder function cannot change it.

func (Builder[T]) WithNamespace
func (b Builder[T]) WithNamespace(namespace string) Builder[T]

WithNamespace sets the namespace of the object, if the namespace is set using this method any changes made by build functions will be reverted.

func (Builder[T]) WithOwnerReference
func (b Builder[T]) WithOwnerReference(owner client.Object) Builder[T]

WithControllerReference adds an owner reference of the object pointing to the provided object. The namespaces of the owner and the object being built must match.

func (Builder[T]) WithUpdateStrategy
func (b Builder[T]) WithUpdateStrategy(strategy UpdateStrategy) Builder[T]

WithUpdateStrategy sets the UpdateStrategy for the generated clients.

type Client

Client is used to create/update/delete a Kubernetes object. It is created using the a Builder.

type Client[T client.Object] struct {
    // contains filtered or unexported fields
}

func (*Client[T]) Create
func (c *Client[T]) Create(ctx context.Context) error

Create attempts to create the object in Kubernetes. It will return an error if the object already exists.

func (*Client[T]) CreateIfNotExists
func (c *Client[T]) CreateIfNotExists(ctx context.Context) error

CreateIfNotExists creates the object if it does not exist.

func (*Client[T]) CreateOrUpdate
func (c *Client[T]) CreateOrUpdate(ctx context.Context) error

CreateOrUpdate will create the object within Kubernetes if it does not exist and will update the object within Kubernetes if it does. This method can be used to ensure the object is what we expect it to be.

func (*Client[T]) Delete
func (c *Client[T]) Delete(ctx context.Context) error

Delete will delete the object within Kubernetes, It will return an error if the object does not exist.

func (*Client[T]) DeleteIfExists
func (c *Client[T]) DeleteIfExists(ctx context.Context) error

DeleteIfExists deletes the object from Kubernetes if it exists.

func (*Client[T]) DeleteIfUpdateRequired
func (c *Client[T]) DeleteIfUpdateRequired(ctx context.Context) error

func (*Client[T]) Exists
func (c *Client[T]) Exists(ctx context.Context) (bool, error)

Exists returns true if the object exists

func (*Client[T]) Get
func (c *Client[T]) Get(ctx context.Context) (T, error)

Get returns the current object from Kubernetes. This may not do a request if the object has already been retrieved by this client. If the object does not exist in Kubernetes an error is returned.

func (*Client[T]) Update
func (c *Client[T]) Update(ctx context.Context) error

Update will update the object within Kubernetes. It will return an error if the object does not exist.

func (*Client[T]) UpdateIfRequired
func (c *Client[T]) UpdateIfRequired(ctx context.Context) error

UpdateIfRequired will update the object within Kubernetes if the UpdateStrategy has determined an update is required. If the object does not exist an error is returned. On conflict the method will retry the entire process, including: - Obtaining the current object from Kubernetes - Running the build functions - Determining if an update is required using the UpdateStrategy - Possibly performing an update, if the UpdateStrategy determined it is necessary

func (*Client[T]) UpdateRequired
func (c *Client[T]) UpdateRequired(ctx context.Context) (bool, error)

UpdateRequired returns true if the object exists and an update is required

type ExistingObjectsSet

ExistingObjectsSet is the set of existing objects.

type ExistingObjectsSet[T client.Object] interface {
    SetDeleteActions
    // contains filtered or unexported methods
}

type FinalizedBuilder

type FinalizedBuilder[T client.Object] struct {
    // contains filtered or unexported fields
}

func (FinalizedBuilder[T]) Apply
func (b FinalizedBuilder[T]) Apply(obj T, action Action) error

Apply runs the builder against the provided object. The operation is information to the build function, allowing it to change behavior based on if it is creating a new instance of an object, or updating an existing object.

func (*FinalizedBuilder[T]) Client
func (b *FinalizedBuilder[T]) Client() *Client[T]

Client creates a client to manage the object that the builder will create.

func (*FinalizedBuilder[T]) ClientForExistingObject
func (b *FinalizedBuilder[T]) ClientForExistingObject(obj T) *Client[T]

ClientForExistingObject creates a client that will manage an existing object.

func (FinalizedBuilder[T]) Create
func (b FinalizedBuilder[T]) Create() (T, error)

Create returns a new instance of the object and runs all the specified build functions over it with ActionCreate passed in.

func (*FinalizedBuilder[T]) ResourceMetadata
func (b *FinalizedBuilder[T]) ResourceMetadata() (resource.ResourceMetadata, error)

ResourceMetadata returns information on the objects type

func (*FinalizedBuilder[T]) SetClient
func (b *FinalizedBuilder[T]) SetClient(strategy SetStrategy, selector Selector) *SetClient[T]

ObjectClient returns a client the can be used to manage an object described by this builder.

type ForEachSetStrategyLookup

ForEachSetStrategyLookup is a lookup returned by the ForEachSetStrategy function, it allows the source object to easily be looked up.

type ForEachSetStrategyLookup[T client.Object] map[string]T

func (ForEachSetStrategyLookup[T]) Source
func (f ForEachSetStrategyLookup[T]) Source(o client.Object) (T, bool)

Source returns the source object when using the ForEachSetStrategy

type Hook

Hook is an interface that is called by the client when specific actions take place.

type Hook interface {
    PreCreate(ctx context.Context, obj client.Object)
    PostCreate(ctx context.Context, obj client.Object)
    CreateError(ctx context.Context, obj client.Object, err error)
    PreUpdate(ctx context.Context, old, new client.Object)
    PostUpdate(ctx context.Context, old, new client.Object)
    UpdateError(ctx context.Context, old, new client.Object, err error)
    PreDelete(ctx context.Context, obj client.Object)
    PostDelete(ctx context.Context, obj client.Object)
    DeleteError(ctx context.Context, obj client.Object, err error)
}

func EventEmitterHook
func EventEmitterHook(recorder record.EventRecorder, object client.Object) Hook

EventEmitterHook returns a hook that emits an event on the provided object when an object is created, updated and deleted.

type Namer

Namer is used to name objects, it should mutate the `metadata.name` and `metadata.generateName` fields only.

type Namer[T client.Object] interface {
    SetObjectName(T) error
}

type NullHook

NullHook is a hook that performs no action. It can be embedded in other hooks to ensure non implemented methods have a null implementation

type NullHook struct{}

func (NullHook) CreateError
func (NullHook) CreateError(ctx context.Context, obj client.Object, err error)

func (NullHook) DeleteError
func (NullHook) DeleteError(ctx context.Context, obj client.Object, err error)

func (NullHook) PostCreate
func (NullHook) PostCreate(ctx context.Context, obj client.Object)

func (NullHook) PostDelete
func (NullHook) PostDelete(ctx context.Context, obj client.Object)

func (NullHook) PostUpdate
func (NullHook) PostUpdate(ctx context.Context, old, new client.Object)

func (NullHook) PreCreate
func (NullHook) PreCreate(ctx context.Context, obj client.Object)

func (NullHook) PreDelete
func (NullHook) PreDelete(ctx context.Context, obj client.Object)

func (NullHook) PreUpdate
func (NullHook) PreUpdate(ctx context.Context, old, new client.Object)

func (NullHook) UpdateError
func (NullHook) UpdateError(ctx context.Context, old, new client.Object, err error)

type ObjectSliceAccessor

ObjectSliceAccessor is used to access items in a slice of client.Objects.

type ObjectSliceAccessor interface {
    Get(int) client.Object
    Len() int
}

type ObjectsToCreateSet

ObjectsToCreateSet is the set of objects to be created.

type ObjectsToCreateSet[T client.Object] interface {
    SetCreateActions
    // contains filtered or unexported methods
}

type ObjectsToDeleteSet

ObjectsToUpdateSet is the set of objects to be deleted.

type ObjectsToDeleteSet[T client.Object] interface {
    SetDeleteActions
    // contains filtered or unexported methods
}

type ObjectsToUpdateSet

ObjectsToUpdateSet is the set of objects that require an update.

type ObjectsToUpdateSet[T client.Object] interface {
    SetUpdateActions
    SetDeleteActions
    // contains filtered or unexported methods
}

type Selector

Selector is the label selector used to match existing objects

type Selector map[string]string

type SetClient

SetClient is used to manage a "set" of items. It is constructed by the Builder object.

type SetClient[T client.Object] struct {
    // contains filtered or unexported fields
}

func (*SetClient[T]) Ensure
func (c *SetClient[T]) Ensure(ctx context.Context) error

Ensure will perform all create/update/delete operations required to make the objects in Kubernetes match the defined set.

func (*SetClient[T]) Existing
func (c *SetClient[T]) Existing() ExistingObjectsSet[T]

Existing returns a set of objects that reprents all existing objects in Kubernetes, regardless of if they are desired by the set.

func (*SetClient[T]) ObjectsToCreate
func (c *SetClient[T]) ObjectsToCreate() ObjectsToCreateSet[T]

Existing returns a set of objects that reprents all existing objects in Kubernetes, regardless of if they are desired by the set.

func (*SetClient[T]) ObjectsToDelete
func (c *SetClient[T]) ObjectsToDelete() ObjectsToDeleteSet[T]

ObjectsToDelete returns a set of objects that should bd deleted in order to conform the the replica count and naming strategy of the set.

func (*SetClient[T]) ObjectsToUpdate
func (c *SetClient[T]) ObjectsToUpdate() ObjectsToUpdateSet[T]

ObjectsToUpdate returns a set of objects that require an update within Kubernetes. Objects that are due to be deleted are not included in this set.

type SetCommonActions

SetCommonActions contains common methods that exists in all object sets.

type SetCommonActions[T1 client.Object, T2 any] interface {
    // Each runs the provided function for each item within the set. The client
    // passed to the function should not be used outside of the function call.
    // For each invocation the object originally passed to the SetBuilder will
    // be populated with the contents of the object.
    Each(context.Context, func(context.Context, *Client[T1]) error) error
    // Count returns the number of objects within the set.
    Count(context.Context) (int, error)
    // Filter returns a filtered version of the set
    Filter(func(T1) bool) T2
}

type SetCreateActions

SetCreateActions contains methods used to create sets of objects within Kubernetes.

type SetCreateActions interface {
    // CreateOne will create a single object within Kubernetes, if the set is
    // empty no action will be performed and false will be returned.
    CreateOne(context.Context) (bool, error)
    // CreateAll will create all objects in the set within Kubernetes. The
    // number of created objects is returned.
    CreateAll(context.Context) (int, error)
}

type SetDeleteActions

SetDeleteActions contains methods used to delete sets of objects within Kubernetes.

type SetDeleteActions interface {
    // DeleteOne will delete a single object within Kubernetes, if the set is
    // empty no action will be performed and false will be returned.
    DeleteOne(context.Context) (bool, error)
    // DeleteAll will delete all objects in the set within Kubernetes. The
    // number of deleted objects is returned.
    DeleteAll(context.Context) (int, error)
}

type SetStrategy

SetStrategy is used by the set client to name the objects it creates. It is also used to determine which objects should be deleted as they do no conform to the strategy.

type SetStrategy interface {
    // DesiredReplicas is the number of desired replicas.
    DesiredReplicas() int
    // Less is used to sort the objects using the naming strategy. This will be
    // called before ShouldBeDeleted in order to allow the strategy to sort the
    // objects before deciding what should be deleted
    Less(i, j client.Object) bool
    // ShouldBeDeleted is called against each object that exists to determine
    // if the object should be deleted due to the nameing strategy or scale
    // down. The slice passed into this function will have been sorted by the
    // provided Sort method.
    ShouldBeDeleted(objects ObjectSliceAccessor, idx int) bool
    // SetName is used to set the name of the object, the existing objects are
    // also passed in. This function may be called multiple times per object
    // so must be idempotent, random names should be generated using
    // the GenerateName field in the Kubernetes object.
    SetName(objects ObjectSliceAccessor, current client.Object) error
}

func FixedNameSetStrategy
func FixedNameSetStrategy(prefix string, names []string) SetStrategy

FixedNameSetStrategy returns a strategy that defines a fixed number of pre-defined names. If a prefix is specified then the names will also be prefixed by this value, with a hyphen separator. Using this strategy the oldest object is deleted first during scale-down.

func GeneratedNameSetStrategy
func GeneratedNameSetStrategy(prefix string, replicas int) SetStrategy

GeneratedNameSetStrategy uses the `metadata.generateName` field to set the name of an object. This gives each object a unique name in the format "prefix-<hash>". For example, this could generate the names example-d42cf, example-ce42r, etc. Using this strategy the oldest object is deleted first during scale-down.

func IndexedSetStrategy
func IndexedSetStrategy(prefix string, replicas int) SetStrategy

IndexedNamingStrategy names objects in the format "prefix-<index>" where index is a zero indexed, incrementing numeric value. For example, this could generate the names example-0, example-1, etc. Using this strategy the highest index is deleted first during scale-down.

type SetUpdateActions

SetUpdateActions contains methods used to update sets of objects within Kubernetes.

type SetUpdateActions interface {
    // UpdateOne will update a single object within Kubernetes, if the set is
    // empty no action will be performed and false will be returned.
    UpdateOne(context.Context) (bool, error)
    // UpdateAll will update all objects in the set within Kubernetes. The
    // number of updated objects is returned.
    UpdateAll(context.Context) (int, error)
}

type UpdateStrategy

UpdateStrategy is used to decide if an update is required to the Kubernetes object.

type UpdateStrategy interface {
    // PreUpdate gets called before the object is created in Kubernetes. It allows the strategy to mutate the object
    // prior to the update. This is useful for strategies that store information within the Kubernetes object itself.
    PreCreate(create client.Object)
    // RequiresUpdate returns true if the object requires an update. The strategy is passed three versions of the
    // object. The "existing" object is the object as it exists in Kubernetes The "create" object is the object that
    // would be created if it did not exist, this is generated by running the build functions against a zero instance of
    // the object (with the OpCreate operation). The "update" object is the pending update, it is generated by running
    // the build functions gainst the existing object (with the OpUpdate operation). Any changes to the "update" object
    // will be persisted within Kubernetes.
    RequiresUpdate(existing, create, update client.Object) bool
}

func DeepEqualUpdateStrategy
func DeepEqualUpdateStrategy(eq equality.Equalities) UpdateStrategy

DeepEqualUpdateStrategy determines if an object requires an update by comparing the object that would be used in the update against the existing object. Since the build functions run against the existing object, this should handle most cases and is the default strategy.

The equality package is used and changes to the status field are ignored.

func HashUpdateStrategy
func HashUpdateStrategy(key string) UpdateStrategy

HashUpdateStrategy performs a SHA1 hash of the creation object prior to both create and update. This hash is stored as an annotation with the specified key.

An object is only updated if the computed hash does not match the hash on the object. Given it is the creation object that is hashed, this allows fields to be mutated and updated without intervention of the controller. This is useful for complex objects such as Pods that may have extra containers or volumes injected in where handling all the cases in the build functions would be overly complex.

Generated by gomarkdoc

Documentation

Overview

Package objects contains utilities for managing Kubernetes objects, either individually or as a larger set. It is designed for use within Kubebuilder and reuses many of the interfaces from the controller-runtime project.

Builders

Builders and the New method are the main entrypoint of this library, they are used to define and construct Kubernetes objects using utility methods. These methods allow common fields to be set such as the namespace, name and owners references while also allowing for custom build functions to be included in order to define more bespoke object customizations.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithName("my-awesome-application").
	WithControllerReference(parentObject)

Client

An Client is a type of client the builder can create. It manages asingle Kubernetes object and provides utility methods to create, update and delete it. The namespace and name of the object are defined by the builder, the client takes this information and can determine if the object exists, and if it is up to date. Using this information utility methods like CreateIfNotExists and UpdateIfRequired are provided. The CreateOrUpdate method will be the standard method used by most reconcilers as it ensures the object exists and is up to date with a single call, this coupled with a controller reference handles most required functionality.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithName("my-awesome-application").
	WithControllerReference(parentObject).
	Finalize().Client()

SetClient

An SetClient is a type of client the builder can create. It manages "sets" of objects. When constructing a SetClient a desired replica count, SetStrategy and a label selector are provided. This allows the client to discover existing objects as well as determine if objects need to be created, updated or deleted. Using this strategy the name provided by the builder is ignored as the SetStrategy takes over this function.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithControllerReference(parentObject).
	Finalize().
	SetClient(objects.GeneratedNameSetStrategy("my-awesome-application", 5), map[string]string{"owner": parentObject.GetName()})

Update strategies

An update strategy is what is used by the package to determine if an object is out of date and requires and update. See the implementations below for more information about included strategies.

Set strategies

When dealing with sets of objects, objects can name the object using many different strategies. The naming strategy also defines what objects are deleted first during scale-down. See the implementations below for more information about included strategies.

Hooks

Hooks can be provided to the builder to allow functions to be called under certain events. This is designed to add a point that observability can be injected into the clients. For example a hook to emit a Kubernetes event when an object is create/updated/deleted is included in the library.

objects.New[*corev1.ServiceAccount](k8sClient).
	WithNamespace("default").
	WithName("my-awesome-application").
	WithControllerReference(parentObject).
	WithHook(EventEmitterHook(eventRecorder, parentObject))

Usage

Inlining all the builders into a Reconcile method can make it very verbose, so instead it is recommended to break out the construction of the client into a separate method that be invoked by the client

func (r *ExampleReconciler) ServiceAccount(ex *examplev1.Example) *objects.Client[*corev1.ServiceAccount] {
    // ServiceAccounts don't have any fields we want to set, so its just an object
    objects.New[*corev1.ServiceAccount](k8sClient).
        WithNamespace(ex.Namespace).
        WithName(ex.Name).
        WithControllerReference(ex).
        Finalize().Client()
}

func (r *ExampleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    ...
    // The most basic usage is using the CreateOrUpdate method to ensure the object is up to date,
    // deletion is handled by the OwnerReference.
    if err := r.ServiceAccount(&example).CreateOrUpdate(ctx); err != nil {
        return ctrl.Result{}, err
    }
    ...
}
Example (ConditionalUpdates)

In this example we create a `PersistentVolumeClaim`. We also update the `PersistentVolumeClaim` if the parent object is updated in order to expand volume automatically. The only field that will be updated is the requested volume size, and this will only ever get larger.

// Create client to manage persistent volume claims
pvcClient := objects.New[*corev1.PersistentVolumeClaim](k8sClient).
	WithNamespace(parentObject.GetNamespace()). // Set the namespace of the PVC
	WithName(parentObject.GetName()).           // Set the name of the PVC
	WithControllerReference(parentObject).
	WithBuilder(func(pvc *corev1.PersistentVolumeClaim, action objects.Action) error {
		// If this is a create operation, just copy the template
		if action == objects.ActionCreate {
			// Copy the template into the PVC object
			*pvc = volumeClaimTemplate
		}

		// If this is an update we only want to update the requested size, even then we only want to expand the
		// volume. If a user wants more drastic action then they should update the PVC themselves.
		desiredStorage := volumeClaimTemplate.Spec.Resources.Requests.Storage()
		if action == objects.ActionUpdate && pvc.Spec.Resources.Requests.Storage().Cmp(*desiredStorage) == -1 {
			pvc.Spec.Resources.Requests[corev1.ResourceStorage] = *desiredStorage
		}

		// Return no error
		return nil
	}).
	Finalize().Client()

// Ensure pvc exists and is up to date
if err := pvcClient.CreateOrUpdate(ctx); err != nil {
	// Handle error
}
Output:

Example (CreateAPod)

This example shows how a single pod can be managed by the builder, since pods are mostly immutable they cannot use the CreateOrUpdate method, instead out of date pods are deleted.

Pods are determined to be out of date using the "HashUpdateStrategy", a strategy that uses an annotation on the Pod to determine if the Pod needs an update.

// Create client to manage pod
podClient := objects.New[*corev1.Pod](k8sClient).
	WithNamespace("my-namespace").                                      // Set the namespace of the pod
	WithName("pod-name").                                               // Set the name of the pod
	WithControllerReference(parentObject).                              // Add a controller reference to the pod
	WithUpdateStrategy(objects.HashUpdateStrategy("example.com/hash")). // Use the "Hash" strategy to determine if the pod needs an update
	WithBuilder(func(pod *corev1.Pod, _ objects.Action) error {         // Do custom build operations
		// Add containers to pod
		pod.Spec.Containers = []corev1.Container{
			// ...
		}

		// Return no error
		return nil
	}).
	Finalize().Client()

// Create new pod if non exists
if err := podClient.CreateIfNotExists(ctx); err != nil {
	// Handle error
}

// Since pods are largely immutable, we delete the pod if it is out of date,
// the next reconcile will recreate the pod.
if err := podClient.DeleteIfUpdateRequired(ctx); err != nil {
	// Handle error
}
Output:

Example (CreateASetOfPods)

This example manages a set of 10 pods, the pods are named using an indexed naming strategy, giving the pods a name of my-cache-0 to my-cache-9.

Provided labels are used to discover existing Pods and will be automatically injected into Pods that are created.

// Create client to manage a set of Pods.
podClient := objects.New[*corev1.Pod](k8sClient).
	WithNamespace("my-namespace").                                      // Set the namespace of the pod
	WithControllerReference(parentObject).                              // Add a controller reference to the pod
	WithUpdateStrategy(objects.HashUpdateStrategy("example.com/hash")). // Use the "Hash" strategy to determine if the pod needs an update
	WithBuilder(func(pod *corev1.Pod, _ objects.Action) error {         // Do custom build operations
		// Add containers to pod
		pod.Spec.Containers = []corev1.Container{
			// ...
		}

		// Return no error
		return nil
	}).
	Finalize().SetClient(objects.IndexedSetStrategy("my-cache", 10), map[string]string{"example.com/owner": parentObject.GetName()})

// Create any pods that are missing.
if _, err := podClient.ObjectsToCreate().CreateAll(ctx); err != nil {
	// Handle error
}

// Delete out of date pods, since pods are largely immutable the pods will be re-created next reconcile.
if _, err := podClient.ObjectsToUpdate().DeleteAll(ctx); err != nil {
	// Handle error
}

// Delete un-needed pods (due to scale down).
if _, err := podClient.ObjectsToDelete().DeleteAll(ctx); err != nil {
	// Handle error
}
Output:

Example (UsingForEachStrategy)

The ForEachStrategy can be used to create an object for each of an input object.

// Create set strategy
strategy, lookup := objects.ForEachSetStrategy("example-prefix", setOfConfigMaps)

// Create set client that creates a Pod for every ConfigMap
podsClient := objects.New[*corev1.Pod](k8sClient).
	WithNamespace(parentObject.GetNamespace()).
	WithName(parentObject.GetName()).
	WithControllerReference(parentObject).
	WithBuilder(func(pod *corev1.Pod, _ objects.Action) error {
		// Get the ConfigMap for this pod
		source, _ := lookup.Source(pod)

		// Add the ConfigMap to the pod spec
		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
			Name: "configmap",
			VolumeSource: corev1.VolumeSource{
				ConfigMap: &corev1.ConfigMapVolumeSource{
					LocalObjectReference: corev1.LocalObjectReference{
						Name: source.Name,
					},
				},
			},
		})
		return nil
	}).
	Finalize().
	SetClient(strategy, map[string]string{"example.com/owner": parentObject.GetName()})

// Ensure pods exists and is up to date
if err := podsClient.Ensure(ctx); err != nil {
	// Handle error
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultUpdateStrategy = DeepEqualUpdateStrategy(equality.Semantic)

DefaultUpdateStrategy is the default update strategy to use. This defaults to the DeepEqualStrategy.

Functions

func ForEachSetStrategy

func ForEachSetStrategy[T client.Object](prefix string, objects []T) (DeterministicSetStrategy, ForEachSetStrategyLookup[T])

ForEachSetStrategy is a convenience method to generate a strategy that produces an object for each input object. It also returns a lookup that can be used to obtain the source object from the name.

Types

type Action

type Action uint8

Action is passed to build functions to allow them to change behavior depending on if its a create or update action allowing for behaviors such as not updating immutable fields.

const (
	// ActionCreate gets called when the object is being mutated prior to being created. At the point this is called,
	// the objects existence may not have been determined yet so this value does not guarantee that the eventual
	// operation will be a create.
	ActionCreate Action = iota + 1
	// ActionUpdate gets called when an object is getting mutated prior to being submitted as an update. When the
	// builder is called with this method the object has just been populated from Kubernetes.
	ActionUpdate
)

type BuildFn

type BuildFn[T client.Object] func(T, Action) error

BuildFn is a function used to build a given object. The action is passed in to allow the function to change behavior based on if it is creating a new instance of an object, or updating an existing object.

type Builder

type Builder[T client.Object] struct {
	// Object is an instance of the object, this is only required if the builder is operating on an unstructured object
	// as it is used to get the group/version/kind information required for interacting with Kubernetes.
	//
	// It is possible to use unstructured objects without specifying this field, however the build functions MUST set
	// the group/version/kind information.
	Object T
	// Builders are the build functions that are called to create or update a Kubernetes object.
	Builders []BuildFn[T]
	// UpdateStrategy defines the strategy used to determine if an object requires an update.
	UpdateStrategy UpdateStrategy
	// The Kubernetes client used by any constructed object/set clients.
	Client client.Client
	// Namer is used to name the object.
	Namer Namer[T]
	// contains filtered or unexported fields
}

Builder is used to construct Kubernetes objects using BuildFn functions.

func New

func New[T client.Object](client client.Client) Builder[T]

New returns a builder for a new object.

func (Builder[T]) Finalize

func (b Builder[T]) Finalize() *FinalizedBuilder[T]

Finalize returns a builder that is ready to be used by the client. The default values are set, and the internal slices are cloned to prevent updates to the builder instance the client is using.

func (Builder[T]) WithAnnotation

func (b Builder[T]) WithAnnotation(key, value string) Builder[T]

WithAnnotation adds the provided annotation to the object.

func (Builder[T]) WithAnnotations

func (b Builder[T]) WithAnnotations(new map[string]string) Builder[T]

WithAnnotations adds the provided annotations to the object.

func (Builder[T]) WithBuilder

func (b Builder[T]) WithBuilder(fn BuildFn[T]) Builder[T]

WithBuilder adds a build function to the builder. The function should not mutate the status of the object as these changes will be ignored.

func (Builder[T]) WithControllerReference

func (b Builder[T]) WithControllerReference(owner client.Object) Builder[T]

WithControllerReference sets controller reference of the object to the provided object. The namespaces of the owner and the object being built must match.

func (Builder[T]) WithFinalizer

func (b Builder[T]) WithFinalizer(finalizer string, fn func(T) (bool, error)) Builder[T]

WithFinalizer sets the finalizer if the provided method returns true, otherwise it removes it.

func (Builder[T]) WithGenerateName

func (b Builder[T]) WithGenerateName(prefix string) Builder[T]

WithGenerateName sets the generated name of the object, if the name is set using this method any changes made by build functions will be reverted. If the prefix provided does not end in a hyphen, then an hyphen is appended.

func (Builder[T]) WithHook

func (b Builder[T]) WithHook(hook Hook) Builder[T]

WithHook adds a hook to the given builder, a hook does nothing to change the builder behavior, but can be used to log/observe the client.

func (Builder[T]) WithLabel

func (b Builder[T]) WithLabel(key, value string) Builder[T]

WithLabel adds the provided label to the object.

func (Builder[T]) WithLabels

func (b Builder[T]) WithLabels(new map[string]string) Builder[T]

WithLabels adds the provided labels to the object.

func (Builder[T]) WithName

func (b Builder[T]) WithName(name string) Builder[T]

WithName sets the name of the object, if the name is set using this method any changes made by build functions will be reverted.

func (Builder[T]) WithNameFn

func (b Builder[T]) WithNameFn(fn func(T) error) Builder[T]

WithNameFn registers a method that will be called to set the name of the object. If the name is set using this method a Builder function cannot change it.

func (Builder[T]) WithNamespace

func (b Builder[T]) WithNamespace(namespace string) Builder[T]

WithNamespace sets the namespace of the object, if the namespace is set using this method any changes made by build functions will be reverted.

func (Builder[T]) WithOwnerReference

func (b Builder[T]) WithOwnerReference(owner client.Object) Builder[T]

WithControllerReference adds an owner reference of the object pointing to the provided object. The namespaces of the owner and the object being built must match.

func (Builder[T]) WithUpdateStrategy

func (b Builder[T]) WithUpdateStrategy(strategy UpdateStrategy) Builder[T]

WithUpdateStrategy sets the UpdateStrategy for the generated clients.

type Client

type Client[T client.Object] struct {
	// contains filtered or unexported fields
}

Client is used to create/update/delete a Kubernetes object. It is created using the a Builder.

func (*Client[T]) Create

func (c *Client[T]) Create(ctx context.Context) error

Create attempts to create the object in Kubernetes. It will return an error if the object already exists.

func (*Client[T]) CreateIfNotExists

func (c *Client[T]) CreateIfNotExists(ctx context.Context) error

CreateIfNotExists creates the object if it does not exist.

func (*Client[T]) CreateOrUpdate

func (c *Client[T]) CreateOrUpdate(ctx context.Context) error

CreateOrUpdate will create the object within Kubernetes if it does not exist and will update the object within Kubernetes if it does. This method can be used to ensure the object is what we expect it to be.

func (*Client[T]) Delete

func (c *Client[T]) Delete(ctx context.Context) error

Delete will delete the object within Kubernetes, It will return an error if the object does not exist.

func (*Client[T]) DeleteIfExists

func (c *Client[T]) DeleteIfExists(ctx context.Context) error

DeleteIfExists deletes the object from Kubernetes if it exists.

func (*Client[T]) DeleteIfUpdateRequired

func (c *Client[T]) DeleteIfUpdateRequired(ctx context.Context) error

func (*Client[T]) Exists

func (c *Client[T]) Exists(ctx context.Context) (bool, error)

Exists returns true if the object exists

func (*Client[T]) Get

func (c *Client[T]) Get(ctx context.Context) (T, error)

Get returns the current object from Kubernetes. This may not do a request if the object has already been retrieved by this client. If the object does not exist in Kubernetes an error is returned.

func (*Client[T]) Update

func (c *Client[T]) Update(ctx context.Context) error

Update will update the object within Kubernetes. It will return an error if the object does not exist.

func (*Client[T]) UpdateIfRequired

func (c *Client[T]) UpdateIfRequired(ctx context.Context) error

UpdateIfRequired will update the object within Kubernetes if the UpdateStrategy has determined an update is required. If the object does not exist an error is returned. On conflict the method will retry the entire process, including: - Obtaining the current object from Kubernetes - Running the build functions - Determining if an update is required using the UpdateStrategy - Possibly performing an update, if the UpdateStrategy determined it is necessary

func (*Client[T]) UpdateRequired

func (c *Client[T]) UpdateRequired(ctx context.Context) (bool, error)

UpdateRequired returns true if the object exists and an update is required

type DeterministicSetStrategy

type DeterministicSetStrategy interface {
	SetStrategy

	// Names returns the set of desired object names, these are not guaranteed
	// to exist.
	Names() []string
}

DeterministicSetStrategy is a set strategy where the names are completely deterministic and are known ahead of time

func FixedNameSetStrategy

func FixedNameSetStrategy(prefix string, names []string) DeterministicSetStrategy

FixedNameSetStrategy returns a strategy that defines a fixed number of pre-defined names. If a prefix is specified then the names will also be prefixed by this value, with a hyphen separator. Using this strategy the oldest object is deleted first during scale-down.

func IndexedSetStrategy

func IndexedSetStrategy(prefix string, replicas int) DeterministicSetStrategy

IndexedNamingStrategy names objects in the format "prefix-<index>" where index is a zero indexed, incrementing numeric value. For example, this could generate the names example-0, example-1, etc. Using this strategy the highest index is deleted first during scale-down.

type ExistingObjectsSet

type ExistingObjectsSet[T client.Object] interface {
	SetCommonActions[T, ExistingObjectsSet[T]]
	SetDeleteActions
}

ExistingObjectsSet is the set of existing objects.

type FinalizedBuilder

type FinalizedBuilder[T client.Object] struct {
	// contains filtered or unexported fields
}

func (FinalizedBuilder[T]) Apply

func (b FinalizedBuilder[T]) Apply(obj T, action Action) error

Apply runs the builder against the provided object. The operation is information to the build function, allowing it to change behavior based on if it is creating a new instance of an object, or updating an existing object.

func (*FinalizedBuilder[T]) Client

func (b *FinalizedBuilder[T]) Client() *Client[T]

Client creates a client to manage the object that the builder will create.

func (*FinalizedBuilder[T]) ClientForExistingObject

func (b *FinalizedBuilder[T]) ClientForExistingObject(obj T) *Client[T]

ClientForExistingObject creates a client that will manage an existing object.

func (FinalizedBuilder[T]) Create

func (b FinalizedBuilder[T]) Create() (T, error)

Create returns a new instance of the object and runs all the specified build functions over it with ActionCreate passed in.

func (*FinalizedBuilder[T]) ResourceMetadata

func (b *FinalizedBuilder[T]) ResourceMetadata() (resource.ResourceMetadata, error)

ResourceMetadata returns information on the objects type

func (*FinalizedBuilder[T]) SetClient

func (b *FinalizedBuilder[T]) SetClient(strategy SetStrategy, selector Selector) *SetClient[T]

ObjectClient returns a client the can be used to manage an object described by this builder.

type ForEachSetStrategyLookup

type ForEachSetStrategyLookup[T client.Object] map[string]T

ForEachSetStrategyLookup is a lookup returned by the ForEachSetStrategy function, it allows the source object to easily be looked up.

func (ForEachSetStrategyLookup[T]) Source

func (f ForEachSetStrategyLookup[T]) Source(o client.Object) (T, bool)

Source returns the source object when using the ForEachSetStrategy

type Hook

type Hook interface {
	PreCreate(ctx context.Context, obj client.Object)
	PostCreate(ctx context.Context, obj client.Object)
	CreateError(ctx context.Context, obj client.Object, err error)
	PreUpdate(ctx context.Context, old, new client.Object)
	PostUpdate(ctx context.Context, old, new client.Object)
	UpdateError(ctx context.Context, old, new client.Object, err error)
	PreDelete(ctx context.Context, obj client.Object)
	PostDelete(ctx context.Context, obj client.Object)
	DeleteError(ctx context.Context, obj client.Object, err error)
}

Hook is an interface that is called by the client when specific actions take place.

func EventEmitterHook

func EventEmitterHook(recorder record.EventRecorder, object client.Object) Hook

EventEmitterHook returns a hook that emits an event on the provided object when an object is created, updated and deleted.

type Namer

type Namer[T client.Object] interface {
	SetObjectName(T) error
}

Namer is used to name objects, it should mutate the `metadata.name` and `metadata.generateName` fields only.

type NullHook

type NullHook struct{}

NullHook is a hook that performs no action. It can be embedded in other hooks to ensure non implemented methods have a null implementation

func (NullHook) CreateError

func (NullHook) CreateError(ctx context.Context, obj client.Object, err error)

func (NullHook) DeleteError

func (NullHook) DeleteError(ctx context.Context, obj client.Object, err error)

func (NullHook) PostCreate

func (NullHook) PostCreate(ctx context.Context, obj client.Object)

func (NullHook) PostDelete

func (NullHook) PostDelete(ctx context.Context, obj client.Object)

func (NullHook) PostUpdate

func (NullHook) PostUpdate(ctx context.Context, old, new client.Object)

func (NullHook) PreCreate

func (NullHook) PreCreate(ctx context.Context, obj client.Object)

func (NullHook) PreDelete

func (NullHook) PreDelete(ctx context.Context, obj client.Object)

func (NullHook) PreUpdate

func (NullHook) PreUpdate(ctx context.Context, old, new client.Object)

func (NullHook) UpdateError

func (NullHook) UpdateError(ctx context.Context, old, new client.Object, err error)

type ObjectSliceAccessor

type ObjectSliceAccessor interface {
	Get(int) client.Object
	Len() int
}

ObjectSliceAccessor is used to access items in a slice of client.Objects.

type ObjectsToCreateSet

type ObjectsToCreateSet[T client.Object] interface {
	SetCommonActions[T, ObjectsToCreateSet[T]]
	SetCreateActions
}

ObjectsToCreateSet is the set of objects to be created.

type ObjectsToDeleteSet

type ObjectsToDeleteSet[T client.Object] interface {
	SetCommonActions[T, ObjectsToDeleteSet[T]]
	SetDeleteActions
}

ObjectsToUpdateSet is the set of objects to be deleted.

type ObjectsToUpdateSet

type ObjectsToUpdateSet[T client.Object] interface {
	SetCommonActions[T, ObjectsToUpdateSet[T]]
	SetUpdateActions
	SetDeleteActions
}

ObjectsToUpdateSet is the set of objects that require an update.

type Selector

type Selector map[string]string

Selector is the label selector used to match existing objects

type SetClient

type SetClient[T client.Object] struct {
	// contains filtered or unexported fields
}

SetClient is used to manage a "set" of items. It is constructed by the Builder object.

func (*SetClient[T]) Ensure

func (c *SetClient[T]) Ensure(ctx context.Context) error

Ensure will perform all create/update/delete operations required to make the objects in Kubernetes match the defined set.

func (*SetClient[T]) Existing

func (c *SetClient[T]) Existing() ExistingObjectsSet[T]

Existing returns a set of objects that reprents all existing objects in Kubernetes, regardless of if they are desired by the set.

func (*SetClient[T]) ObjectsToCreate

func (c *SetClient[T]) ObjectsToCreate() ObjectsToCreateSet[T]

Existing returns a set of objects that reprents all existing objects in Kubernetes, regardless of if they are desired by the set.

func (*SetClient[T]) ObjectsToDelete

func (c *SetClient[T]) ObjectsToDelete() ObjectsToDeleteSet[T]

ObjectsToDelete returns a set of objects that should bd deleted in order to conform the the replica count and naming strategy of the set.

func (*SetClient[T]) ObjectsToUpdate

func (c *SetClient[T]) ObjectsToUpdate() ObjectsToUpdateSet[T]

ObjectsToUpdate returns a set of objects that require an update within Kubernetes. Objects that are due to be deleted are not included in this set.

type SetCommonActions

type SetCommonActions[T1 client.Object, T2 any] interface {
	// Each runs the provided function for each item within the set. The client
	// passed to the function should not be used outside of the function call.
	// For each invocation the object originally passed to the SetBuilder will
	// be populated with the contents of the object.
	Each(context.Context, func(context.Context, *Client[T1]) error) error
	// Count returns the number of objects within the set.
	Count(context.Context) (int, error)
	// Filter returns a filtered version of the set
	Filter(func(T1) bool) T2
}

SetCommonActions contains common methods that exists in all object sets.

type SetCreateActions

type SetCreateActions interface {
	// CreateOne will create a single object within Kubernetes, if the set is
	// empty no action will be performed and false will be returned.
	CreateOne(context.Context) (bool, error)
	// CreateAll will create all objects in the set within Kubernetes. The
	// number of created objects is returned.
	CreateAll(context.Context) (int, error)
}

SetCreateActions contains methods used to create sets of objects within Kubernetes.

type SetDeleteActions

type SetDeleteActions interface {
	// DeleteOne will delete a single object within Kubernetes, if the set is
	// empty no action will be performed and false will be returned.
	DeleteOne(context.Context) (bool, error)
	// DeleteAll will delete all objects in the set within Kubernetes. The
	// number of deleted objects is returned.
	DeleteAll(context.Context) (int, error)
}

SetDeleteActions contains methods used to delete sets of objects within Kubernetes.

type SetStrategy

type SetStrategy interface {
	// DesiredReplicas is the number of desired replicas.
	DesiredReplicas() int
	// Less is used to sort the objects using the naming strategy. This will be
	// called before ShouldBeDeleted in order to allow the strategy to sort the
	// objects before deciding what should be deleted
	Less(i, j client.Object) bool
	// ShouldBeDeleted is called against each object that exists to determine
	// if the object should be deleted due to the nameing strategy or scale
	// down. The slice passed into this function will have been sorted by the
	// provided Sort method.
	ShouldBeDeleted(objects ObjectSliceAccessor, idx int) bool
	// SetName is used to set the name of the object, the existing objects are
	// also passed in. This function may be called multiple times per object
	// so must be idempotent, random names should be generated using
	// the GenerateName field in the Kubernetes object.
	SetName(objects ObjectSliceAccessor, current client.Object) error
}

SetStrategy is used by the set client to name the objects it creates. It is also used to determine which objects should be deleted as they do no conform to the strategy.

func GeneratedNameSetStrategy

func GeneratedNameSetStrategy(prefix string, replicas int) SetStrategy

GeneratedNameSetStrategy uses the `metadata.generateName` field to set the name of an object. This gives each object a unique name in the format "prefix-<hash>". For example, this could generate the names example-d42cf, example-ce42r, etc. Using this strategy the oldest object is deleted first during scale-down.

type SetUpdateActions

type SetUpdateActions interface {
	// UpdateOne will update a single object within Kubernetes, if the set is
	// empty no action will be performed and false will be returned.
	UpdateOne(context.Context) (bool, error)
	// UpdateAll will update all objects in the set within Kubernetes. The
	// number of updated objects is returned.
	UpdateAll(context.Context) (int, error)
}

SetUpdateActions contains methods used to update sets of objects within Kubernetes.

type UpdateStrategy

type UpdateStrategy interface {
	// PreUpdate gets called before the object is created in Kubernetes. It allows the strategy to mutate the object
	// prior to the update. This is useful for strategies that store information within the Kubernetes object itself.
	PreCreate(create client.Object)
	// RequiresUpdate returns true if the object requires an update. The strategy is passed three versions of the
	// object. The "existing" object is the object as it exists in Kubernetes The "create" object is the object that
	// would be created if it did not exist, this is generated by running the build functions against a zero instance of
	// the object (with the OpCreate operation). The "update" object is the pending update, it is generated by running
	// the build functions gainst the existing object (with the OpUpdate operation). Any changes to the "update" object
	// will be persisted within Kubernetes.
	RequiresUpdate(existing, create, update client.Object) bool
}

UpdateStrategy is used to decide if an update is required to the Kubernetes object.

func DeepEqualUpdateStrategy

func DeepEqualUpdateStrategy(eq equality.Equalities) UpdateStrategy

DeepEqualUpdateStrategy determines if an object requires an update by comparing the object that would be used in the update against the existing object. Since the build functions run against the existing object, this should handle most cases and is the default strategy.

The equality package is used and changes to the status field are ignored.

func HashUpdateStrategy

func HashUpdateStrategy(key string) UpdateStrategy

HashUpdateStrategy performs a SHA1 hash of the creation object prior to both create and update. This hash is stored as an annotation with the specified key.

An object is only updated if the computed hash does not match the hash on the object. Given it is the creation object that is hashed, this allows fields to be mutated and updated without intervention of the controller. This is useful for complex objects such as Pods that may have extra containers or volumes injected in where handling all the cases in the build functions would be overly complex.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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