DEV Community

Joonas Venäläinen for Polar Squad

Posted on • Edited on

Crossplane: How do providers work

Providers are the meat around Crossplane’s bones, and they are used to extend the capabilities of Crossplane. When Crossplane is installed, it doesn't have any capabilities to interact with external systems. A core Crossplane pod will only watch the following resources.

compositeresourcedefinitions.apiextensions.crossplane.io
compositionrevisions.apiextensions.crossplane.io         
compositions.apiextensions.crossplane.io                 
configurationrevisions.pkg.crossplane.io                 
configurations.pkg.crossplane.io                         
controllerconfigs.pkg.crossplane.io                      
locks.pkg.crossplane.io                                  
providerrevisions.pkg.crossplane.io                      
providers.pkg.crossplane.io                              
storeconfigs.secrets.crossplane.io
Enter fullscreen mode Exit fullscreen mode

When you install a provider, a new pod is created to Crossplane's installation namespace. This pod is a Kubernetes Controller that watches the CRDs that are also installed as part of the provider package.

To find out what different kinds of providers are available, you can check the Upbound Marketplace and crossplane-contrib repository. For this series, we are going to work with the following providers:

Those GCP providers are installed from the provider-family-gcp package. These provider-family packages are special packages that allow you to install only the provider packages you need instead of installing everything, which would mean 343 CRDs if you install the provider-gcp package instead. Crossplane also states:

On average, 30 CRDs are used from Provider packages.

Looking at the average number, you would still have ~313 CRDs in the cluster that aren't used 🤯.

Install the providers

cat <<EOF | kubectl apply --filename=-
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-gcp-storage
spec:
  package: xpkg.upbound.io/upbound/provider-gcp-storage:v0.36.0
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-gcp-cloudplatform
spec:
  package: xpkg.upbound.io/upbound/provider-gcp-cloudplatform:v0.36.0
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-terraform
spec:
  package: xpkg.upbound.io/upbound/provider-terraform:v0.10.0
EOF
Enter fullscreen mode Exit fullscreen mode

After a little while, you should see the providers installed and in a healthy state

kubectl get provider
---
NAME                          INSTALLED   HEALTHY   PACKAGE                                                      AGE
provider-gcp-cloudplatform    True        True      xpkg.upbound.io/upbound/provider-gcp-cloudplatform:v0.36.0   116s
provider-gcp-storage          True        True      xpkg.upbound.io/upbound/provider-gcp-storage:v0.36.0         116s
provider-terraform            True        True      xpkg.upbound.io/upbound/provider-terraform:v0.10.0           116s
upbound-provider-family-gcp   True        True      xpkg.upbound.io/upbound/provider-family-gcp:v0.37.0          107s
Enter fullscreen mode Exit fullscreen mode

Now the providers are installed and ready, we need to set up the ProviderConfig, which configures the credentials for the provider to be able to interact with external systems, in this case, with Google Cloud. You can have multiple ProviderConfigs and reference them in managed resources using providerConfigRef. ProviderConfigs are cluster-scoped resources.

You can set up a ProviderConfig per tenant when you have a multi-tenant cluster. When creating compositions, you could patch the value of providerConfigRef in managed resources with a value of spec.claimRef.namespace, which points to the namespace where the XRC was created.

Multi-tenant providerconfig

Every provider has their own individual settings available when it comes to ProviderConfig. For the GCP provider, you can find all the available configuration options here and for Terraform provider here. If you need to override Controller related settings eg. ServiceAccount you can use ControllerConfig for that.

In upcoming chapters, we will create resources in Google Cloud that involve creating a bucket, serviceaccount, iam-binding, and serviceaccountkey. Use the following to configure a new service account with needed permissions in GCP:

# GCP Project ID
PROJECT_ID=""

gcloud iam service-accounts create crossplane-sa-demo --display-name "Crossplane Service Account Demo"

gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:crossplane-sa-demo@$PROJECT_ID.iam.gserviceaccount.com --role roles/storage.admin
gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:crossplane-sa-demo@$PROJECT_ID.iam.gserviceaccount.com --role roles/iam.serviceAccountAdmin
gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:crossplane-sa-demo@$PROJECT_ID.iam.gserviceaccount.com --role roles/iam.serviceAccountKeyAdmin
gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:crossplane-sa-demo@$PROJECT_ID.iam.gserviceaccount.com --role roles/storage.iamMember
Enter fullscreen mode Exit fullscreen mode

Create a service account key:

gcloud iam service-accounts keys create credentials.json --iam-account=crossplane-sa-demo@$PROJECT_ID.iam.gserviceaccount.com
Enter fullscreen mode Exit fullscreen mode

Create a Kubernetes secret in crossplane-system namespace that contains the previously created credentials:

kubectl create secret generic gcp-creds --from-file=creds=./credentials.json -n crossplane-system
Enter fullscreen mode Exit fullscreen mode

Create ProviderConfig that uses these credentials:

cat <<EOF | kubectl apply --filename=-
apiVersion: gcp.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  projectID: $PROJECT_ID
  credentials:
    source: Secret
    secretRef:
    name: gcp-creds
    namespace: crossplane-system
    key: creds
EOF
Enter fullscreen mode Exit fullscreen mode

If you run this inside GKE, using the Workload Identity for authentication is much better. You can find detailed instructions for it here.

You can also read the secret from the filesystem using fs. This might come in handy in cases where you are leveraging, for example, Hashicorp Vault with Vault Agent sidecar to inject secrets to pods. Here is a quick example of how you would configure it without going into too much detail about how to work with Vault Agent Injector:

apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: gcp-config
spec:
  metadata:
    annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "crossplane-providers"
    ...
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-gcp-storage
spec:
  package: xpkg.upbound.io/upbound/provider-gcp-storage:v0.36.0
  controllerConfigRef:
    name: gcp-config
---
apiVersion: gcp.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  projectID: $PROJECT_ID
  credentials:
    source: Filesystem
    fs:
      path: /vault/secrets/gcp-creds
Enter fullscreen mode Exit fullscreen mode

Now we can quickly test that everything is working by creating a Bucket resource:

cat <<EOF | kubectl apply --filename=-
apiVersion: storage.gcp.upbound.io/v1beta1
kind: Bucket
metadata:
  name: ps-bucket-${RANDOM}
spec:
  forProvider:
    location: US
EOF
Enter fullscreen mode Exit fullscreen mode

After a little while, you should see the bucket resource ready and synced:

kubectl get bucket
---
NAME                            READY   SYNCED   EXTERNAL-NAME                  AGE
bucket-crossplane-demo-30855    True    True     bucket-crossplane-demo-30855   15m
Enter fullscreen mode Exit fullscreen mode

At this point, we are ready to start working with GCP using Crossplane. I will go through setting up the Terraform provider configs later in the series when it's time to start working with it.

Remember to delete the test bucket resource:

kubectl delete bucket <bucket_name>
Enter fullscreen mode Exit fullscreen mode

The next chapter quickly reviews available configuration options for managed resources.

Top comments (0)