Crossplane: Share data between resources within the same Composite

crossplane kubernetes composite status

6 min read | by Jordi Prats

Following up on the previous crossplane example on Composition: creating a SecurityGroup and a SecurityGroupRule using a Composition we are now going to push information from one of the objects into the Composition and then push it back to the other resource:

The composistion is going to create a SecurityGroup and push it's ID up to the Composite's status. Once the ID is on the Composition, this will push this ID into the SecurityGroupRule to set the SecurityGroup's ID to which we want to create the rule

To achieve this, first we will have to tell the CompositeResourceDefinition that we are going to have the status.sgId field that will hold a string:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: sgsallowoutgoing.pet2cattle.com
spec:
  group: pet2cattle.com
  names:
    kind: SGAllowOutgoing
    plural: sgsallowoutgoing
  versions:
  - name: v1alpha1
    served: true
    referenceable: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  region:
                    type: string
                required:
                - region
            required:
            - parameters
          status:
            type: object
            properties:
              sgId:
                description: SecurityGroup ID
                type: string

Once we have this sort of variable available, we are going to use the patches attribute to copy from the resource one of it's status fields into it. To do so we are going to use a ToCompositeFieldPath patch to copy it from status.atProvider.id (that's the SecurityGroup resource) to the status.sgId (that's the Composite resource we are building):

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: sgsallowoutgoing
  labels:
    crossplane.io/xrd: sgsallowoutgoing.pet2cattle.com
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:
    apiVersion: pet2cattle.com/v1alpha1
    kind: SGAllowOutgoing
  resources:
  - name: sg
    base:
      apiVersion: ec2.aws.jet.crossplane.io/v1alpha2
      kind: SecurityGroup
      spec:
        forProvider:
          description: 'xplane SG test'
          vpcId: 'vpc-1234abcd'
        providerConfigRef:
          name: 'aws-jetprovider-config'
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: metadata.name
      toFieldPath: spec.forProvider.name
    - type: FromCompositeFieldPath
      fromFieldPath: metadata.name
      toFieldPath: metadata.annotations.crossplane.io/external-name
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.region
      toFieldPath: spec.forProvider.region
    - type: ToCompositeFieldPath
      fromFieldPath: status.atProvider.id
      toFieldPath: status.sgId
(...)

With this we are going to be able to retrieve the SecurityGroup's ID from the SGAllowOutgoing resource (the Composition) but we want is to be able to define to set it into the SecurityGroupRule. To do so we can use the FromCompositeFieldPath patch to push it into it:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: sgsallowoutgoing
  labels:
    crossplane.io/xrd: sgsallowoutgoing.pet2cattle.com
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:
    apiVersion: pet2cattle.com/v1alpha1
    kind: SGAllowOutgoing
  resources:
(...)
  - name: sg-egress
    base:
      apiVersion: ec2.aws.jet.crossplane.io/v1alpha2
      kind: SecurityGroupRule
      spec:
        forProvider:
          fromPort: 0
          toPort: 0
          protocol: "-1"
          type: 'egress'
          cidrBlocks:
            - '0.0.0.0/0'
        providerConfigRef:
          name: 'aws-jetprovider-config'
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.region
      toFieldPath: spec.forProvider.region
    - type: FromCompositeFieldPath
      fromFieldPath: status.sgId
      toFieldPath: spec.forProvider.securityGroupId

To sum it up, the changes we made to the Composition are the following:

We have removed:

  • The need to adding labels to it's resources
  • The use of a selector to identify to which SG we need to set the rule

We have added:

  • A patch to copy the SecurityGroup ID up to the Composite
  • A patch to copy from the Composite to the SecurityGroupRule resource

An actual patch would look like this:

--- composition/composition.yaml  2022-03-14 23:48:19.246227739 +0100
+++ ../crossplane-composition-publish-status/composition.yaml 2022-03-15 18:19:47.375130779 +0100
@@ -28,11 +28,11 @@
       fromFieldPath: metadata.name
       toFieldPath: metadata.annotations.crossplane.io/external-name
     - type: FromCompositeFieldPath
-      fromFieldPath: metadata.name
-      toFieldPath: metadata.labels.sgname
-    - type: FromCompositeFieldPath
       fromFieldPath: spec.parameters.region
       toFieldPath: spec.forProvider.region
+    - type: ToCompositeFieldPath
+      fromFieldPath: status.atProvider.id
+      toFieldPath: status.sgId
   - name: sg-egress
     base:
       apiVersion: ec2.aws.jet.crossplane.io/v1alpha2
@@ -52,5 +52,5 @@
       fromFieldPath: spec.parameters.region
       toFieldPath: spec.forProvider.region
     - type: FromCompositeFieldPath
-      fromFieldPath: metadata.name
-      toFieldPath: spec.forProvider.securityGroupIdSelector.matchLabels.sgname
\ No newline at end of file
+      fromFieldPath: status.sgId
+      toFieldPath: spec.forProvider.securityGroupId
\ No newline at end of file

If we create one instance of this composition:

$ kubectl apply -f instances/sgone.yaml 
sgallowoutgoing.pet2cattle.com/sgone created

We can check how the SecurityGroup ID is now part of it's status:

$ kubectl describe sgallowoutgoing.pet2cattle.com/sgone
Name:         sgone
Namespace:    
Labels:       crossplane.io/composite=sgone
Annotations:  <none>
API Version:  pet2cattle.com/v1alpha1
Kind:         SGAllowOutgoing
Metadata:
(...)
Spec:
  Composition Ref:
    Name:                     sgsallowoutgoing
  Composition Update Policy:  Automatic
  Parameters:
    Region:  us-west-2
  Resource Refs:
    API Version:  ec2.aws.jet.crossplane.io/v1alpha2
    Kind:         SecurityGroup
    Name:         sgone-5bvfc
    API Version:  ec2.aws.jet.crossplane.io/v1alpha2
    Kind:         SecurityGroupRule
    Name:         sgone-grd7m
  Write Connection Secret To Ref:
    Name:       203e831b-a64d-4d5a-9757-6f17a0c32940
    Namespace:  crossplane-system
Status:
  Conditions:
    Last Transition Time:  2022-03-15T23:26:00Z
    Reason:                Creating
    Status:                False
    Type:                  Ready
  Connection Details:
    Last Published Time:  2022-03-15T23:26:00Z
  Sg Id:                  sg-3c426074e66289c02
Events:
  Type    Reason                   Age                From                                                             Message
  ----    ------                   ----               ----                                                             -------
  Normal  PublishConnectionSecret  40s                defined/compositeresourcedefinition.apiextensions.crossplane.io  Successfully published connection details
  Normal  SelectComposition        10s (x7 over 40s)  defined/compositeresourcedefinition.apiextensions.crossplane.io  Successfully selected composition
  Normal  ComposeResources         10s (x7 over 40s)  defined/compositeresourcedefinition.apiextensions.crossplane.io  Successfully composed resources

If we check the SecurityGroupRule resource we will see how it was not possible to create the resource at first, since the SecurityGroup ID wasn't available yet. However, as soon as this ID propagated up to the Composite and then down to the SecurityGroupRule it could reconcile the resource by creating the rule on it:

$ kubectl describe SecurityGroupRule sgone-grd7m
Name:         sgone-grd7m
Namespace:    
Labels:       crossplane.io/claim-name=
              crossplane.io/claim-namespace=
              crossplane.io/composite=sgone
Annotations:  crossplane.io/composition-resource-name: sg-egress
              crossplane.io/external-create-pending: 2022-03-15T23:26:36Z
              crossplane.io/external-create-succeeded: 2022-03-15T23:26:42Z
              crossplane.io/external-name: sgrule-1606513619
              terrajet.crossplane.io/provider-meta: {"schema_version":"2"}
API Version:  ec2.aws.jet.crossplane.io/v1alpha2
Kind:         SecurityGroupRule
Metadata:
(...)
Spec:
  Deletion Policy:  Delete
  For Provider:
    Cidr Blocks:
      0.0.0.0/0
    From Port:          0
    Protocol:           -1
    Region:             us-west-2
    Security Group Id:  sg-3c426074e66289c02
    To Port:            0
    Type:               egress
  Provider Config Ref:
    Name:  aws-jetprovider-config
Status:
  At Provider:
    Id:  sgrule-1606513619
  Conditions:
    Last Transition Time:  2022-03-15T23:26:42Z
    Reason:                ReconcileSuccess
    Status:                True
    Type:                  Synced
    Last Transition Time:  2022-03-15T23:26:46Z
    Reason:                Available
    Status:                True
    Type:                  Ready
    Last Transition Time:  2022-03-15T23:26:46Z
    Reason:                Finished
    Status:                True
    Type:                  AsyncOperation
Events:
  Type     Reason                         Age                 From                                                                Message
  ----     ------                         ----                ----                                                                -------
  Warning  CannotObserveExternalResource  27s (x12 over 55s)  managed/ec2.aws.jet.crossplane.io/v1alpha2, kind=securitygrouprule  cannot run refresh: refresh failed: Missing required argument: The argument "security_group_id" is required, but no definition was found.: File name: main.tf.json
  Normal   CreatedExternalResource        17s                 managed/ec2.aws.jet.crossplane.io/v1alpha2, kind=securitygrouprule  Successfully requested creation of external resource

All the YAML definitions on this blog post have been uploaded the the repository pet2cattle/crossplane-composition-publish-status on GitHub


Posted on 22/03/2022