simple

package
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2024 License: MIT Imports: 13 Imported by: 0

README

Simple Reconciler Package

The Simple Reconciler package provides an opinionated and streamlined approach to building reconcilers for Kubernetes custom resources. It offers a set of powerful features and utilities to simplify the reconciliation process and enhance the overall development experience.

Features

  • Reconciles a child object for a parent object using a simple and intuitive API
  • Supports generic parent and child objects that implement the client.Object interface
  • Provides a fluent builder pattern for constructing reconcilers with ease
  • Offers customizable predicate functions to control reconciliation behavior
  • Supports optional owner references for child objects
  • Enables dry-run mode for avoiding unnecessary requeues and optimizing performance
  • Allows customization of object comparison options to avoid unnecessary updates
  • Supports deletion of child objects based on custom conditions
  • Integrates with the Conductor package for advanced status condition handling

Usage

To use the Simple Reconciler package, follow these steps:

  1. Define your parent and child objects as Kubernetes custom resources, ensuring they implement the client.Object interface.

  2. Create a reconcile function that accepts a parent object and returns the desired state of the child object. The function should have the following signature:

    func(ctx context.Context, parent Parent) (Child, error)
    
  3. Use the FromReconcileFunc function to create a new reconciler builder, passing in your reconcile function:

    builder := simple.FromReconcileFunc(func(ctx context.Context, parent *myapi.MyParent) (*myapi.MyChild, error) {
        // Reconcile logic goes here
    })
    
  4. Customize the reconciler behavior using the available builder methods:

    • WithPredicateFn: Set a predicate function to control when the reconcile function should be called.
    • WithNoReference: Disable setting the owner reference on the child object.
    • WithDryRunType: Configure the dry-run behavior of the reconciler for avoiding unnecessary requeues and optimizing performance.
    • AddCompareOpt: Add custom comparison options to avoid unnecessary updates.
    • WithDetails: Set the reconciler details, including name and description, for documentation and debugging purposes.
    • WithShouldDeleteFn: Specify a function to determine when the child object should be deleted.
    • WithChildKeyFn: Set a function to return the child object with only a key (name and namespace) set.
  5. Build the reconciler by calling the Build method on the builder:

    reconciler := builder.Build()
    
  6. Use the built reconciler in your controller or conductor to reconcile the child object for the parent object.

Integration with Conductor Package

The Simple Reconciler package seamlessly integrates with the Conductor package to provide advanced status condition handling. When used within a conductor, the reconciler automatically updates the status conditions of the parent object based on the reconciliation result.

To leverage the status condition handling capabilities, follow these steps:

  1. Ensure that your parent object has a Status field that includes a Conditions field of type []metav1.Condition.

  2. Register the simple reconciler with the conductor using the Register method:

    conductor := conductor.ForParent(parent).
        WithClient(client).
        WithContext(ctx).
        WithLogger(logger).
        Build()
    
    conductor.Register(reconciler)
    
  3. The simple reconciler will automatically update the status conditions of the parent object based on the reconciliation result. It adds the following conditions:

    • <ReconcilerName>Reconciled: Indicates whether the reconciliation was successful or not.
    • <ReconcilerName>Error: Indicates if an error occurred during reconciliation, along with the error message.
  4. You can access the updated status conditions of the parent object in your controller or other reconcilers to make decisions based on the reconciliation status.

Example

Here's a minimal example of how to use the Simple Reconciler package to reconcile a child object for a parent object:

package main

import (
	"context"
	"github.com/ethan-gallant/maestro/pkg/reconciler/simple"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func podReconcileFunc(ctx context.Context, deployment *appsv1.Deployment) (*corev1.Pod, error) {
	return &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      deployment.Name + "-pod",
			Namespace: deployment.Namespace,
			Labels:    deployment.Spec.Template.Labels,
		},
		Spec: deployment.Spec.Template.Spec,
	}, nil
}

func newPodReconciler() *simple.Reconciler[*appsv1.Deployment, *corev1.Pod] {
	return simple.FromReconcileFunc(podReconcileFunc).
		WithDetails(api.Descriptor{
			Name:        "PodReconciler",
			Description: "Reconciles Pod for Deployment",
		}).
		Build()
}

Here's a more detailed example, including custom predicate, deletion, and child key functions:

package main

