This document is for an older version of Crossplane.

This document applies to Crossplane v1.20 and not to the latest release v2.2.

Important

This guide is part 2 of a series.

Part 1 covers to installing Crossplane and connect your Kubernetes cluster to GCP.

This guide walks you through building and accessing a custom API with Crossplane.

Prerequisites

  1. Add the Crossplane Helm repository and install Crossplane.
1helm repo add \
2crossplane-stable https://charts.crossplane.io/stable
3helm repo update
4&&
5helm install crossplane \
6crossplane-stable/crossplane \
7--namespace crossplane-system \
8--create-namespace
  1. When the Crossplane pods finish installing and are ready, apply the GCP Provider.
1
2
3
4
5
6
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-gcp-storage
spec:
  package: xpkg.crossplane.io/crossplane-contrib/provider-gcp-storage:v1.12.1
1kubectl apply -f https://deploy-preview-1086--crossplane.netlify.app/v1.20/manifests/getting-started/gcp-part-2/provider-gcp-storage.yaml
  1. Create a file called gcp-credentials.json with your GCP service account JSON file.
Tip
The GCP documentation provides information on how to generate a service account JSON file.
  1. Create a Kubernetes secret from the GCP JSON file
1kubectl create secret \
2generic gcp-secret \
3-n crossplane-system \
4--from-file=creds=./gcp-credentials.json
  1. Create a ProviderConfig Include your GCP project ID in the ProviderConfig settings.
Tip
Find your GCP project ID from the project_id field of the gcp-credentials.json file.
 1cat <<EOF | kubectl apply -f -
 2apiVersion: gcp.upbound.io/v1beta1
 3kind: ProviderConfig
 4metadata:
 5  name: default
 6spec:
 7  projectID: 
 8  credentials:
 9    source: Secret
10    secretRef:
11      namespace: crossplane-system
12      name: gcp-secret
13      key: creds
14EOF

Install the PubSub Provider

Part 1 only installed the GCP Storage Provider. This section deploys a PubSub Topic along with a GCP storage bucket. First install the GCP PubSub Provider.

Add the new Provider to the cluster.

1
2
3
4
5
6
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-gcp-pubsub
spec:
  package: xpkg.crossplane.io/crossplane-contrib/provider-gcp-pubsub:v1.12.1
1kubectl apply -f https://deploy-preview-1086--crossplane.netlify.app/v1.20/manifests/getting-started/gcp-part-2/provider-gcp-pubsub.yaml

View the new PubSub provider with kubectl get providers.

1kubectl get providers
2NAME                                     INSTALLED   HEALTHY   PACKAGE                                                              AGE
3crossplane-contrib-provider-family-gcp   True        True      xpkg.crossplane.io/crossplane-contrib/provider-family-gcp:v1.12.1    48m
4provider-gcp-pubsub                      True        True      xpkg.crossplane.io/crossplane-contrib/provider-gcp-pubsub:v1.12.1    14s
5provider-gcp-storage                     True        True      xpkg.crossplane.io/crossplane-contrib/provider-gcp-storage:v1.12.1   48m

Create a custom API

Crossplane allows you to build your own custom APIs for your users, abstracting away details about the cloud provider and their resources. You can make your API as complex or simple as you wish.

The custom API is a Kubernetes object. Here is an example custom API.

1apiVersion: database.example.com/v1alpha1
2kind: NoSQL
3metadata:
4  name: my-nosql-database
5spec:
6  location: "US"

Like any Kubernetes object the API has a version, kind and spec.

Define a group and version

To create your own API start by defining an API group and version.

The group can be any value, but common convention is to map to a fully qualified domain name.

The version shows how mature or stable the API is and increments when changing, adding or removing fields in the API.

Crossplane doesn’t require specific versions or a specific version naming convention, but following Kubernetes API versioning guidelines is strongly recommended.

  • v1alpha1 - A new API that may change at any time.
  • v1beta1 - An existing API that’s considered stable. Breaking changes are strongly discouraged.
  • v1 - A stable API that doesn’t have breaking changes.

This guide uses the group database.example.com.

Because this is the first version of the API, this guide uses the version v1alpha1.

1apiVersion: database.example.com/v1alpha1

Define a kind

The API group is a logical collection of related APIs. In a group are individual kinds representing different resources.

For example a queue group may have a PubSub and CloudTask kinds.

The kind can be anything, but it must be UpperCamelCased.

This API’s kind is PubSub

1apiVersion: queue.example.com/v1alpha1
2kind: PubSub

Define a spec

The most important part of an API is the schema. The schema defines the inputs accepted from users.

