Composite resource definitions (XRDs) define the type and schema of your composite resource (XR). Think of the XRD as the object that defines the shape of your API. The fields you choose to define in the
spec of your XRD are highly dependent on your use case. Below are best practices you can apply to help you approach how to define an XRD.
If you haven’t read and gone through the framework’s self-evaluation exercise, it’s recommended you do that now. It’s difficult to build a custom API without knowing who you are building it for and why you’re building it. First, figure this out. The next questions you need to figure out are:
- What managed resources do you want to compose together as part of this API?
- For each managed resource you want to compose, what are the required fields?
Most Crossplane providers have lots of different managed resources that map to appropriate APIs to manage a particular set of infrastructure or resources.
|Generic||Crossplane provider-aws||AWS infrastructure|
|resource-1||An EC2 Instance|
|resource-2||A Security Group|
When you create infrastructure in a cloud service, sometimes that infrastructure maps to a single API (such as an S3 bucket). Other times it actually maps to multiple APIs. For example, when you create an EKS cluster in AWS, these objects get created:
- an AWS cluster object
- some IAM rules
- a NodeGroup
- network settings
- and more
Multiple APIs get called and multiple objects get created.
Use the Upbound Marketplace’s API documentation feature. In the Marketplace, search for a resource you want to create. Within that resource’s API documentation, under the
spec.forProvider struct, look for properties that have a naming pattern like
<resource>IdSelector. Those properties may communicate that, in order for your desired resource to create, you may need to provide references to other resources. This tip is a heuristic, not a firm rule.
As an example, consider the provider-aws
Instance resource for an EC2 Instance. You can see below there is a reference to a
networkInterface. This points to another resource you may choose to create in Crossplane and then reference in your Instance resource.
Two reasons it’s important to know the required fields in the managed resources you want to compose are:
- There’s a high likelihood you want to either expose or let your consumers influence indirectly these fields in your API
- Composite Resources result in instantiated managed resources. You need to create valid managed resources in order for your composite resource to create.
Use the Upbound Marketplace’s API documentation feature to see the required fields under the
spec.forProvider. For example, notice in the AWS EKS Cluster resource API documentation how
region has a “required” label whereas
Sometimes a managed resource has fields that need to have a value but it’s not explicitly marked as
required. For example, consider the AWS Lambda Function resource API documentation. It stipulates that
filename, image_uri, or s3_bucket must be specified, yet none of those fields have a “required” label. It’s helpful to look at a resource’s “Examples” tab in the Upbound Marketplace. Examples show verified configurations for managed resources.
XRDs follow the OpenAPI “structural schema.” Below is boilerplate .YAML that you can use to scaffold the start of an XRD.
apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition metadata: name: <plural-name>.<group> spec: group: <group.example.com> names: kind: X-<kind-name> plural: X-<kind-name-plural> claimNames: kind: <kind-name> plural: <kind-name-plural> versions: - name: v1alpha1 served: true referenceable: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: parameters: type: object properties: <to-do> required: - parameters status: type: object properties: <todo>
In your XRD, you should give your Composite Resource a name and update the API group. The preceding scaffold also assumes you want this Composite Resource to be claimable. If you don’t want to allow users to create claims against this Composite Resource, omit the
The following are some other best practices to abide by as you author new XRDs:
All the inputs you want to provide as part of your API belong in your XRD’s
spec.properties.parameters stanza, as seen in the example below.
apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition spec: ... versions: - name: v1alpha1 served: true referenceable: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: parameters: type: object properties: storageGB: type: integer required: - storageGB required: - parameters
The OpenAPI v3 data models define the types and values available for you to use as part of defining your XRD.
The OpenAPI v3 data types define the value types available for you to use as part of your schema.
It’s recommended you use Enums for static options for field value. For example, if you have field called
environment and there are a defined list of options for your users to choose from:
environment: type: string enum: [Dev, DR, UAT, Pre-prod, Prod]
It’s recommended you use
pattern for complex string validations. For example:
teamEmailAddress: type: string pattern: "^[a-zA-Z0-9@\\.]*$"
spec.status field of your XRD is a useful way to exchange data between various Crossplane resources. To patch a value from one Crossplane resource to another, you can publish and consume values via an XRD’s status. To do so, your XRD must first define the status field. Patching is a concept relevant to compositions; it’s discussed in detail later for how to do this.
Over the lifetime of your custom API, there is a chance the shape of the API could change and you could introduce breaking changes. Crossplane has a built-in capability to help with this. The preceding scaffolding recommends a boilerplate version named
v1alpha1. Notice the
versions field is an array, so you can declare multiple versions of your API definition in the XRD. Crossplane doesn’t allow serving multiple versions. Meaning, once v1alpha2 goes live, all the composites are force-migrated to that version:
apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition spec: ... versions: - name: v1alpha1 served: false referenceable: true schema: ... - name: v1alpha2 served: true referenceable: true schema: ...
Note the preceding example is just the definition part of your new API version. You also need to create new compositions that serve the implementation the new version. Read Authoring Compositions to learn how to do that.
Beginning in Crossplane v1.12.0 and later, Crossplane supports using webhooks to convert resources between defined versions of an XRD. To do this, you declare a
conversion strategy in the
spec of the XRD.
apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition spec: conversion: strategy: Webhook webhook: ...
Defining an XRD is half the equation for building a custom API. The other important steps is to define a composition which implements the definition contained in your XRD. Read Authoring Compositions to learn how to do that.