Chapter 5: Exploring Infrastructure Platform Patterns

The success of running an infrastructure platform product with Crossplane depends on following a few principles and patterns as and when required. This chapter will explore some of these critical practices. We will also learn a few debugging skills while exploring the concepts. After learning the basics of the Crossplane in the last few chapters, this will be a place for learning advanced patterns that are key to building the state-of-the-art infrastructure platform for your organization. You will learn a few critical aspects of building robust XR APIs and debugging issues with ease.

The topics covered in this chapter are as follows:

  • Evolving the APIs
  • Nested and multi-resource XRs
  • XRD detailed
  • Managing external software resources

Evolving the APIs

Crossplane is primarily an API-based infrastructure automation platform. Changes to the APIs are inevitable as the business requirements and technology landscape evolve. We can classify these changes into three different buckets:

  • API implementation change
  • Non-breaking API contract change
  • Breaking API contract change

Let’s start with the API implementation change.

API implementation change

These changes are limited to the API implementation details without any changes to the contract. In other words, these are changes to Compositions YAML, a construct used by XR for API implementation. CompositionRevision is the Crossplane concept that will work with compositions to support such changes. If the --enable-composition-revisions flag is set while installing Crossplane, a CompositionRevision object is created with all the updates to composition. The name of the CompositionRevision object is autogenerated on every increment. The compositions are mutable objects that can change forever, but individual CompositionRevision is immutable. Composition and CompositionRevision are in one-to-many relationships. We will have only one CompositionRevision active at any given instance. The latest revision number will always be active, excluding the following scenario.

Tip

Each configuration state of the composition maps to a single CompositionRevision. Let’s say we are in revision 2 and changing the composition configuration the same as the first revision. A new revision is not created. Instead, revision 1 becomes active, making revision 2 inactive.

In a Crossplane environment where the composition revision flag is enabled, we will have two attributes automatically added to every XR/Claim object by Crossplane. The following are attribute names and how they are used:

  • spec.compositionRevisionRef: This will hold the name of CompositionRevision with which the resources are created.
  • spec.compositionUpdatePolicy: This attribute will indicate whether the XR/Claim will automatically migrate to a new, available CompositionRevision. Manual and automatic are the two possible values, with automatic as the default value. If you would like to override the default behavior, add this attribute with a manual indicator in the XR/Claim configuration.

The following diagram represents how Composition and CompositionRevision work together to evolve infrastructure API implementation continuously:

Figure 5.1 – Evolving compositions

Figure 5.1 – Evolving compositions

To manually migrate the composition, update the spec.compositionRevisionRef configuration in the XR/Claim with the latest revision name. This specific design enables the separation of concerns between the platform API creator and consumers. Infrastructure API creators will update the compositions, and API consumers can choose their revision upgrade strategy. If you want a specific revision of composition to be used while creating an XR/Claim, explicitly mention the revision name under spec.compositionRevisionRef.

Let’s look at some examples of such changes:

  • Bugfix: Let's say we mapped an incorrect attribute to the XRD status field. The scope of the correct mapping is limited to changes in the respective Composition patch section.
  • Policy changes without contract change: Adding a new infrastructure compliance policy to provide all new resources in a specific region.
  • Shared infrastructure: Moving toward a shared Virtual Private Cloud (VPC) instead of dynamically provisioning a new VPC for all new XR/Claim requests.

The composition revision flag is not enabled by default. Use the --enable-composition-revisions argument with a Crossplane pod to enable composition revision. The following Helm command will set up/update the Crossplane environment with composition revision:

#Enable Composition revision in an existing environment
helm upgrade crossplane –namespace crossplane-system crossplane-stable/crossplane –set args='{--enable-composition-revisions}' 
#Enable Composition revision in a new Crossplane setup
helm install crossplane –namespace crossplane-system crossplane-stable/crossplane –set args='{--enable-composition-revisions}'

The following section will look at composition revision with an example.

Hands-on journey with composition revision

