diff --git a/examples/aks-cluster/README.md b/examples/aks-cluster/README.md new file mode 100644 index 0000000..efbb56b --- /dev/null +++ b/examples/aks-cluster/README.md @@ -0,0 +1,32 @@ +# AKS Cluster Example + +This example demonstrates how to provision an AKS cluster using Crossplane v1. + +## Prerequisites + +- Kubernetes cluster (v1.31+) +- Helm v3.x + +## Install Crossplane v1 via Helm + +1. **Add the Crossplane Helm repository:** + ```sh + helm repo add crossplane-stable https://charts.crossplane.io/stable + helm repo update + ``` + +2. **Install Crossplane v1:** + ```sh + helm install crossplane --namespace crossplane-system --create-namespace crossplane-stable/crossplane --version 1.x.x + ``` + Replace `1.x.x` with the desired Crossplane v1 release. + +3. **Verify installation:** + ```sh + kubectl get pods -n crossplane-system + ``` + +4. **Apply the manifests:** + ```sh + ./install.sh + ``` \ No newline at end of file diff --git a/examples/aks-cluster/cluster-function-pythonic.yaml b/examples/aks-cluster/cluster-function-pythonic.yaml new file mode 100644 index 0000000..ccd98af --- /dev/null +++ b/examples/aks-cluster/cluster-function-pythonic.yaml @@ -0,0 +1,63 @@ +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: function-pythonic +spec: + package: ghcr.io/fortra/function-pythonic:v0.0.8 + runtimeConfigRef: + name: function-pythonic +--- +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: function-pythonic +spec: + deploymentTemplate: + spec: + selector: {} + template: + spec: + containers: + - name: package-runtime + args: + - --debug + - --packages + - --packages-namespace=crossplane-system + serviceAccountName: function-pythonic + serviceAccountTemplate: + metadata: + name: function-pythonic +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: crossplane-system + name: function-pythonic +rules: +- apiGroups: + - '' + resources: + - configmaps + verbs: + - list + - watch + - patch +- apiGroups: + - '' + resources: + - events + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + namespace: crossplane-system + name: function-pythonic +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: function-pythonic +subjects: +- kind: ServiceAccount + name: function-pythonic diff --git a/examples/aks-cluster/composition.yaml b/examples/aks-cluster/composition.yaml new file mode 100644 index 0000000..8573204 --- /dev/null +++ b/examples/aks-cluster/composition.yaml @@ -0,0 +1,24 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: aks-cluster +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1 + kind: AzureKubernetesCluster + mode: Pipeline + pipeline: + - step: create-resourcegroup + functionRef: + name: function-pythonic + input: + apiVersion: pythonic.fn.fortra.com/v1alpha1 + kind: Composite + composite: resourcegroup.ResourceGroupComposite + - step: create-aks-cluster + functionRef: + name: function-pythonic + input: + apiVersion: pythonic.fn.fortra.com/v1alpha1 + kind: Composite + composite: kubernetescluster.KubernetesClusterComposite diff --git a/examples/aks-cluster/definition.yaml b/examples/aks-cluster/definition.yaml new file mode 100644 index 0000000..380b6bf --- /dev/null +++ b/examples/aks-cluster/definition.yaml @@ -0,0 +1,43 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: azurekubernetesclusters.example.crossplane.io +spec: + group: example.crossplane.io + names: + kind: AzureKubernetesCluster + plural: azurekubernetesclusters + claimNames: + kind: AzureKubernetesClusterClaim + plural: azurekubernetesclusterclaims + versions: + - name: v1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + defaultNodePool: + type: object + properties: + autoScaling: + type: object + properties: + enabled: + type: boolean + minCount: + type: integer + maxCount: + type: integer + nodeCount: + type: integer + vmSize: + type: string + resourceGroupName: + type: string + location: + type: string diff --git a/examples/aks-cluster/functions.yaml b/examples/aks-cluster/functions.yaml new file mode 100644 index 0000000..064538f --- /dev/null +++ b/examples/aks-cluster/functions.yaml @@ -0,0 +1,10 @@ +apiVersion: pkg.crossplane.io/v1beta1 +kind: Function +metadata: + name: function-pythonic + annotations: + render.crossplane.io/runtime: Development +spec: + package: ghcr.io/fortra/function-pythonic:v0.0.8 + runtimeConfigRef: + name: function-pythonic diff --git a/examples/aks-cluster/install.sh b/examples/aks-cluster/install.sh new file mode 100755 index 0000000..47e2446 --- /dev/null +++ b/examples/aks-cluster/install.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# Install function-pythonic and azure providers +kubectl apply -f cluster-function-pythonic.yaml +kubectl apply -f providers.yaml + +# Install AKS Crossplane XRDs and Compositions +kubectl apply -f definition.yaml +# Wait for the generated CRDs to be created +sleep 5 + +# Create the AKS cluster +kubectl apply -k . diff --git a/examples/aks-cluster/kubernetescluster.py b/examples/aks-cluster/kubernetescluster.py new file mode 100644 index 0000000..66f5f9c --- /dev/null +++ b/examples/aks-cluster/kubernetescluster.py @@ -0,0 +1,29 @@ +from crossplane.pythonic import BaseComposite + +class KubernetesClusterComposite(BaseComposite): + def compose(self): + labels = {'example.crossplane.io/AzureKubernetesCluster': self.metadata.name} + self.logger.info(f"Composing AKS cluster {self.metadata.name}") + + aks = self.resources.KubernetesCluster( + 'containerservice.azure.upbound.io/v1beta2', 'KubernetesCluster' + ) + aks.metadata.name = self.metadata.name + aks.metadata.labels = labels + + aks.spec.forProvider.location = self.spec.location + aks.spec.forProvider.resourceGroupNameRef.name = self.spec.resourceGroupName + aks.spec.forProvider.identity.type = 'SystemAssigned' + + if self.spec.defaultNodePool.autoScaling.enabled: + aks.spec.forProvider.defaultNodePool.autoScalingEnabled = True + aks.spec.forProvider.defaultNodePool.minCount = self.spec.defaultNodePool.autoScaling.minCount + aks.spec.forProvider.defaultNodePool.maxCount = self.spec.defaultNodePool.autoScaling.maxCount + else: + aks.spec.forProvider.defaultNodePool.nodeCount = self.spec.nodeCount + + aks.spec.forProvider.defaultNodePool.name = 'systempool1' + aks.spec.forProvider.defaultNodePool.vmSize = self.spec.vmSize + aks.spec.forProvider.dnsPrefix = self.metadata.name + + self.status.aks = aks.status.endpoint diff --git a/examples/aks-cluster/kustomization.yaml b/examples/aks-cluster/kustomization.yaml new file mode 100644 index 0000000..7edc650 --- /dev/null +++ b/examples/aks-cluster/kustomization.yaml @@ -0,0 +1,20 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +generatorOptions: + disableNameSuffixHash: true + +configMapGenerator: + +- namespace: crossplane-system + name: pythonic-aks + options: + labels: + function-pythonic.package: '' + files: + - kubernetescluster.py + - resourcegroup.py + +resources: +- composition.yaml +- xr.yaml diff --git a/examples/aks-cluster/providers.yaml b/examples/aks-cluster/providers.yaml new file mode 100644 index 0000000..82106a3 --- /dev/null +++ b/examples/aks-cluster/providers.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: upbound-provider-family-azure +spec: + package: xpkg.upbound.io/upbound/provider-family-azure:v1 +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: upbound-provider-azure-containerservice +spec: + package: xpkg.upbound.io/upbound/provider-azure-containerservice:v1 diff --git a/examples/aks-cluster/render.sh b/examples/aks-cluster/render.sh new file mode 100755 index 0000000..2a42755 --- /dev/null +++ b/examples/aks-cluster/render.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +# In one terminal +# PYTHONPATH=. function-pythonic --insecure --debug + +# In another terminal: + +cd $(dirname $(realpath $0)) +exec crossplane render xr.yaml composition.yaml functions.yaml diff --git a/examples/aks-cluster/resourcegroup.py b/examples/aks-cluster/resourcegroup.py new file mode 100644 index 0000000..91664ae --- /dev/null +++ b/examples/aks-cluster/resourcegroup.py @@ -0,0 +1,14 @@ +from crossplane.pythonic import BaseComposite + +class ResourceGroupComposite(BaseComposite): + def compose(self): + labels = {'example.crossplane.io/AzureKubernetesCluster': self.metadata.name} + self.logger.info(f"Composing Azure ResourceGroup {self.spec.resourceGroupName}") + + rg = self.resources.ResourceGroup( + 'azure.upbound.io/v1beta1', 'ResourceGroup' + ) + rg.metadata.name = self.spec.resourceGroupName + rg.metadata.labels = labels + + rg.spec.forProvider.location = self.spec.location diff --git a/examples/aks-cluster/xr.yaml b/examples/aks-cluster/xr.yaml new file mode 100644 index 0000000..a567ddf --- /dev/null +++ b/examples/aks-cluster/xr.yaml @@ -0,0 +1,15 @@ +apiVersion: example.crossplane.io/v1 +kind: AzureKubernetesCluster +metadata: + namespace: my-aks-namespace + name: my-aks-cluster +spec: + defaultNodePool: + autoScaling: + enabled: False + minCount: 1 + maxCount: 3 + nodeCount: 1 + vmSize: Standard_B2ms + resourceGroupName: my-resource-group + location: westeurope