import (
	"context"
	"github.com/ethan-gallant/maestro/pkg/reconciler/simple"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func podReconcileFunc(ctx context.Context, deployment *appsv1.Deployment) (*corev1.Pod, error) {
	return &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      deployment.Name + "-pod",
			Namespace: deployment.Namespace,
			Labels:    deployment.Spec.Template.Labels,
		},
		Spec: deployment.Spec.Template.Spec,
	}, nil
}

func podPredicateFunc(deployment *appsv1.Deployment) bool {
	// Only reconcile if the deployment has a specific label
	return deployment.Labels["app"] == "my-app"
}

func podShouldDeleteFunc(deployment *appsv1.Deployment) bool {
	// Delete the pod if the deployment is being deleted
	return !deployment.DeletionTimestamp.IsZero()
}

func podChildKeyFunc(deployment *appsv1.Deployment) *corev1.Pod {
	return &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      deployment.Name + "-pod",
			Namespace: deployment.Namespace,
		},
	}
}

func newPodReconciler() *simple.Reconciler[*appsv1.Deployment, *corev1.Pod] {
	return simple.FromReconcileFunc(podReconcileFunc).
		WithDetails(api.Descriptor{
			Name:        "PodReconciler",
			Description: "Reconciles Pod for Deployment",
		}).
		WithPredicateFn(podPredicateFunc).
		WithShouldDeleteFn(podShouldDeleteFunc).
		WithChildKeyFn(podChildKeyFunc).
		Build()
}

func main() {
	reconciler := newPodReconciler()

	// Use the reconciler in your controller or conductor
	// ...
}

💡 Tip: When your predicate and deletion functions are opposites, you can use the simple.InversePredicate function to create the deletion function from the predicate function. This is useful when you have an Enabled field in the parent object and want to reconcile only when it's true and delete the child object when it's false.

In this example, the reconciler is created using the FromReconcileFunc function, specifying the desired state of the child object based on the parent object. The reconciler is customized with additional options, such as a predicate function to control reconciliation, a deletion function to handle child object deletion, and a child key function to specify the key of the child object. The newMyReconciler function encapsulates the builder configuration and returns the constructed reconciler.

Best Practices

When using the Simple Reconciler package, consider the following best practices:

  1. Keep your reconcile functions focused and single-purpose. Each reconciler should be responsible for reconciling a specific child object.

  2. Use the predicate function to optimize reconciliation performance by avoiding unnecessary reconciliation cycles.

  3. Leverage the dry-run mode for avoiding unnecessary requeues and optimizing performance. Use the WithDryRunType method with the DryRunWarn option to log the differences and set the necessary defaults.

  4. Ensure that you set the desired defaults on the objects returned by the reconcile function. If you encounter noisy objects that trigger unnecessary updates, consider using the WithDryRunType method with the DryRunWarn option to log the differences and set the necessary defaults.

  5. Customize the object comparison options using the AddCompareOpt method to avoid unnecessary updates when the child object is already in the desired state.

  6. Provide meaningful and descriptive names and descriptions for your reconcilers using the WithDetails method to enhance documentation and debugging.

  7. Handle errors gracefully and use the status condition handling capabilities of the Conductor package to propagate reconciliation status to the parent object.

  8. Consider using the WithShouldDeleteFn and WithChildKeyFn methods to handle the deletion of child objects based on custom conditions and to specify the key of the child object.

  9. Create separate functions for each builder configuration option and reference them when constructing the reconciler. This promotes code reusability and improves readability.

  10. Define a function that encapsulates the builder configuration and returns the constructed reconciler. This function should be used to create new instances of the reconciler, making it easier to manage and maintain the reconciler configuration.

Additional Resources

For more information on the Kubernetes concepts and resources mentioned in this README, refer to the official Kubernetes documentation:

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Builder

type Builder[Parent client.Object, Child client.Object] struct {
	// contains filtered or unexported fields
}

Builder is a builder for the Reconciler.

func FromReconcileFunc

func FromReconcileFunc[Parent client.Object, Child client.Object](fn ReconcileFn[Parent, Child]) *Builder[Parent, Child]

FromReconcileFunc returns a new instance of Builder for the ReconcileFn

func (*Builder[Parent, Child]) AddCompareOpt

func (b *Builder[Parent, Child]) AddCompareOpt(compareOpts []cmp.Option) *Builder[Parent, Child]