Let’s go through a hands-on journey to experience composition revision. The objectives of the exercise will be as follows:

  • Building an XR API for GCP MySQL provisioning in a composition revision-enabled Crossplane environment
  • Creating two MySQL instances with automated and manual composition revision policies
  • Updating the Composition to change the calculation for database disk size
  • Validating if the MySQL instance with automated revision policy automatically migrates to the latest composition revision
  • Seeing that the MySQL instance with the manual revision policy does not migrate to the latest composition revision
  • Finally, migrating the second MySQL instance manually to the latest composition revision

Let’s use a simple XRD and composition to explore composition revision. The following is the XRD with just one parameter that takes the MySQL disk size:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xmysqls.composition-revision.imarunrk.com
spec:
  group: composition-revision.imarunrk.com
  names:
    kind: XMySql
    plural: xmysqls
  claimNames:
    kind: MySql
    plural: mysqls
  versions:
  - name: v1
    served: true
    referenceable: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  size:
                    type: integer
                required:
                - size
            required:
            - parameters

The composition for the preceding XRD is as follows, which patches the size attribute from XR into the GCP CloudSQLInstance MR:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: gcp-mysql
spec:
  compositeTypeRef:
    apiVersion: composition-revision.imarunrk.com/v1
    kind: XMySql
  resources:
  - name: cloudsqlinstance
    base:
      apiVersion: database.gcp.crossplane.io/v1beta1
      kind: CloudSQLInstance
      spec:
        providerConfigRef:
          name: gcp-credentials-project-1
        forProvider:
          region: us-central1
          databaseVersion: MYSQL_5_7
          settings:
            tier: db-g1-small
            dataDiskSizeGb: 40
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.size
      toFieldPath: spec.forProvider.settings.dataDiskSizeGb

Apply both the YAML to a target Crossplane cluster with composition revision enabled. You will see that CompositionRevision is created for the composition. Execute the following command to view all CompositionRevision for the given composition:

# List of revisions for Composition named gcp-mysql
kubectl get compositionrevision -l crossplane.io/composition-name=gcp-mysql

Refer to the following screenshot with one revision object created for the gcp-mysql composition. Note that the current attribute is true for revision 1. It will change if we update the composition:

Figure 5.2 – Composition Revision list

Figure 5.2 – Composition Revision list

Now, let’s provision two MySQL instances with the Claim API. An example of manual revision update policy configuration is as follows. The automated revision version of the YAML will be the same without the compositionUpdatePolicy parameter, which defaults to an automatic revision update:

apiVersion: composition-revision.imarunrk.com/v1
kind: MySql
metadata:
  namespace: alpha
  name: mysql-db-manual
spec:
  compositionUpdatePolicy: Manual
  compositionRef:
    name: gcp-mysql
  parameters:
    size: 10

You can refer to the following screenshot with two MySQL instances onboarded:

Figure 5.3 – MySQL claims

Figure 5.3 – MySQL claims

Now, update the composition patch with a transform function to multiply the disk size by four before patching. The patches section of the updated composition will look like the following:

patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.size
      toFieldPath: spec.forProvider.settings.dataDiskSizeGb
      transforms:
      - type: math
        math:
          multiply: 4

After updating the composition, you will see two revisions. Only the latest revision will have the current flag of true. Also, we can notice that the MySQL provisioned with an automated revision update policy would have increased the storage. The following screenshot summarizes the output after applying the updated composition:

Figure 5.4 – New Composition Revision

Figure 5.4 – New Composition Revision

Finally, we can manually upgrade the second MySQL instance by adding the spec.compositionRevisionRef attribute to the XR/Claim configuration. The field will hold the autogenerated composition revision name. The composition revision hands-on journey example is available at https://github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/Composition-Revision. In the following section, we will explore the ways to change the XR API contract.

API contract changes

API implementation details are just one direction in which XR changes can evolve. The highly interoperable API contract between the XR creating and consuming teams also needs to change over time. Contract change can fall under two categories:

  • Non-breaking changes: The XR API will be backward-compatible, meaning that consumers are either not impacted by the change or can choose to adopt the new changes at their phase.
  • Breaking changes: The XR API will not be backward-compatible. A new API version must be introduced, and the old API version must be deprecated at an appropriate time. All old API users should be safely migrating to the new API version.

Let’s delve into non-breaking changes.

Non-breaking changes

