Kubernetes and Crossplane
The Kubernetes library turns any registered Go API type into a Graphene resource: initialize applies and waits, reconcile heals drift, finalize deletes. Crossplane resources use the same mechanism; the provider and credentials belong to the user's cluster.
Client and types
client := k8slib.NewClientFromSecret(
pipeline.Secret(ctx, "kubeconfig"),
k8slib.WithScheme(providerapis.AddToScheme),
)
The kubeconfig resolves from the secret only inside the run worker. Built-in
Kubernetes types are registered by default; WithScheme adds CRDs from their
native Go packages.
Declare a resource
network := k8slib.Resource(ctx, client, "net", &vpc.Network{
Spec: vpc.NetworkSpec{ForProvider: vpc.NetworkParameters{
FolderID: ¶ms.FolderId,
}},
},
k8slib.WithReady(func(live *vpc.Network) bool {
return live.Status.GetCondition(xpv1.TypeReady).Status == corev1.ConditionTrue
}),
k8slib.WithTimeout[vpc.Network](30*time.Minute),
k8slib.WithResourceOption[vpc.Network](pipeline.WithLabels(labels)),
)
The name argument becomes both record id and metadata.name; a conflicting
name on the object is rejected. Ready returns the live typed object, including
status written by the cluster or provider.
Options
| Option | Default | Contract |
|---|---|---|
WithReady(fn) | kstatus/Ready-condition convention | decide readiness from the live object |
WithDrifted(fn) | only disappearance is drift | decide whether to reapply desired state |
WithValidate(fn) | none | reject desired state before activity execution |
WithReconcileEvery(d) | 30s | drift-check period |
WithPollInterval(d) | 5s | readiness polling period |
WithTimeout(d) | 20m | maximum convergence wait |
WithFailed(fn) | none | a non-nil error ends the declaration at once — broken for good |
WithResourceOption(...) | run ownership | parent, children, labels and flows |
The first declaration of a Go kind fixes its handlers for that worker. Keep the same options for subsequent instances.
Crossplane managed resources often have no conditions immediately after apply;
provide WithReady for the provider's real readiness signal.
What the controller says reaches a person while the object converges: each
change of status.conditions is a conditions event of the record and a log
line — a warning when a condition is False with a reason, for example
Synced=False (ReconcileError: Quota limit compute.ssdDisks.size exceeded).
A declaration that fails — the timeout, or WithFailed returning an error on
an observation — carries the last observed conditions in its error and is not
retried: declaring again would create the object anew and wait out the same
refusal. A failed declaration takes its object out of the cluster before its
record closes, and waits up to ten minutes for it to be gone: a controller left
with the object would finish creating it once the cloud allowed — a network
refused for quota appearing an hour later, with nothing to delete it. An
object that does not go away in time is said on the record: a warning and an
orphaned event. Use WithFailed for refusals that will not change, such as quota or
permission. The apply and observe calls of a converging object give up after
ten failed attempts in a row. Prefer native
reference fields between Kubernetes objects: the cluster resolves them without
moving generated ids through workflow history.
The current project has unit coverage for the library but no shipped live-kind cluster conformance suite. Treat provider behavior as part of your pipeline's tested contract.
Local tests
See Local tests for fixtures, dependency adapters and pipeline assertions.
Installation cluster
k8slib.NewClientInCluster(opts...) explicitly selects the run worker's
projected ServiceAccount credentials. Configure serviceAccountName and RBAC
in the installation's managed.pod_template. Only grant this identity to trusted
pipeline workers. A missing or invalid NewClientFromSecret credential never
falls back to the installation identity. The selected connection is retained for
reconciliation and deletion, including deletion after a failed initialization.
For externally owned configuration with its own explicit lifecycle, an activity
may use client.ObjectClient(ctx, object) to obtain the native Kubernetes
resource interface. This does not create a Graphene entity or automatic cleanup.
Use Resource for run-owned objects. Resolve secrets within the activity and
never include their contents in workflow inputs, activity results or logs.