This API allows users to provide a location of where to run their cloud resources.

All other resource settings can’t be configurable by the users. This allows Crossplane to enforce any policies and standards without worrying about user errors.

1apiVersion: queue.example.com/v1alpha1
2kind: PubSub
3spec:
4  location: "US"

Apply the API

Crossplane uses Composite Resource Definitions (also called an XRD) to install your custom API in Kubernetes.

The XRD spec contains all the information about the API including the group, version, kind and schema.

The XRD’s name must be the combination of the plural and group.

The schema uses the OpenAPIv3 specification to define the API spec.

The API defines a location that must be oneOf either EU or US.

Apply this XRD to create the custom API in your Kubernetes cluster.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: pubsubs.queue.example.com
spec:
  group: queue.example.com
  names:
    kind: PubSub
    plural: pubsubs
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              location:
                type: string
                oneOf:
                  - pattern: '^EU$'
                  - pattern: '^US$'
            required:
              - location
    served: true
    referenceable: true
  claimNames:
    kind: PubSubClaim
    plural: pubsubclaims
1kubectl apply -f https://deploy-preview-1086--crossplane.netlify.app/v1.20/manifests/getting-started/gcp-part-2/pubsubs.queue.example.com.yaml

Adding the claimNames allows users to access this API either at the cluster level with the pubsub endpoint or in a namespace with the pubsubclaim endpoint.

The namespace scoped API is a Crossplane Claim.

Tip
For more details on the fields and options of Composite Resource Definitions read the XRD documentation.

View the installed XRD with kubectl get xrd.

1kubectl get xrd
2NAME                        ESTABLISHED   OFFERED   AGE
3pubsubs.queue.example.com   True          True      7s

View the new custom API endpoints with kubectl api-resources | grep pubsub

1kubectl api-resources | grep queue.example
2pubsubclaims                 queue.example.com/v1alpha1             true         PubSubClaim
3pubsubs                      queue.example.com/v1alpha1             false        PubSub

Create a deployment template

When users access the custom API Crossplane takes their inputs and combines them with a template describing what infrastructure to deploy. Crossplane calls this template a Composition.

The Composition defines all the cloud resources to deploy. Each entry in the template is a full resource definitions, defining all the resource settings and metadata like labels and annotations.

This template creates a GCP Storage Bucket and a PubSub Topic.

This Composition takes the user’s location input and uses it as the location used in the individual resource.

Important

This Composition uses an array of resource templates. You can patch each template with data copied from the custom API. Crossplane calls this a Patch and Transform Composition.

You don’t have to use Patch and Transform. Crossplane supports a variety of alternatives, including Go Templating and CUE. You can also write a function in Go or Python to template your resources.

Read the Composition documentation for more information on configuring Compositions and all the available options.

Apply this Composition to your cluster.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: topic-with-bucket
spec:
  mode: Pipeline
  pipeline:
  - step: patch-and-transform
    functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      kind: Resources
      resources:
        - name: crossplane-quickstart-bucket
          base:
            apiVersion: storage.gcp.upbound.io/v1beta1
            kind: Bucket
            spec:
              forProvider:
                location: "US"
          patches:
            - fromFieldPath: "spec.location"
              toFieldPath: "spec.forProvider.location"
              transforms:
                - type: map
                  map:
                    EU: "EU"
                    US: "US"
        - name: crossplane-quickstart-topic
          base:
            apiVersion: pubsub.gcp.upbound.io/v1beta1
            kind: Topic
            spec:
              forProvider:
                messageStoragePolicy:
                  - allowedPersistenceRegions:
                    - "us-central1"
          patches:
            - fromFieldPath: "spec.location"
              toFieldPath: "spec.forProvider.messageStoragePolicy[0].allowedPersistenceRegions[0]"
              transforms:
                - type: map
                  map:
                    EU: "europe-central2"
                    US: "us-central1"
  compositeTypeRef:
    apiVersion: queue.example.com/v1alpha1
    kind: PubSub
1kubectl apply -f https://deploy-preview-1086--crossplane.netlify.app/v1.20/manifests/getting-started/gcp-part-2/topic-with-bucket.yaml

The compositeTypeRef defines which custom APIs can use this template to create resources.

A Composition uses a pipeline of composition functions to define the cloud resources to deploy. This template uses function-patch-and-transform. You must install the function before you can use it in a Composition.

Apply this Function to install function-patch-and-transform:

1
2
3
4
5
6
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
  name: function-patch-and-transform
spec:
  package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.8.2
1kubectl apply -f https://deploy-preview-1086--crossplane.netlify.app/v1.20/manifests/getting-started/gcp-part-2/function-patch-and-transform.yaml
Tip