Adding one or more optional parameters to the XRD contract can be considered a non-breaking change. It is non-breaking because the old external resources provisioned can co-exist with the new schema as the new parameters are optional. Note that removing an existing optional parameter in the XRD is a breaking change as Crossplane upfront does not know how to reconcile existing provisioned resources. A simple way to think about this is that if Composition/CompositionRevision can handle the co-existence of old and newly provisioned resources, then the XRD contract change is non-breaking. A new optional parameter in the MySQL XR to choose the disk size is an example of a non-breaking change. The change will involve both a contract change and a composition revision. Let’s go through a hands-on journey to make the previous XR example. All the configuration YAML required for this journey is available at https://github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/XRD-Contract-Change-Non-Breaking. Refer to the following screenshot of our hands-on journey:

Figure 5.5 – Non-breaking contract change

Figure 5.5 – Non-breaking contract change

The following are the steps to be performed throughout the hands-on journey to experiment with the non-breaking contract change:

  1. Create the first version of the XRD in the target cluster (xrd-v1.yaml). The schema has vm as a mandatory parameter.
  2. Create the first revision of the composition (Composition-V1.yaml). It will patch the vm value back into the MR-CloudSQLInstance tier attribute.
  3. Now, the MySQL resource can be provisioned with db-n1-standard-1 as the tire in GCP (Claim-v1.yaml).
  4. Update and apply the XRD with an additional optional parameter, size, to specify the database disk size (xrd-v2.yaml).
  5. Update and apply the new composition (Composition-V2.yaml). It will patch the additional size parameter into the MR.
  6. Finally, create the second MySQL instance with a specific disk size and tire (Claim-v2.yaml).
  7. To validate whether the first MySQL instance can be sill updated, change the tier with an update YAML (Claim-v1-validate.yaml).

We did not upgrade the API version when updating the contract. We will discuss this more in the upcoming section.

Version upgrade

In the previous section, we did not change the XRD version number from v1. Crossplane does not currently support XR version upgrades once a contract changes. API versioning without a contract change will be helpful in indicating API stability (alpha, beta, v1, and so on). We can just move from alpha to beta to a more stable version without changing the contract. The version upgrade is currently achieved by listing the old and new version definitions in the XRD. The versions array is the construct used for listing multiple versions. The two critical Boolean attributes under each version are served and referenceable. The referenceable flag will determine whether we can define a composition implementation for the given version. Only one version can have the referenceable flag set to true. This will be the version used by any new XR create/update event. A create/update event triggered by the old API version will still use the composition from the latest version, marked as referenceable. The served flag will indicate whether the given XR API version is in use. Some teams may still use the old version to consume the API. Switching off the served flag means that the given version is no longer available for clients. It will be the last step before removing the old version from the XR.

Look at a sample XRD with three versions, alpha, beta, and v1, at https://github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/blob/main/Chapter05/Samples/XRD-Versions/xrd-multiple-version.yaml. This XRD has three versions. Version alpha will no longer be served, and beta will be served but cannot be referred for resource creation or update. The latest version, v1, will be the preferred version for any resource creation or updates.

Kubernetes CRDs support multiple API versions both with and without an API contract change. When there is an API contract change, a conversion webhook is configured by the CRD author to support conversion between the versions. Conversions are required as CR objects will be stored in the etcd with both old and new contracts. XRD, the Crossplane equivalent to CRDs, does not take this approach. A conversion webhook involves programming. Taking that route will violate the no-code agenda of Crossplane when composing APIs. It’s important to note that the Crossplane community is actively working to build a configuration-based solution to support conversion and migration between versions.

Version upgrade with breaking changes

