runtime

所属分类:云原生工具
开发工具:GO
文件大小:0KB
下载次数:0
上传日期:2024-03-13 20:03:02
上 传 者sh-1993
说明:  基于控制器运行时的Kubernetes协调器框架
(Kubernetes reconciler framework building on controller-runtime)

文件列表:
apis/
docs/
duck/
hack/
internal/
reconcilers/
testing/
time/
tracker/
CODE_OF_CONDUCT.md
CONTRIBUTING.md
LICENSE
MAINTAINERS.md
NOTICE
codecov.yml
go.mod
go.sum

# Reconciler.io runtime ![CI](https://github.com/reconcilerio/runtime/workflows/CI/badge.svg?branch=main) [![GoDoc](https://godoc.org/reconciler.io/runtime?status.svg)](https://godoc.org/reconciler.io/runtime) [![Go Report Card](https://goreportcard.com/badge/reconciler.io/runtime)](https://goreportcard.com/report/reconciler.io/runtime) [![codecov](https://codecov.io/gh/reconcilerio/runtime/main/graph/badge.svg)](https://codecov.io/gh/reconcilerio/runtime) `reconciler.io` is an opinionated framework for authoring and testing Kubernetes reconcilers using [`controller-runtime`](https://github.com/kubernetes-sigs/controller-runtime) project. `controller-runtime` provides infrastructure for creating and operating controllers, but provides little support for the business logic of implementing a reconciler within the controller. The [`Reconciler` interface](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler) provided by `controller-runtime` is the primary hand-off point with `reconciler.io`. Within an existing Kubebuilder or controller-runtime project, reconcilers.io may be adopted incrementally without disrupting existing controllers. A common approach for adopting reconciler.io runtime to use the [testing](#testing) support to test existing reconcilers in a project. If there is a use case runtime does not handle well, you may drop down to the controller-runtime APIs directly, or use any other library that is compatible with controller-runtime. - [Reconcilers](#reconcilers) - [ResourceReconciler](#resourcereconciler) - [AggregateReconciler](#aggregatereconciler) - [SubReconciler](#subreconciler) - [SyncReconciler](#syncreconciler) - [ChildReconciler](#childreconciler) - [ChildSetReconciler](#childsetreconciler) - [Higher-order Reconcilers](#higher-order-reconcilers) - [CastResource](#castresource) - [Sequence](#sequence) - [IfThen](#ifthen) - [While](#while) - [TryCatch](#trycatch) - [OverrideSetup](#overridesetup) - [WithConfig](#withconfig) - [WithFinalizer](#withfinalizer) - [AdmissionWebhookAdapter](#admissionwebhookadapter) - [Testing](#testing) - [ReconcilerTests](#reconcilertests) - [SubReconcilerTests](#subreconcilertests) - [AdmissionWebhookTests](#admissionwebhooktests) - [ExpectConfig](#expectconfig) - [Utilities](#utilities) - [Config](#config) - [Stash](#stash) - [Tracker](#tracker) - [Status](#status) - [Finalizers](#finalizers) - [ResourceManager](#resourcemanager) - [Time](#time) - [Breaking Changes](#breaking-changes) - [Current Deprecations](#current-deprecations) - [Contributing](#contributing) - [Acknowledgements](#acknowledgements) - [License](#license) ## Reconcilers Reconcilers can operate on three different types of objects: - structured types (e.g. [`corev1.Pod`](https://pkg.go.dev/k8s.io/api/core/v1#Pod)) - unstructured types (e.g. [`unstructured.Unstructured`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured#Unstructured)) - semi-structured duck types (e.g. [`PodSpecable`](https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#PodSpecable), [`ProvisionedService`](https://servicebinding.io/spec/core/1.0.0/#provisioned-service)) Structured types are often the best choice as they allow easy interaction with the full object and have full client support. The type must be registered with the [`Scheme`](https://pkg.go.dev/k8s.io/apimachinery/pkg/runtime#Scheme). The type must be pre-defined and compiled into the controller. Unstructured types are useful when the resources are not known at compile time and full access to the resource and client methods is desired. Since the type is not known in advance, it cannot be registered with the scheme. Interacting with the object is difficult as traversing the object requires lots of casts or reflection. The [`TypeMeta`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#TypeMeta) `APIVersion` and `Kind` fields must be defined for the client to operate on the object. Semi-structured duck types offer a middle ground. They are strongly typed, but only cover a subset of the full object. They are intended to facilitate normalized operations across a number of concrete types that share a common subset of their own schema. The concrete objects compatible with this type are not required to be known at compile time. Because duck types are not full objects, client operations for `Create` and `Update` are disallowed (`Patch` is available). Like unstructured objects, the duck type should not be registered in the scheme, and the [`TypeMeta`](https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#TypeMeta) `APIVersion` and `Kind` fields must be defined for the client to operate on the object. The controller-runtime client is able to work with structured and unstructured objects natively, reconciler.io runtime adds support for duck typed objects via the [`duck.NewDuckAwareClientWrapper`](https://pkg.go.dev/reconciler.io/runtime/duck#NewDuckAwareClientWrapper). ### ResourceReconciler A [`ResourceReconciler`](https://pkg.go.dev/reconciler.io/runtime/reconcilers#ResourceReconciler) (formerly ParentReconciler) is responsible for orchestrating the reconciliation of a single resource. The reconciler delegates the manipulation of other resources to SubReconcilers. The resource reconciler is responsible for: - fetching the resource being reconciled - creating a stash to pass state between sub reconcilers - passing the resource to each sub reconciler in turn - initialize conditions on the status by calling status.InitializeConditions() if defined (not available for Unstructured resources) - normalizing the .status.conditions[].lastTransitionTime for status conditions that are metav1.Condition (the previous timestamp is preserved if the condition is otherwise unchanged) (not available for Unstructured resources) - reflects the observed generation on the status (not available for Unstructured resources) - updates the resource status if it was modified - logging the reconcilers activities - records events for mutations and errors The implementor is responsible for: - defining the set of sub reconcilers **Example:** Resource reconcilers tend to be quite simple, as they delegate their work to sub reconcilers. We'll use an example from projectriff of the Function resource, which uses Kpack to build images from a git repo. In this case the FunctionTargetImageReconciler resolves the target image for the function, and FunctionChildImageReconciler creates a child Kpack Image resource based on the resolve value. ```go func FunctionReconciler(c reconcilers.Config) *reconcilers.ResourceReconciler[*buildv1alpha1.Function] { return &reconcilers.ResourceReconciler[*buildv1alpha1.Function]{ Name: "Function", Reconciler: reconcilers.Sequence[*buildv1alpha1.Function]{ FunctionTargetImageReconciler(c), FunctionChildImageReconciler(c), }, Config: c, } } ``` [full source](https://github.com/projectriff/system/blob/4c3b75327bf99cc37b57ba14df4c65d21dc79d28/pkg/controllers/build/function_reconciler.go#L39-L51) **Recommended RBAC:** Replace `` and `` with values for the reconciled resource type. ```go // +kubebuilder:rbac:groups=,resources=,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=,resources=/status,verbs=get;update;patch // +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete ``` or ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: # any name that is bound to the ServiceAccount used by the client rules: - apiGroups: [""] resources: [""] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: [""] resources: ["/status"] verbs: ["get", "update", "patch"] - apiGroups: ["core"] resources: ["events"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] ``` ### AggregateReconciler An [`AggregateReconciler`](https://pkg.go.dev/reconciler.io/runtime/reconcilers#AggregateReconciler) is responsible for synthesizing a single resource, aggregated from other state. The AggregateReconciler is a fusion of the [ResourceReconciler](#resourcereconciler) and [ChildReconciler](#childreconciler). Instead of operating on all resources of a type, it will only operate on a specific resource identified by the type and request (namespace and name). Unlike the child reconciler, the "parent" and "child" resources are the same. The aggregate reconciler is responsible for: - fetching the resource being reconciled - creating a stash to pass state between sub reconcilers - passing the resource to each sub reconciler in turn - creates the resource if it does not exist - updates the resource if it drifts from the desired state - deletes the resource if no longer desired - logging the reconcilers activities - records events for mutations and errors The implementor is responsible for: - specifying the type, namespace and name of the aggregate resource - defining the desired state - merging the actual resource with the desired state (often as simple as copying the spec and labels) **Example:** Aggregate reconcilers resemble a simplified child reconciler with many of the same methods combined directly into a parent reconciler. The `Reconcile` method is used to collect reference data and the `DesiredResource` method defines the desired state. Unlike with a child reconciler, the desired resource may be a direct mutation of the argument. In the example, we are controlling and existing `ValidatingWebhookConfiguration` named `my-trigger` (defined by `Request`). Based on other state in the cluster, the Reconcile method delegates to `DeriveWebhookRules()` to stash the rules for the webhook. Those rules are retrieved in the `DesiredResource` method, augmenting the `ValidatingWebhookConfiguration`. The `MergeBeforeUpdate` function is responsible for merging the desired state into the actual resource, when there is a significant change, the resource is updated on the api server. The resulting `ValidatingWebhookConfiguration` will have the current desired rules defined by this reconciler, combined with existing state like the location of the webhook server, and other policies. ```go // AdmissionTriggerReconciler reconciles a ValidatingWebhookConfiguration object to // dynamically be notified of resource mutations. A less reliable, but potentially more // efficient than an informer watching each tracked resource. func AdmissionTriggerReconciler(c reconcilers.Config) *reconcilers.AggregateReconciler[*admissionregistrationv1.ValidatingWebhookConfiguration] { return &reconcilers.AggregateReconciler[*admissionregistrationv1.ValidatingWebhookConfiguration]{ Name: "AdmissionTrigger", Request: reconcilers.Request{ NamesspacedName: types.NamesspacedName{ // no namespace since ValidatingWebhookConfiguration is cluster scoped Name: "my-trigger", }, }, Reconciler: reconcilers.Sequence[*admissionregistrationv1.ValidatingWebhookConfiguration]{ DeriveWebhookRules(), }, DesiredResource: func(ctx context.Context, resource *admissionregistrationv1.ValidatingWebhookConfiguration) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) { // assumes other aspects of the webhook config are part of a preexisting // install, and that there is a server ready to receive the requests. rules := RetrieveWebhookRules(ctx) resource.Webhooks[0].Rules = rules return resource, nil }, MergeBeforeUpdate: func(current, desired *admissionregistrationv1.ValidatingWebhookConfiguration) { current.Webhooks[0].Rules = desired.Webhooks[0].Rules }, Sanitize: func(resource *admissionregistrationv1.ValidatingWebhookConfiguration) interface{} { return resource.Webhooks[0].Rules }, Config: c, } } ``` [full source](https://github.com/scothis/servicebinding-runtime/blob/8ae0b1fb8b7a37856fa18171bc34e3462c35348b/controllers/webhook_controller.go#L171-L221) **Recommended RBAC:** Replace `` and `` with values for the reconciled resource type. ```go // +kubebuilder:rbac:groups=,resources=,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete ``` or ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: # any name that is bound to the ServiceAccount used by the client rules: - apiGroups: [""] resources: [""] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["core"] resources: ["events"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] ``` ### SubReconciler The [`SubReconciler`](https://pkg.go.dev/reconciler.io/runtime/reconcilers#SubReconciler) interface defines the contract between the host and sub reconcilers. #### SyncReconciler The [`SyncReconciler`](https://pkg.go.dev/reconciler.io/runtime/reconcilers#SyncReconciler) is the minimal type-aware sub reconciler. It is used to manage a portion of the resource reconciliation that is custom, or whose behavior is not covered by another sub reconciler type. Common uses include looking up reference data for the reconciliation, or controlling APIs that are not Kubernetes resources. When a resource is deleted that has pending finalizers, the Finalize method is called instead of the Sync method. If the SyncDuringFinalization field is true, the Sync method will also by called. If creating state that must be manually cleaned up, it is the users responsibility to define and clear finalizers. Using the [finalizer helper methods](#finalizers) is strongly encouraged with working under a [ResourceReconciler](#resourcereconciler). **Example:** While sync reconcilers have the ability to do anything a reconciler can do, it's best to keep them focused on a single goal, letting the resource reconciler structure multiple sub reconcilers together. In this case, we use the reconciled resource and the client to resolve the target image and stash the value on the resource's status. The status is a good place to stash simple values that can be made public. More [advanced forms of stashing](#stash) are also available. Learn more about [status and its contract](#status). ```go func FunctionTargetImageReconciler(c reconcilers.Config) reconcilers.SubReconciler[*buildv1alpha1.Function] { return &reconcilers.SyncReconciler[*buildv1alpha1.Function]{ Name: "TargetImage", Sync: func(ctx context.Context, resource *buildv1alpha1.Function) error { log := logr.FromContextOrDiscard(ctx) targetImage, err := resolveTargetImage(ctx, c.Client, resource) if err != nil { return err } resource.Status.MarkImageResolved() resource.Status.TargetImage = targetImage return nil }, } } ``` [full source](https://github.com/projectriff/system/blob/4c3b75327bf99cc37b57ba14df4c65d21dc79d28/pkg/controllers/build/function_reconciler.go#L53-L74) #### ChildReconciler The [`ChildReconciler`](https://pkg.go.dev/reconciler.io/runtime/reconcilers#ChildReconciler) is a sub reconciler that is responsible for managing a single controlled resource. Within a child reconciler, the reconciled resource is referred to as the parent resource to avoid ambiguity with the child resource. A developer defines their desired state for the child resource (if any), and the reconciler creates/updates/deletes the resource to match the desired state. The child resource is also used to update the parent's status. Mutations and errors are recorded for the parent. The `ChildReconciler` is responsible for: - looking up an existing child - creating/updating/deleting the child resource based on the desired state - setting the owner reference on the child resource (when not using a finalizer) - logging the reconcilers activities - enqueuing the parent resource for reconciliation when the child is mutated - recording child mutations and errors for the parent resource - adapting to child resource changes applied by mutating webhooks - adding and clearing of a finalizer, if specified The implementor is responsible for: - defining the desired resource - merging the actual resource with the desired state (often as simple as copying the spec and labels) - updating the parent's status from the child - defining the status subresource [according to the contract](#status) When a finalizer is defined, the parent resource is patched to add the finalizer before creating the child; it is removed after the child is deleted. If the parent resource is pending deletion, the desired child method is not called, and existing children are deleted. Using a finalizer means that the child resource will not use an owner reference. The `OurChild` method must be implemented in a way that can uniquely and unambiguously identify the child that this parent resource is responsible for from any other resources of the same kind. The child resource is tracked explicitly to watch for mutations triggering the parent resource to be reconciled. > Warning: It is crucial that each `ChildReconciler` using a finalizer have a unique and stable finalizer name. Two reconcilers that use the same finalizer, or a reconciler that changed the name of its finalizer, may leak the child resource when the parent is deleted, or the parent resource may never terminate. **Example:** Now it's time to create the child Image resource that will do the work of building our Function. This reconciler looks more more complex than what we have seen so far, each function on the reconciler provides a focused hook into the lifecycle being orchestrated by the ChildReconciler. ```go func FunctionChildImageReconciler(c reconcilers.Config) reconcilers.SubReconciler[*buildv1alpha1.Function] { return &reconcilers.ChildReconciler[*buildv1alpha1.Function, *kpackbuildv1alpha1.Image, *kpackbuildv1alpha1.ImageList]{ Name: "ChildImage", DesiredChild: func(ctx context.Context, parent *buildv1alpha1.Function) (*kpackbuildv1alpha1.Image, error) { if parent.Spec.Source == nil { // don't create an Image, and delete any existing Image return nil, nil } child := &kpackbuildv1alpha1.Image{ ObjectMeta: metav1.ObjectMeta{ Labels: reconcilers.MergeMaps(parent.Labels, map[string]string{ buildv1alpha1.FunctionLabelKey: parent.Name, }), Annotations: make(map[string]string), // Name or GenerateName are supported GenerateName: fmt.Sprintf("%s-function-", parent.Name), Namespace: parent.Namespace, }, Spec: kpackbuildv1alpha1.ImageSpec{ Tag: parent.Status.TargetImage, // value set by sync reconciler // ... abbreviated }, } return child, nil }, MergeBeforeUpdate: func(actual, desired *kpackbuildv1alpha1.Image) { // mutate actual resource with desired state actual.Labels = desired.Labels actual.Spec = desired.Spec }, ReflectChildStatusOnParent: func(ctx context.Context, parent *buildv1alpha1.Function, child *kpackbuildv1alpha1.Image, err error) { // child is the value of the freshly created/updated/deleted child // resource as returned from the api server // If a fixed desired resource name is used instead of a generated // name, check if the err is because the resource already exists. // The ChildReconciler will not claim ownership of another resource. // // See https://github.com/projectriff/syste ... ...

近期下载者

相关文件


收藏者