Read the Composition documentation for more information on configuring Compositions and all the available options.

Read the Patch and Transform function documentation for more information on how it uses patches to map user inputs to Composition resource templates.

View the Composition with kubectl get composition

1kubectl get composition
2NAME                XR-KIND   XR-APIVERSION       AGE
3topic-with-bucket   PubSub    queue.example.com   3s

Access the custom API

With the custom API (XRD) installed and associated to a resource template (Composition) users can access the API to create resources.

Create a PubSub object to create the cloud resources.

1
2
3
4
5
6
apiVersion: queue.example.com/v1alpha1
kind: PubSub
metadata:
  name: my-pubsub-queue
spec:
  location: "US"
1kubectl apply -f https://deploy-preview-1086--crossplane.netlify.app/v1.20/manifests/getting-started/gcp-part-2/pubsub-my-pubsub-queue.yaml

View the resource with kubectl get pubsub.

1kubectl get pubsub
2NAME              SYNCED   READY   COMPOSITION         AGE
3my-pubsub-queue   True     True    topic-with-bucket   2m12s

This object is a Crossplane composite resource (also called an XR). It’s a single object representing the collection of resources created from the Composition template.

View the individual resources with kubectl get managed

1kubectl get managed
2NAME                                                READY   SYNCED   EXTERNAL-NAME           AGE
3topic.pubsub.gcp.upbound.io/my-pubsub-queue-cjswx   True    True     my-pubsub-queue-cjswx   3m4s
4
5NAME                                                  READY   SYNCED   EXTERNAL-NAME           AGE
6bucket.storage.gcp.upbound.io/my-pubsub-queue-vljg9   True    True     my-pubsub-queue-vljg9   3m4s

Delete the resources with kubectl delete pubsub.

1kubectl delete pubsub my-pubsub-queue
2pubsub.queue.example.com "my-pubsub-queue" deleted

Verify Crossplane deleted the resources with kubectl get managed

Note
It may take up to 5 minutes to delete the resources.
1kubectl get managed
2No resources found

Using the API with namespaces

Accessing the API pubsub happens at the cluster scope. Most organizations isolate their users into namespaces.

A Crossplane Claim is the custom API in a namespace.

Creating a Claim is just like accessing the custom API endpoint, but with the kind from the custom API’s claimNames.

Create a new namespace to test create a Claim in.

1kubectl create namespace crossplane-test

Then create a Claim in the crossplane-test namespace.

1
2
3
4
5
6
7
apiVersion: queue.example.com/v1alpha1
kind: PubSubClaim
metadata:
  name: my-pubsub-queue
  namespace: crossplane-test
spec:
  location: "US"
1kubectl apply -f https://deploy-preview-1086--crossplane.netlify.app/v1.20/manifests/getting-started/gcp-part-2/pubsubclaim-my-pubsub-queue.yaml
View the Claim with kubectl get claim -n crossplane-test.

1kubectl get claim -n crossplane-test
2NAME                SYNCED   READY   CONNECTION-SECRET   AGE
3my-pubsub-queue   True     True                        2m10s

The Claim automatically creates a composite resource, which creates the managed resources.

View the Crossplane created composite resource with kubectl get composite.

1kubectl get composite
2NAME                    SYNCED   READY   COMPOSITION         AGE
3my-pubsub-queue-7bm9n   True     True    topic-with-bucket   3m10s

Again, view the managed resources with kubectl get managed.

1kubectl get managed
2NAME                                                      READY   SYNCED   EXTERNAL-NAME                 AGE
3topic.pubsub.gcp.upbound.io/my-pubsub-queue-7bm9n-6kdq4   True    True     my-pubsub-queue-7bm9n-6kdq4   3m22s
4
5NAME                                                        READY   SYNCED   EXTERNAL-NAME                 AGE
6bucket.storage.gcp.upbound.io/my-pubsub-queue-7bm9n-hhwx8   True    True     my-pubsub-queue-7bm9n-hhwx8   3m22s

Deleting the Claim deletes all the Crossplane generated resources.

kubectl delete claim -n crossplane-test my-pubsub-queue

1kubectl delete pubsubclaim my-pubsub-queue -n crossplane-test
2pubsubclaim.queue.example.com "my-pubsub-queue" deleted
Note
It may take up to 5 minutes to delete the resources.

Verify Crossplane deleted the composite resource with kubectl get composite.

1kubectl get composite
2No resources found

Verify Crossplane deleted the managed resources with kubectl get managed.

1kubectl get managed
2No resources found

Next steps