An alternative approach supports breaking contracts by introducing a new XR API parallel. This approach uses an external naming technique and deletion policy to handle breaking changes. With this pattern, we will migrate the resources to a new XR API and remove the old API once the migration is finished in its entirety. The steps to achieve such a version upgrade are as follows:

  1. Create the v1 version of XRD. In the composition, define a standard nomenclature for naming the external resources (MRs). We should be able to reconstruct the names again in the new API. Generally, we can concatenate the XR and the composition name (<XR>+'-'+<Composition>). You can come up with a resource naming strategy that suits your environment. Maybe we can even use the namespace name to represent the product owner of the resource.
  2. Ensure that all the MRs in the composition have spec.deletionPolicy defined as Orphan.
  3. Let’s say we have a couple of consumers for the XR, and they have created a few external resources. Assume that we have a policy requirement that requires breaking changes to the API contract.
  4. To support the breaking change, delete all v1 versions of the XR. It will just delete the Crossplane references. The external resources are not deleted on account of the orphan deletion policy.
  5. Then, delete the v1 version of XRD and Composition.
  6. Finally, create the new v2 version of XRD with the same XR name. Update the composition to handle the recent breaking changes. Ensure that the new composition follows the same external resource name creation logic and maps to the new XRD version.
  7. Create the deleted XR objects again, pointing to the v2 version of the API. The new XR objects will reference the old orphaned external resources. Crossplane controllers will reconcile any attribute value change.

Note that this type of migration to a new API version must be coordinated with all the XR-consuming teams. Once the migration is completed, the old API version will no longer be available. The following figure represents the migration process:

Figure 5.6 – Version migration

Figure 5.6 – Version migration

Tip

It is always good to have a standard way of generating external resource names. In addition to version migration, a reproducible naming pattern can afford several other advantages. Using pre-provisioned resources for a shared or cached infrastructure is an example of using the standard external resource naming pattern. Migrating resources to a new Crossplane environment can be another example.

It’s recommended to go through a hands-on journey of breaking API contract, with the sample configuration provided at https://github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/XRD-Contract-Change-Breaking. Perform the following steps to go through the hands-on journey to handle breaking contract changes:

  1. First, execute xrd-v1.yaml, Composition-V1.yaml, and Claim.yaml.
  2. It will create an XRD and a Composition with the database size as optional parameter and the VM as a mandatory parameter. The Claim will provision the database wih the specified size and VM. The provisioned resource will get a standard external resource name.
  3. Note that the claim.name label in Claim.yaml is used for constructing the external resource name in the composition section. It should be unique for every XR/Claim object to generate unique external resource names.
  4. Let’s now delete the v1 version of Claim, Composition, and XRD. When we delete the v1 claim, the external resource will not be deleted because the deletion policy is configured as an orphan.
  5. Finally, apply the v2 version of Claim, Composition, and XRD. In the v2 XRD, we have broken the contract by removing the mandatory parameter, vm. The new v2 claim (Claim-migrate.yaml) will not have the vm parameter. Note that both Composition and Claim will point to the v2 version XRD.
  6. Notice that the Crossplane will reclaim the orphaned resource and reconcile the virtual machine with the new default value provided in the Composition. We can validate that by looking into the GCP console or the Claim resource description.

Refer to the following screenshot where the preceding example is tested:

Figure 5.7 – XRD breaking changes

Figure 5.7 – XRD breaking changes

Following is the code snippet relating to external resource name patching from the preceding Composition example. This must be present in both composition versions, and the name generated should be the same for both versions:

- type: FromCompositeFieldPath
      fromFieldPath: metadata.name
      toFieldPath: metadata.annotations[crossplane.io/external-name]
      transforms:
      - type: string
        string:
          fmt: "%s-gcp-mysql-cloudsqlinstance"

Note that we have used a new transform type to format the string before we patch. With this, we conclude the different ways of evolving the XR APIs. We will dive into an interesting case in the following section to build one XR composing another XR.

Nested and multi-resource XRs

Every software product depends on more than one infrastructure resource. It is essential to build single infrastructure recipes in order for the product teams to consume with a unified experience. The orchestration of infrastructure dependencies should remain abstracted. Such recipes require multiple resources to be composed into a single XR. In all the examples hitherto, we have always composed a single GCP resource inside an XR. Let’s look at an XR sample where multiple GCP resources are composed into a single XR API. The following figure represents the resources and XR APIs that we are going to work with in the example:

Figure 5.8 – Multi-resource nested XR

Figure 5.8 – Multi-resource nested XR

