Skip to main content

Crossplane Provider

Obmondo-authored chart (the only vendored upstream chart is the optional Crossview UI) that installs every Crossplane provider KubeAid supports, together with the provider configuration and the managed resources KubeAid models for it. Each provider is a sub-chart switched on from values, the way capi-cluster enables its infrastructure providers:

azure:
enabled: true
scaleway:
enabled: true
buckets:
- name: backups
versioning: true
keycloak:
enabled: true

It replaces the crossplane-providers-and-functions and crossplane-compositions charts, which have been removed from KubeAid. See Migration.

Why it's in KubeAid​

crossplane only installs the Crossplane core. Everything cloud-specific used to be split over two more charts and three ArgoCD Applications, because the CRDs a ProviderConfig or a Bucket need do not exist until the matching Provider package is installed. That made adding a provider, or a single bucket, a change across several charts and apps. This chart keeps one Application and expresses the ordering with ArgoCD sync waves instead (see How ordering works), so for a cluster operator a new Scaleway bucket is one entry in one values file.

Prerequisites​

  • crossplane core installed in the cluster (same namespace, by default crossplane).
  • A credentials Secret per enabled provider in the release namespace, created out of band (typically by the cluster's sealed-secrets app): azure-credentials, scaleway-credentials. The Keycloak credentials Secret belongs to the keycloakx chart, see below.
  • The ArgoCD Application must track resources by annotation (configs.cm.application.resourceTrackingMethod: annotation in argo-cd, KubeAid's default) and should carry a sync retry block, see How ordering works.

Key values / KubeAid-specific configuration​

Common​

ValueDescriptionDefault
<provider>.enabledInstall that provider sub-chartfalse
<provider>.extraResourcesList of raw Kubernetes objects (usually managed resources this chart does not model yet) rendered verbatim in sync wave 2[]

azure​

ValueDescriptionDefault
azure.compositions.workloadIdentityInfrastructure.enabledInstall the WorkloadIdentityInfrastructure XRD + Compositiontrue
azure.compositions.disasterRecoveryInfrastructure.enabledInstall the DisasterRecoveryInfrastructure XRD + Compositionfalse
azure.credentials.secretName / .secretKeySecret (release namespace) and key with the Service Principal credentialsazure-credentials / credentials

The sub-chart installs the five Azure providers (azure-network, azure-storage, azure-managed-identity, azure-ad, azure-authorization; azure-network also pulls in crossplane-contrib-provider-family-azure which handles authentication for the whole family), the three composition functions (patch-and-transform, go-templating, auto-ready), the default ProviderConfig, and the two KubeAid composite APIs:

  • WorkloadIdentityInfrastructure (azure.kubeaid.org/v1alpha1): given subscriptionID, clusterName, location, aadApplicationPrincipalID and storageAccountName, provisions a ResourceGroup, a Blob storage Account + oidc-provider Container (the OIDC issuer for workload identity), a capi UserAssignedIdentity with a Contributor role assignment, and federated identity credentials for CAPZ and Azure Service Operator.
  • DisasterRecoveryInfrastructure (azure.kubeaid.org/v1alpha1): given subscriptionID, clusterName, location and storageAccountName, provisions velero-backups and sealed-secrets-backups Containers, a velero UserAssignedIdentity with a Storage Blob Data Owner role assignment, and a federated identity credential for the velero ServiceAccount.

Both compositions run mode: Pipeline, both XRDs use defaultCompositionUpdatePolicy: Manual (composition changes need an explicit revision bump on existing claims), and the ResourceGroup, Account and Container resources use the Orphan deletion policy. Unlike the old crossplane-compositions chart, the compositions.*.enabled switches here actually work: the old chart's nested sub-chart conditions were resolved against the wrong values path and always installed both APIs.

scaleway​

ValueDescriptionDefault
scaleway.provider.package / .versionprovider-scaleway package and versionxpkg.upbound.io/scaleway/provider-scaleway / v0.6.0
scaleway.credentials.secretName / .secretKeySecret (release namespace) and key with the Scaleway API keyscaleway-credentials / credentials
scaleway.projectIdScaleway project every resource is created in (required once buckets are configured)""
scaleway.regionScaleway regionfr-par
scaleway.bucketNameSuffixWhen set, a bucket's Scaleway name is <name>.<suffix> unless the bucket sets bucketName""
scaleway.tagsTags applied to every bucket (per-bucket tags merged on top){}
scaleway.managementPoliciesManagement policies for every bucket; no Delete by default[Observe, Create, Update, LateInitialize]
scaleway.buckets[]Object Storage buckets, see below[]

The provider runs with --enable-management-policies (a DeploymentRuntimeConfig), and every bucket authenticates through the default ClusterProviderConfig this sub-chart renders.

Creating a Scaleway bucket​

Append an entry to scaleway.buckets in the cluster's values-crossplane-provider.yaml, merge, and sync the crossplane-provider Application. Nothing else changes.

scaleway:
enabled: true
projectId: 11111111-2222-3333-4444-555555555555
region: fr-par
bucketNameSuffix: my-cluster # bucket names are global per region
tags:
environment: production
buckets:
- name: backups # Bucket resource name; Scaleway name backups.my-cluster
versioning: true
objectLock:
enabled: true # also renders a LockConfiguration
retention:
mode: GOVERNANCE
days: 30
lifecycleRules: # passed through verbatim as forProvider.lifecycleRule
- id: default
enabled: true
expiration:
- days: 30
abortIncompleteMultipartUploadDays: 1
- name: harbor-oci-artifacts
externalName: fr-par/harbor-oci-artifacts.my-cluster # adopt an existing bucket: <region>/<bucket name>
- name: legacy
bucketName: some-old-name # opt out of the suffix rule

Check the result with kubectl get buckets.object.scaleway.m.upbound.io -n crossplane; Ready=True and Synced=True means the bucket exists.

  • A bucket that already exists in Scaleway fails with BucketAlreadyOwnedByYou until the entry carries externalName in the form <region>/<bucket name>, after which Crossplane adopts it.
  • Removing an entry deletes the Bucket object but never the bucket in Scaleway, because the default management policies have no Delete. To really delete, set managementPolicies: ["*"] on that entry first, or delete it in the Scaleway console.
  • LockConfiguration needs an API key with object-lock permissions; otherwise it stays Synced=False with AccessDenied while the Bucket itself is fine.
  • Anything the chart does not model (bucket policies, databases, instances) goes into scaleway.extraResources as the raw managed resource.

keycloak​

ValueDescriptionDefault
keycloak.provider.package / .versionprovider-keycloak package and versionxpkg.upbound.io/crossplane-contrib/provider-keycloak / v2.18.0

This sub-chart installs the provider package (with --enable-management-policies) and nothing else. The ClusterProviderConfig and the Realm, User, Client, ... resources for a Keycloak that KubeAid deploys are rendered by the keycloakx chart from its own values file, because Crossplane v2 managed resources are namespaced and resolve the Secrets they reference (client secrets, initial passwords) in their own namespace, which is where the apps consuming those Secrets live. The rule generalises: a provider that targets an external cloud renders its resources here; a provider that configures an application KubeAid deploys renders them from that application's chart.

Crossview​

Crossview is a web UI for browsing Crossplane resources. It ships as the upstream crossview sub-chart and is off by default.

ValueDescriptionDefault
crossview.enabledDeploy Crossviewtrue
crossview.config.server.auth.modenone (no login), header (identity from a trusted proxy header) or session (login, needs Postgres)none
crossview.config.database.enabled, crossview.database.enabledUse a database / deploy the upstream postgres:latestfalse
global.postgresql.enabledCreate a CloudNativePG cluster crossview-pgsql via kubeaid-addons (session mode only)false
crossview.ingressIngress, traefik-cert-manager classdisabled

By default Crossview has no login and no database. Everyone who can reach it gets its read access to the whole cluster, Secrets included, so expose it only on a private ingress, or use header mode behind an auth proxy.

Session mode​

For a login (local admin or SSO), switch to session mode. Sessions are kept in Postgres, from the kubeaid-addons CNPG cluster or an existing CNPG cluster in the same namespace:

# CloudNativePG cluster crossview-pgsql (service crossview-pgsql-rw, credentials in
# Secret crossview-pgsql-app). Requires the cloudnative-pg operator.
global:
postgresql:
enabled: true
instanceName: crossview
database: crossview
owner: crossview
instances: 1
size: 2Gi

crossview:
config:
server:
auth:
mode: session
cors:
# Public URL of Crossview, no trailing slash. Also the post-SSO redirect target.
origin: https://crossview.example.com
database:
enabled: true
host: crossview-pgsql-rw
database: crossview
username: crossview
ssl:
mode: require
# Crossview falls back to admin/password and a hard-coded session secret when
# these are unset, so read them from a Secret you provide (e.g. a SealedSecret).
secrets:
adminUsername: admin
adminPassword:
secretKeyRef:
name: crossview-auth
key: admin-password
sessionSecret:
secretKeyRef:
name: crossview-auth
key: session-secret
dbPassword:
secretKeyRef:
name: crossview-pgsql-app
key: password

For an existing CNPG cluster, point host at its -rw service and secrets.dbPassword at its -app Secret; database and username must match its initdb owner. OIDC login (Keycloak etc.) is configured under crossview.config.sso.oidc, with the client secret in crossview.secrets.OIDCClientSecret.

How ordering works​

The chart is synced as ONE ArgoCD Application. Every object carries an argocd.argoproj.io/sync-wave:

WaveObjects
0Provider, DeploymentRuntimeConfig, Function packages
1ProviderConfig / ClusterProviderConfig, CompositeResourceDefinition
2Composition, managed resources (Bucket, LockConfiguration, ...), extraResources

ArgoCD only starts a wave once the previous one is healthy. ArgoCD ships a health check for pkg.crossplane.io/Provider that reports Progressing until the package is Installed and Healthy, so wave 1 waits for the CRDs. Objects in waves 1 and 2 also carry argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true, because ArgoCD dry-runs the whole manifest set before wave 0 has run and those kinds are unknown to the API server at that point.

Give the Application a retry block so a slow provider install is retried instead of failing the sync:

syncPolicy:
retry:
limit: 5
backoff:
duration: 30s
factor: 2
maxDuration: 5m
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=true

Migration from the two old charts​

Object names and specs are unchanged, so ArgoCD adopts the existing Providers, Functions, ProviderConfigs, XRDs, Compositions and managed resources; the only diff is the new annotations.

The old chart directories are gone from this KubeAid release on. An Application that still points at argocd-helm-charts/crossplane-providers-and-functions or argocd-helm-charts/crossplane-compositions can no longer be rendered once the cluster moves to it (ArgoCD reports a comparison error); the objects it created stay in the cluster. Migrate in the same change that bumps the KubeAid version.

  1. Add the crossplane-provider Application and its values file. Values move as follows: azure.enable → azure.enabled, azure.compositions.*.enable → .enabled, scaleway.enable → scaleway.enabled, scaleway.objectStorage.buckets → scaleway.buckets.
  2. Remove the crossplane-providers-and-functions and crossplane-compositions Applications without cascading: delete the Application objects with --cascade=false, or remove their resources-finalizer.argocd.argoproj.io finalizer first. A normal (cascading) delete removes the Providers and every managed resource with them.
  3. Sync crossplane-provider; it takes ownership of the orphaned objects.