In recent blogs, the exploration of how Crossplane works and its potential to create self-service models by enabling custom APIs has been discussed. The previous example, however, was quite basic.
Self-Service : Building and Enabling APIs with Crossplane and AWS
Javier Sepúlveda ・ Oct 8
This time, the focus shifts to a more advanced concept in Crossplane: functions.
One limitation of Crossplane is its inability to use loops or conditionals when creating resources. A previous experiment highlighted this challenge, requiring resources to be defined individually. This approach often resulted in manifests that were difficult to manage and prone to complexity, resembling spaghetti code.
For context, here is an example using only providers: My first steps with Crossplane.
To address these issues, functions provide a solution that improves code reusability, adheres to the DRY (Don't Repeat Yourself) principle, and reduces the likelihood of human error. Functions enable the use of complex logic within compositions, making them more robust and easier to maintain.
By incorporating functions like these, Crossplane compositions can overcome many limitations, becoming more flexible and capable of handling intricate scenarios.
Deploying A network Layer with crossplane and KCL.
Requirements
- Kubernetes cluster (Minikube)
- Helm version v3.13.1 or later
- Crossplane
- programmatic access AWS
Step 1.
This blog assumes that you have all requirements completed and assume that you have installed the providers and the ProviderConfig used in the last blog.
If you have any doubt, please check the this post.
Step 2.
Install kcl function.
kubectl apply -f functions.yaml
Step 3.
Defined the API using the CompositeResourceDefinition.yaml or XRD.
In this case, the API only receives: vpccidrBlock, region, and projectName through claim.yaml.
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: multiazwordpressinfra.segoja7.example
spec:
group: segoja7.example
names:
kind: MultiAzWordpressInfra
plural: multiazwordpressinfra
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
projectName:
type: string
vpccidrBlock:
type: string
default: "172.16.0.0/16"
region:
type: string
default: "us-east-1"
required:
- region
Step 4.
Composition.
Crossplane has two modes of composition: pipeline and resouces(Deprecated) check the documentation.
Following best practices, the compositions have the pipeline mode, and through it, a sequence of steps is defined.
This is the most important lines for the compositions using kcl function.
pipeline:
- step: normal
functionRef:
name: function-kcl
input:
apiVersion: krm.kcl.dev/v1alpha1
kind: KCLRun
metadata:
name: basic
spec:
source: |
# Removed for Brevity
- step: ready
functionRef:
name: function-auto-ready
How do KCL functions help users?
As previously mentioned, when needing to deploy complex infrastructure, Crossplane doesn't directly allow this. Functions become necessary, and with KCL functions, users can solve and improve the code for deploying that infrastructure.
For example, this is subnets resource with out crossplane. have a declaration for create 4 subnets type private.
---
apiVersion: ec2.aws.upbound.io/v1beta1
kind: Subnet
metadata:
name: database-private-subnet-az1
labels:
dbsubnet1: private1
spec:
forProvider:
availabilityZone: us-east-1a
cidrBlock: 172.16.1.0/24
mapPublicIpOnLaunch: false
region: us-east-1
tags:
Name: database-private-subnet-az1
ManagedBy: crossplane
vpcIdSelector:
matchLabels:
vpc: wordpress
providerConfigRef:
name: segoja7
---
apiVersion: ec2.aws.upbound.io/v1beta1
kind: Subnet
metadata:
name: database-private-subnet-az2
labels:
dbsubnet2: private2
spec:
forProvider:
availabilityZone: us-east-1b
cidrBlock: 172.16.2.0/24
mapPublicIpOnLaunch: false
region: us-east-1
tags:
Name: database-private-subnet-az2
ManagedBy: crossplane
vpcIdSelector:
matchLabels:
vpc: wordpress
providerConfigRef:
name: segoja7
---
apiVersion: ec2.aws.upbound.io/v1beta1
kind: Subnet
metadata:
name: app-private-subnet-az1
labels:
appsubnet1: private1
spec:
forProvider:
availabilityZone: us-east-1a
cidrBlock: 172.16.3.0/24
mapPublicIpOnLaunch: false
region: us-east-1
tags:
Name: app-private-subnet-az1
ManagedBy: crossplane
vpcIdSelector:
matchLabels:
vpc: wordpress
providerConfigRef:
name: segoja7
---
apiVersion: ec2.aws.upbound.io/v1beta1
kind: Subnet
metadata:
name: app-private-subnet-az2
labels:
appsubnet2: private2
spec:
forProvider:
availabilityZone: us-east-1b
cidrBlock: 172.16.4.0/24
mapPublicIpOnLaunch: false
region: us-east-1
tags:
Name: app-private-subnet-az2
ManagedBy: crossplane
vpcIdSelector:
matchLabels:
vpc: wordpress
providerConfigRef:
name: segoja7
And this is an example using kcl-function.
- Defining an map of objects for iterate about them.
database_subnet_configs = [
{"name": "data-private-subnet-az1", "cidr": "172.16.1.0/24", "zone": "us-east-1a", "type": "private"},
{"name": "data-private-subnet-az2", "cidr": "172.16.2.0/24", "zone": "us-east-1b", "type": "private"},
{"name": "app-private-subnet-az1", "cidr": "172.16.3.0/24", "zone": "us-east-1a", "type": "private"},
{"name": "app-private-subnet-az2", "cidr": "172.16.4.0/24", "zone": "us-east-1b", "type": "private"},
{"name": "public-subnet-az1", "cidr": "172.16.5.0/24", "zone": "us-east-1a", "type": "public"},
{"name": "public-subnet-az2", "cidr": "172.16.6.0/24", "zone": "us-east-1b", "type": "public"}
]
- creating subnets using an for.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: multiazwordpressinfra-composition
spec:
compositeTypeRef:
apiVersion: segoja7.example/v1alpha1
kind: MultiAzWordpressInfra
mode: Pipeline
pipeline:
- step: normal
functionRef:
name: function-kcl
input:
apiVersion: krm.kcl.dev/v1alpha1
kind: KCLRun
metadata:
name: basic
spec:
source: |
# Removed for Brevity
_resources += [
{
apiVersion = "ec2.aws.upbound.io/v1beta1"
kind = "Subnet"
metadata.name = subnet_config.name
metadata.labels = {
type = subnet_config.type
}
spec.forProvider = {
region = region
vpcIdSelector.matchControllerRef = True
availabilityZone = subnet_config.zone
cidrBlock = subnet_config.cidr
mapPublicIpOnLaunch = True if subnet_config.type == "public" else False
tags = {
"app" = "wordpress"
"Name" = metadata.name + "-" + projectName
"Type" = subnet_config.type
}
}
spec.providerConfigRef.name = providerConfigName
}for subnet_config in database_subnet_configs
]
# Removed for Brevity
items = _resources
- step: ready
functionRef:
name: function-auto-ready
For this blog the compositions only allow the resources creation of network layer in AWS.
Check all code in this repo.
segoja7 / crossplane_compositions_kcl
This is an example using compositions with kcl
Step 5.
The claim is the trigger to deploy the resources defined in the compositions.
apiVersion: segoja7.example/v1alpha1
kind: MultiAzWordpressInfra
metadata:
name: wordpress-claim
namespace: default
spec:
parameters:
projectName: wordpress-team1s
In this claim, the only parameter for the end user is the project name, which allows the abstraction to be on the platform team side and not the product team side.
Step 6.
Verify the Resources
Please check the following help links, which offer valuable information to improve your understanding of the tool and make the most of its features.
Useful links:
Conclusion: Crossplane, along with KCL, provides a powerful solution for managing cloud resources more flexibly and efficiently. By enabling the creation of reusable compositions and integrating complex logic, it improves maintainability and reduces errors. Additionally, by abstracting technical details, it eases the work of platform teams, optimizing the experience for end users.
Thanks to the Crossplane and KCL-Functions community for the support, they helped me with doubts and questions to make this post.
Thanks for reading this post, let me know if you have any question or comment.
Top comments (0)