In addition to multiple resource provisioning in a single XR, we also have a nested XR pattern in Figure 5.8. We are composing three resources within two XRs. The first XR composes two resources, and the second XR composes the first XR and a database resource. Let’s look at the details of each XR:

  • XR 1: We will compose a Google Kubernetes Engine cluster and a Google Cloud storage resource with this XR. The idea is to provide cloud storage to hold the application logs for future analysis. Note that this XR will not have a claim name in the XRD definition. It will be of a cluster scope and a private API for the platform team. Product teams with access only to a namespace will not use this API directly. This XR will expose the region and the autopilot configuration as parameters. The region will be propagated back into both the resources and the autopilot configuration is used for the Kubernetes provisioning.
  • XR/Claim 2: The second XR will compose the MySQL database, an MR, and the first XR to create a nested API. We will patch region parameters to the MySQL MR and propagate the same into the inner XR.

All examples in this hands-on journey are available at https://github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/Nested-Multi-Resource-XR.

Let’s first create the XRD and Composition for both the XRs. Apply xrd k8s.yaml, Composition k8s.yaml, xrd Application.yaml, and Composition Application.yaml to the Crossplane cluster. You will see that the ESTABLISHED flag is True for both the XRDs. This indicates that the Crossplane has started a new controller to reconcile the established XR. The OFFERED flag will be True for the application XR and False for the Kubernetes XR. This indicates that the Crossplane has started a new controller to reconcile the established Claim only for the application XR. It is false for the Kubernetes XR because we don’t have the respective claim. Refer to the following screenshot regarding XRD creation:

Figure 5.9 – Nested XR-XRD and Composition

Figure 5.9 – Nested XR-XRD and Composition

Tip

Similar to creating an XR API with multiple resources from a single cloud provider, we can also mix and match resources from multiple clouds. We just have to add the resources concerned with respective ProviderConfig clouds.

It’s now time to create an application Claim resource. Apply Claim Application.yaml to the Crossplane cluster. You will see that a CloudSQLInstance instance, a cluster, and a bucket resource have been provisioned. Refer to the following screenshot where the resources are provisioned successfully:

Figure 5.10 – Resource provisioning

Figure 5.10 – Resource provisioning

If you would like to explore each resource in detail, use the Resource references. Execute kubectl describe application my-application -n alpha to see the details of the claim. It will refer to the XApplication XR object. If we look at the details of the XApplication object, it will hold the reference to the CloudSQLInstance MR and XGCPCluster XR. Similarly, we can go on till you reach the last MR. This is beneficial for debugging activities. Sometimes you may see that the resources are not getting ready. In those instances, explore each nested resource and refer to the events section to ascertain what is happening. An example of referring nested resources from the resource description is as follows:

Figure 5.11 – Nested resource reference example 1

Figure 5.11 – Nested resource reference example 1

The preceding screenshot represented the Application claim description referring to the XApplication XR resource. The following screenshot represents the XApplication XR description referring to the XGCPCluster XR instance and CloudSQLInstance MR:

Figure 5.12 – Nested resource reference example 2

Figure 5.12 – Nested resource reference example 2

The following is an example event that tells us that we have provided the wrong region as a parameter:

Figure 5.13 – Resource description with an error

Figure 5.13 – Resource description with an error

Important

We need to follow many more patterns when we compose multiple resources to give a unified experience for product teams. The preceding example is a simple example to start the topic. We will see more on this in the upcoming chapters.

PatchSets

If you look at the composition in the preceding example, you can see that we have used a new pattern called PatchSets. If you find yourself repeating the same patch operation again and again under each resource, then PatchSets is the way to go. Here, we define the patch operation as a static function and include it under the required resource sections. The following is an example of the patchSets function definition to patch a region:

patchSets:
  - name: region
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.region
      toFieldPath: spec.forProvider.region

We can define multiple patchSet functions. To include a specific patch set function within a given resource, use the following code snippet:

patches:
    - type: PatchSet
      patchSetName: region

We will see more nested and multi-resource XR examples in the upcoming chapters. In the following section, we will look at detailed configuration options for defining the XRD schema.

XRD detailed

While looking at Composite Resource Definition (XRD) in the previous chapter, we touched on limited configuration options required to learn the basics of XR. It’s now time to look at more detailed configuration options to build clean and robust XR APIs. A significant part of the details we will look at are about openAPIV3Schema, which is used to define the input and output of the XR API. The following are the topics we will cover in this section:

  • Naming the versions
  • The openAPIV3Schema structure
  • The additional parameter of an attribute
  • Printer columns

