If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
ducks
is an experimental library for interacting with arbitrary resources in a Kubernetes cluster that share a common shape within the reconciler.io ecosystem. The reonciler.io/runtime
project has strong support for interacting with Duck typed resources as if they were traditional typed resources. The ducks
project extends that support to facilitate discovery of resources compatible with a duck type and dynamically creates informers to track changes to referenced types.
Duck types are defined in ducks
by the DuckType
resource. For example, the Service Binding for Kubernetes project defines a Provisioned Serivce duck type.
apiVersion: duck.reconciler.io/v1
kind: DuckType
metadata:
name: provisionedservices.duck.servicebinding.io
spec:
group: duck.servicebinding.io
plural: provisionedservices
kind: ProvisionedService
Resources implementing the duck type are marked. For example, the ExternalSecret
resource from the External Secrets Operator project implements the provisioned service duck type:
apiVersion: duck.servicebinding.io/v1
kind: ProvisionedService
metadata:
name: externalsecrets.external-secrets.io
spec:
group: external-secrets.io
version: v1beta1
kind: ExternalSecret
The ducks
manager will validate the marked API exists and creates ClusterRole
s for clients to be able to view or edit marked resources.
kubectl get clusterrole --selector ducks.reconciler.io/type=provisionedservices.duck.servicebinding.io
NAME
reconcilerio-ducks-provisionedservices.duck.servicebinding.io-edit
reconcilerio-ducks-provisionedservices.duck.servicebinding.io-view
<snip>
Controllers can use a ClusterRoleBinding
to grant access to all current and future known resources implementing the duck type.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-ducks-provisionedservices-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: reconcilerio-ducks-provisionedservices.duck.servicebinding.io-view
subjects:
- kind: ServiceAccount
name: my-controller-manager
namespace: my-system
Inside the controller manager updates to duck typed resources can be tracked by subscribing to a broker watching all resource for the duck type.
// typically in main.go
provisionedServiceDuckBroker, err := duckclient.NewBroker(mgr, schema.GroupKind{Group: "duck.servicebinding.io/v1", Kind: "ProvisionedService"})
if err != nil {
setupLog.Error(err, "unable to create ProvisionedServiceBroker")
os.Exit(1)
}
Inside a reconciler, the broker can be combined with a tracker to cause the reconciled resource to be reprocessed when a tracked duck is updated.
&reconcilers.SyncReconciler[componentsv1alpha1.GenericComponent]{
Setup: func(ctx context.Context, mgr manager.Manager, bldr *builder.TypedBuilder[reconcile.Request]) error {
// watching all resources backing the ProvisionedService duck type
bldr.WatchesRawSource(source.Channel(provisionedServiceDuckBroker.Subscribe(ctx), reconcilers.EnqueueTracked(ctx)))
return nil
},
Sync: func() error {
provisionedServiceClient := duckclient.New(
"provisionedservices.duck.servicebinding.io",
reconcilers.RetrieveConfigOrDie(ctx),
)
// strongly typed representation of the duck type's shape
provisionedService := &componentsv1alpha1.ProvisionedService{
// type metadata for resource implementing the duck type
TypeMeta: metav1.TypeMeta{
APIVersion: "external-secrets.io/v1beta1",
Kind: "ExternalSecret",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "my-externalservice"
}
}
// get the provisioned service and track it for changes
if err := provisionedServiceClient.TrackAndGet(ctx, client.ObjectKeyFromObject(provisionedService), provisionedService); err != nil {
return err
}
// do something with the provisionedService
return nil
}
},
}
You’ll need a Kubernetes cluster to run against. You can use kind to get a local cluster for testing, or run against a remote cluster.
The easiest way to get started is by deploying the latest release. Alternatively, you can build from source.
-
Define where to publish images:
export KO_DOCKER_REPO=<a-repository-you-can-write-to>
For kind, a registry is not required (or run
make kind-deploy
):export KO_DOCKER_REPO=kind.local
-
Build and deploy the controller to the cluster:
Note: The cluster must have the cert-manager deployed. There is a
make deploy-cert-manager
target to deploy the cert-manager.make deploy
Undeploy the controller to the cluster:
make undeploy
The reconciler.io projects follow the Contributor Covenant Code of Conduct. In short, be kind and treat others with respect.
General discussion and questions about the project can occur either on the Kubernetes Slack #reconcilerio channel, or in the project's GitHub discussions. Use the channel you find most comfortable.
The reconciler.io ducks project team welcomes contributions from the community. A contributor license agreement (CLA) is not required. You own full rights to your contribution and agree to license the work to the community under the Apache License v2.0, via a Developer Certificate of Origin (DCO). For more detailed information, refer to CONTRIBUTING.md.
This project was inspired by experiences working with duck typed resources in the Service Binding for Kubernetes project, and the Knative project.
Apache License v2.0: see LICENSE for details.