AddCompareOpt adds a comparator option to the reconciler

func (*Builder[Parent, Child]) Build

func (b *Builder[Parent, Child]) Build() *Reconciler[Parent, Child]

Build returns the constructed Reconciler.

func (*Builder[Parent, Child]) WithChildKeyFn

func (b *Builder[Parent, Child]) WithChildKeyFn(childKeyFn func(Parent) Child) *Builder[Parent, Child]

func (*Builder[Parent, Child]) WithDetails

func (b *Builder[Parent, Child]) WithDetails(details api.Descriptor) *Builder[Parent, Child]

WithDetails sets the Details field.

func (*Builder[Parent, Child]) WithDryRunType

func (b *Builder[Parent, Child]) WithDryRunType(dryRunType reconciler.DryRunType) *Builder[Parent, Child]

WithDryRunType configures the dry-run behavior of the reconciler.

func (*Builder[Parent, Child]) WithNoReference

func (b *Builder[Parent, Child]) WithNoReference(noReference bool) *Builder[Parent, Child]

WithNoReference sets the NoReference field.

func (*Builder[Parent, Child]) WithPreUpdateFn added in v0.0.5

func (b *Builder[Parent, Child]) WithPreUpdateFn(preUpdateFn func(ctx context.Context, parent Parent, previous, child Child) error) *Builder[Parent, Child]

func (*Builder[Parent, Child]) WithPredicateFn

func (b *Builder[Parent, Child]) WithPredicateFn(predicate func(parent Parent) bool) *Builder[Parent, Child]

WithPredicateFn sets the PredicateFn field.

func (*Builder[Parent, Child]) WithShouldDeleteFn

func (b *Builder[Parent, Child]) WithShouldDeleteFn(shouldDeleteFn func(Parent) bool) *Builder[Parent, Child]

type ReconcileFn

type ReconcileFn[Parent client.Object, Child client.Object] func(ctx context.Context, parent Parent) (Child, error)

type Reconciler

type Reconciler[Parent client.Object, Child client.Object] struct {
	// Details is the descriptor for the reconciler.
	// It should contain the name and description of the reconciler for documentation and debugging purposes.
	Details api.Descriptor // required
	// ReconcileFn is the function that reconciles the Child object.
	// The ReconcileFn accepts a Parent object, and returns the desired state of the Child object, or an error.
	ReconcileFn func(ctx context.Context, parent Parent) (Child, error) // required
	// PredicateFn is a function that returns true if the ReconcileFn should be called.
	// If nil, the ReconcileFn will always be called.
	PredicateFn func(parent Parent) bool // optional
	// NoReference optionally disables setting the owner reference on the child object.
	NoReference bool // optional
	// DryRunType configures the dry-run behavior of the reconciler.
	DryRunType reconciler.DryRunType // optional
	// CompareOpts are the options to use when comparing the child object to the desired state.
	// This helps avoid unnecessary updates when the child object is already in the desired state.
	CompareOpts []cmp.Option // optional
	// ShouldDeleteFn is a function that if returns true, the child object will be deleted.
	// It is called regardless of the PredicateFn function. If no function is provided, the child object will never be deleted.
	ShouldDeleteFn func(Parent) bool // optional
	// ChildKeyFn returns the child object with only a key (name and namespace) set.
	// It must always match the key the ReconcileFn returns. Otherwise, Reconcile calls will fail.
	// All other fields should be empty and will be ignored.
	ChildKeyFn func(Parent) Child // required if ShouldDeleteFn is set
	// PreUpdateFn is a function that is called before the child object is applied.
	// This function is not called for the first creation of the child object.
	PreUpdateFn func(ctx context.Context, parent Parent, previous, child Child) error // optional
}

Reconciler (SimpleReconciler) is a simple reconciler that reconciles a child object for a parent object.

func (*Reconciler[Parent, Child]) Describe

func (r *Reconciler[Parent, Child]) Describe() api.Descriptor

Describe returns the descriptor for the reconciler.

func (*Reconciler[Parent, Child]) Reconcile

func (r *Reconciler[Parent, Child]) Reconcile(ctx context.Context, k8sCli client.Client, parent Parent) (reconcile.Result, error)

Reconcile method for SimpleReconciler calls the embedded ChildReconciler's Reconcile method and handles the child object.

Jump to

Keyboard shortcuts

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