Let’s start with the Naming the versions section.

Naming the versions

The version name of our XRD cannot have any random string. It has a specific validation inherited from the CRDs and standard Kubernetes APIs. The string can contain only lowercase alphanumeric characters and -. Also, it must always start with an alphabetic character and end with an alphanumeric character, which means that - cannot be the start or ending character. Also, a number cannot be the starting character. Some valid versions are my-version, version-1, abc-version1, and v1. While we can have many permutations and combinations for naming a version, some standard practices are followed across CRDs. Following the same with XRDs will enable API consumers to understand the stability of the API. The version string starts with v followed by a number with these standards (v1, v2). This is then optionally followed by either alpha or beta, depending on the API’s stability. Generally, the alpha string represents the lowest stability (v5alpha), while beta is the next stability level (v3beta). If both texts are missing, the XR is ready for production use. An optional number can follow the optional alpha/beta text representing the incremental releases (v2alpha1, v2alpha2, and so on).

If you have an invalid version string provided with the XRD, you will see that the XRD will not get configured properly. The ESTABLISHED flag will not be set to True. You apply the – xrd invalid version test.yaml file from the samples folder to see what happens when you have an incorrect version number. Refer to the following screenshot:

Figure 5.14 – Invalid version XRD

Figure 5.14 – Invalid version XRD

Also, you will be able to see the following error logs in the Crossplane pod in the crossplane-system namespace:

2022-01-15T20:06:46.217Z     ERROR     crossplane.controller-runtime.manager.controller.defined/compositeresourcedefinition.apiextensions.crossplane.io     Reconciler error     {"reconciler group": "apiextensions.crossplane.io", "reconciler kind": "CompositeResourceDefinition", "name": "xbuckets.version-test.imarunrk.com", "namespace": "", "error": "cannot apply rendered composite resource CustomResourceDefinition: cannot create object: CustomResourceDefinition.apiextensions.k8s.io "xbuckets.version-test.imarunrk.com" is invalid: [spec.versions[0].name: Invalid value: "v1.0": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?'), spec.version: Invalid value: "v1.0": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')]"}

Tip

When we troubleshoot an issue with Crossplane, logs from the Crossplane pod can help. Enable debugging mode by adding an argument, --debug, to the Crossplane pod. Similarly, we can even look at the provider’s container logs.

The openAPIV3Schema structure

The specification of the XR API is defined using openAPIV3Schema. Every configuration element in the XRD under this section represents the input and output of the XR API:

versions:
  - name: v1alpha
    schema:
      openAPIV3Schema:
      # Input and output definition for the XR

Generally, we configure the openAPIV3Schema section with two objects, spec and status. The spec object represents the API input, while the status object represents the response. We can skip defining the status section in XRD if we don’t have any custom requirements. Crossplane would inject the standard status fields into the XR/Claim. Refer to the following code snippet representing the openAPIV3Schema configuration template for the XR API input and output:

openAPIV3Schema:
  type: object
  properties:
    # spec – the API input configuration
    spec:
      type: object
      properties:
        ............. configuration continues 
     # status – the API output configuration 
     status:
       type: object
       properties:
         ............. configuration continues

The schema configuration is all about a mix of - attributes, their types, and properties. An attribute type of object will hold a list of properties. For example, the root attribute openAPIV3Schema: is of the object type followed by a list of properties (spec and status). A list of properties is nothing but a list of attributes. Suppose the attribute type is primitive, such as string or integer. Such an attribute will be the end node. The object-properties recursion can continue in as much depth as we require. Refer to the following code snippet:

# The root attribute openAPIV3Schema of type object
openAPIV3Schema:
  type: object
  # spec/status - attributes (properties) of openAPIV3Schema
  properties:
    # spec – the XR input
    spec:
      type: object
      properties:
        # parameters - again an object with attributes list
        parameters:
          type: object
          properties:
            # region – string primitive  - node ends
            region:
            type: string
    # status – API output configuration
    # The exact structure of configuration as before
    # Attributes, their types, and properties
    status:
      type: object
            properties:
              zone:
                description: DB zone.
                type: string   

