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
crossplanecore installed in the cluster (same namespace, by defaultcrossplane).- 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 thekeycloakxchart, see below. - The ArgoCD Application must track resources by annotation
(
configs.cm.application.resourceTrackingMethod: annotationinargo-cd, KubeAid's default) and should carry a syncretryblock, see How ordering works.
Key values / KubeAid-specific configuration
Common
| Value | Description | Default |
|---|---|---|
<provider>.enabled | Install that provider sub-chart | false |
<provider>.extraResources | List of raw Kubernetes objects (usually managed resources this chart does not model yet) rendered verbatim in sync wave 2 | [] |
azure
| Value | Description | Default |
|---|---|---|
azure.compositions.workloadIdentityInfrastructure.enabled | Install the WorkloadIdentityInfrastructure XRD + Composition | true |
azure.compositions.disasterRecoveryInfrastructure.enabled | Install the DisasterRecoveryInfrastructure XRD + Composition | false |
azure.credentials.secretName / .secretKey | Secret (release namespace) and key with the Service Principal credentials | azure-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): givensubscriptionID,clusterName,location,aadApplicationPrincipalIDandstorageAccountName, provisions aResourceGroup, a Blob storageAccount+oidc-providerContainer(the OIDC issuer for workload identity), acapiUserAssignedIdentitywith aContributorrole assignment, and federated identity credentials for CAPZ and Azure Service Operator.DisasterRecoveryInfrastructure(azure.kubeaid.org/v1alpha1): givensubscriptionID,clusterName,locationandstorageAccountName, provisionsvelero-backupsandsealed-secrets-backupsContainers, aveleroUserAssignedIdentitywith aStorage Blob Data Ownerrole assignment, and a federated identity credential for theveleroServiceAccount.
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
| Value | Description | Default |
|---|---|---|
scaleway.provider.package / .version | provider-scaleway package and version | xpkg.upbound.io/scaleway/provider-scaleway / v0.6.0 |
scaleway.credentials.secretName / .secretKey | Secret (release namespace) and key with the Scaleway API key | scaleway-credentials / credentials |
scaleway.projectId | Scaleway project every resource is created in (required once buckets are configured) | "" |
scaleway.region | Scaleway region | fr-par |
scaleway.bucketNameSuffix | When set, a bucket's Scaleway name is <name>.<suffix> unless the bucket sets bucketName | "" |
scaleway.tags | Tags applied to every bucket (per-bucket tags merged on top) | {} |
scaleway.managementPolicies | Management 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
BucketAlreadyOwnedByYouuntil the entry carriesexternalNamein the form<region>/<bucket name>, after which Crossplane adopts it. - Removing an entry deletes the
Bucketobject but never the bucket in Scaleway, because the default management policies have noDelete. To really delete, setmanagementPolicies: ["*"]on that entry first, or delete it in the Scaleway console. LockConfigurationneeds an API key with object-lock permissions; otherwise it staysSynced=FalsewithAccessDeniedwhile theBucketitself is fine.- Anything the chart does not model (bucket policies, databases, instances) goes into
scaleway.extraResourcesas the raw managed resource.
keycloak
| Value | Description | Default |
|---|---|---|
keycloak.provider.package / .version | provider-keycloak package and version | xpkg.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.
| Value | Description | Default |
|---|---|---|
crossview.enabled | Deploy Crossview | true |
crossview.config.server.auth.mode | none (no login), header (identity from a trusted proxy header) or session (login, needs Postgres) | none |
crossview.config.database.enabled, crossview.database.enabled | Use a database / deploy the upstream postgres:latest | false |
global.postgresql.enabled | Create a CloudNativePG cluster crossview-pgsql via kubeaid-addons (session mode only) | false |
crossview.ingress | Ingress, traefik-cert-manager class | disabled |
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:
| Wave | Objects |
|---|---|
0 | Provider, DeploymentRuntimeConfig, Function packages |
1 | ProviderConfig / ClusterProviderConfig, CompositeResourceDefinition |
2 | Composition, 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.
- Add the
crossplane-providerApplication 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. - Remove the
crossplane-providers-and-functionsandcrossplane-compositionsApplications without cascading: delete the Application objects with--cascade=false, or remove theirresources-finalizer.argocd.argoproj.iofinalizer first. A normal (cascading) delete removes the Providers and every managed resource with them. - Sync
crossplane-provider; it takes ownership of the orphaned objects.