Arnab Saha - Engineering Leader
Posts

Crossplane Composition Dependency & Debugging Complex Compositions 2023

July 4, 2023

5 min read

crossplanekubernetesiacplatform-engineering

So, this post is about covering these two topics:

  • Creating dependencies between resources in a composition
  • Debugging complex or nested compositions when they aren't working

Pre-requisites

Playground

Here's the repository we will be using to go through the above topics.

Structure of the repository:

.
├── claims          # xplane claims for nested compositions
├── compositions    # xplane compositions defining a host-cluster i.e. vpc-subnets, eks, services
└── README.md

We have the following composition:

├── eks        # composition defining the eks cluster and nodegroup
├── service    # composition defining k8s manifest via k8s provider
└── xcluster   # composition defining dependency between eks & service

Creating dependencies between resources in a composition

While using crossplane native providers to provision infra resources (e.g. k8s cluster), there can be a need to bootstrap some application flows. For example: store the k8s cluster kubeconfig to secrets manager OR install a helm application on the cluster once the cluster is ready. In most cases for real-world application scenarios you likely would end up in a scenario as such.

In our example we will:

1. Install the VPC composition and the claim

kubectl apply -f compositions/vpc-subnets/
kubectl apply -f claims/vpc-subnets-claim.yaml

2. Check the claim status and verify on AWS console

NAME                                        READY   SYNCED   AGE
vpcsubnet.network.glide.io/xplane-glide-vpc-subnets   True    True     45d

We will need the subnet IDs in the upcoming section.

3. Apply the other compositions

Once we have the claim in ready state, we will apply the other compositions that we intend to create dependency with. In our example we create EKS cluster & Service composition and create dependency with an XCluster composition:

kubectl apply -f compositions/eks
kubectl apply -f compositions/services
kubectl apply -f compositions/xcluster

4. Update the subnet IDs in the claim definition

subnetIds:   # Update your private subnet ids
  - "subnet-xx"
  - "subnet-xx"
  - "subnet-xx"

5. Apply the claim

kubectl apply -f claims/xcluster-claim.yaml

This will now create both the EKS & service composition via this intermediary xcluster composition and claim.

The Trick

The trick is to create a dependency between two composites by combining them in a third composite and use patching with policy.fromFieldPath: Required to block creation of the second composite until some information from the first composite is available. Generally, use a status field that is not available until the composite is Ready and use that to trigger creation of the second composite.

Debugging complex or nested compositions when they aren't working

While writing complex compositions with multiple managed objects and dependencies between other compositions, we are bound to face situations when our claims are not ready.

Example:

kubectl apply -f claims/xcluster-claim.yaml

After applying the dependency claim we notice that the claim itself isn't ready.

In such cases, we can start debugging via the claim and the nested objects in the following way:

kubectl describe cls.supercluster.glide.io/managed-cluster

Events can often help us get into the direction needed to further debug the issue:

Events:
  Type     Reason                     Age   Message
  ----     ------                     ----  -------
  Warning  ConfigureCompositeResource 19m   cannot apply composite resource...
  Normal   BindCompositeResource      4m9s  Composite resource is not yet ready
  Normal   ConfigureCompositeResource 3m9s  Successfully applied composite resource

Get the kind and name of the composition

kubectl get cls.supercluster.glide.io/managed-cluster \
  -o=jsonpath='{.spec.resourceRef}{" "}{.spec.resourceRefs}' | jq

Output:

{
  "apiVersion": "supercluster.glide.io/v1alpha1",
  "kind": "XCls",
  "name": "managed-cluster-srhb7"
}

Describe the nested kind

kubectl describe XCls managed-cluster-srhb7
Events:
  Type     Reason            Age   Message
  ----     ------            ----  -------
  Warning  ComposeResources  24m   cannot compose resources: cannot render...
  Normal   ComposeResources  24m   Composed resource "add-cluster-to-loft" is not yet ready
  Normal   ComposeResources  23m   Successfully composed resources
  Normal   ComposeResources  23m   Composed resource "eks-cluster" is not yet ready

Drill down further

kubectl get XCls managed-cluster-srhb7 \
  -o=jsonpath='{.spec.resourceRef}{" "}{.spec.resourceRefs}' | jq

Output:

[
  {
    "apiVersion": "cluster.glide.io/v1alpha1",
    "kind": "XEks",
    "name": "managed-cluster-srhb7"
  },
  {
    "apiVersion": "supercluster.glide.io/v1alpha1",
    "kind": "XHost",
    "name": "managed-cluster-srhb7-4fv5g"
  }
]

We can now see this composition is defined of two other compositions, which we can further drill down in a similar fashion.

Example with XEks kind

kubectl describe XEks managed-cluster-srhb7
Events:
  Type    Reason            Age   Message
  ----    ------            ----  -------
  Normal  ComposeResources  28m   Composed resource "eks-addons" is not yet ready
  Normal  ComposeResources  28m   Successfully composed resources
  Normal  ComposeResources  28m   Composed resource "eks-cluster" is not yet ready
  Normal  ComposeResources  28m   Composed resource "eks-cluster-key" is not yet ready

These events help to understand issues with our composition. Similar approach can be taken for even more complex or nested compositions.

Conclusion

Hope this information helps you on your journey with platform engineering and Crossplane!