In the following section, we can look at a few additional valuable configuration options along with the basic openAPIV3Schema configuration.

The additional parameter of an attribute

The attribute node can configure a few other critical configurations that API developers will use daily. Following are some of the frequently used configurations:

  • Description is a string that will help us provide valuable information for the API consumers about the attribute. It can hold information about the use of the parameter, possible values we can configure, and validation requirements.
  • Required is an attribute representing the list of mandatory inputs that are required from the user for the API.
  • Default is an attribute that provides a default value if the user does not input a value.
  • Enum can configure the list of possible values for a given attribute.

In addition to these fields, there is a list of validation-related configurations including minimum, maximum, pattern, maxLength, and minLength. Refer to the following sample configuration:

spec:
  type: object
  description: API input specification 
  properties:
    parameters:
      type: object
      description: Parameter's to configure the resource
      properties:
        size:
          type: integer
          description: Disk size of the database
          default: 20
          minimum: 10
          maximum: 100
        vm:
          type: string
          description: Size of the virtual machine.
        enum:
        - small
        - medium
        - large
      required:
      - size
  required:
  - parameters

To explore more detailed possibilities, visit https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#schemaObject.

Tip

We can use the description field to announce the parameter deprecation information. This technique can be helpful in delaying breaking changes to a contract by making a mandatory field optional with a deprecation message.

Printer columns

We can use the printer columns to add what kubectl will display when we get the resource list. We should provide a name, data type, and JSON path mapping to the attribute we wish to display for each column. Optionally, we may also provide a description. Refer to the following sample configuration:

additionalPrinterColumns:
- name: Zone
  type: string
  description: 
  jsonPath: .spec.zone
- name: Age
  type: date
  jsonPath: .metadata.creationTimestamp

The printer column configuration remains parallel to the schema configuration.

This concludes our discussion of detailed XRD configuration. We have covered most of the configuration required for day-to-day work, but there are endless possibilities. It will add value by reading up on CRD at https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/.

Managing external software resources

We have always talked about managing external infrastructure resources using Crossplane from the beginning of this book. However, it does not always have to be just an infrastructure resource. We could even manage external software applications from Crossplane. For a software application to be able to work best with the Crossplane ecosystem, it must have the following qualities:

  • We should have well-defined and stable APIs to perform CRUD operations.
  • The API should have a high-fidelity design with filters to control granular application configuration.

It’s time to look at an example. Think about deploying an application in Kubernetes using Helm. Helm can package any application and provide a well-defined CRUD API to deploy, read, update, and uninstall. Above all, we can create granular control over the application configuration with parameters. We have a helm Crossplane provider already available and used extensively by the community. The idea of managing external applications from a Crossplane control plane can enable a new world of unifying application and infrastructure automation. The following section will cover the unifying aspect in more detail.

Unifying the automation

Managing external software resources with Crossplane is the crossroad for unifying infrastructure and application DevOps. We could package software and infrastructure dependencies into a single XRs. Such a complete package of applications and infrastructure introduces numerous advantages, some of which are listed here:

  • The approach will unify the tooling and skills required for application and infrastructure automation.
  • More importantly, the entire stack will enjoy the advantages of the Kubernetes operating model.
  • Integrating vendor software into an enterprise ecosystem will become quicker and more standardized. Software vendors can quickly build packages that fit into different ecosystems. Currently, software vendors must custom-build for the individual cloud provider marketplace. This approach can assist in building a universal vendor software marketplace.
  • We can easily apply the audit process to comply with any compliance standards. Previously, this would have been complicated as software and its infrastructure dependencies are spread about.

The following figure represents a unified XR API:

Figure 5.15 – Unified XR

Figure 5.15 – Unified XR

Important

In a later chapter, we can go through a hands-on journey to experience building an XR API covering both applications and infrastructure dependencies.

Summary

I hope it’s been fun to read this chapter and go through the hands-on journey. It covered different patterns that are useful in our day-to-day work when adopting Crossplane. We covered different ways to evolve our XR APIs, detailed XR configurations, how to manage application resources, and nested and multi-resource XRs. There are more patterns to be covered.

The next chapter will discuss more advanced Crossplane methods and their respective hands-on journeys.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset