Have crossplane compositions be managed by ArgoCD

This commit is contained in:
Richard Robert Reitz 2024-11-22 23:53:10 +01:00
parent 714d59513c
commit bab8b3091a
4 changed files with 456 additions and 0 deletions

View file

@ -0,0 +1,23 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: crossplane-compositions
namespace: argocd
labels:
env: dev
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
syncPolicy:
automated:
selfHeal: true
syncOptions:
- CreateNamespace=true
destination:
name: in-cluster
namespace: crossplane-system
source:
path: stacks/core/crossplane-compositions
repoURL: https://gitea.cnoe.localtest.me/giteaAdmin/edfbuilder-shoot.git
targetRevision: HEAD

View file

@ -0,0 +1,6 @@
apiVersion: edfbuilder.crossplane.io/v1alpha1
kind: EDFBuilder
metadata:
name: kindcluster
spec:
repoURL: "https://github.com/argoproj/argocd-example-apps.git"

View file

@ -0,0 +1,397 @@
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: edfbuilders.edfbuilder.crossplane.io
spec:
writeConnectionSecretsToNamespace: crossplane-system
compositeTypeRef:
apiVersion: edfbuilder.crossplane.io/v1alpha1
kind: EDFBuilder
mode: Pipeline
pipeline:
- step: patch-and-transform
functionRef:
name: crossplane-contrib-function-patch-and-transform
input:
apiVersion: pt.fn.crossplane.io/v1beta1
kind: Resources
resources:
### shell provider config
- name: provider-shell
base:
apiVersion: shell.crossplane.io/v1alpha1
kind: ProviderConfig
spec:
credentials:
source: InjectedIdentity
patches:
- type: FromCompositeFieldPath
fromFieldPath: metadata.name
toFieldPath: metadata.name
readinessChecks:
- type: None
### bash-oneshot
- name: bash-oneshot
base:
apiVersion: provisioning.shell.crossplane.io/v1alpha1
kind: Bash
metadata:
name: bash-oneshot
spec:
forProvider:
script: |
# setup
DOMAIN=cnoe.localtest.me
#CLUSTER_NAME=$(openssl rand -hex 8)
CLUSTER_NAME=shoot
mkdir -p /tmp/rundir
export HOME=/tmp/rundir
cd
# get stacks folder
rm -Rf stacks &> /dev/null || true
git clone https://forgejo.edf-bootstrap.cx.fg1.ffm.osc.live/richardrobertreitz/stacks.git
# workdir for template helm values files
rm -Rf work &> /dev/null || true
cp -r stacks/kind work
rm -Rf stacks
# create namespaces
echo create namespaces
kubectl create namespace argo
kubectl create namespace argocd
kubectl create namespace gitea
kubectl create namespace ingress-nginx
# create and upload self signed certs
echo create and upload self signed certs
mkdir -p tls
if [[ ! -f tls/$DOMAIN.key || ! -f tls/$DOMAIN.crt ]]; then
openssl req -x509 -newkey rsa:4096 -keyout tls/$DOMAIN.key -out tls/$DOMAIN.crt -sha256 -days 3650 -nodes -subj "/C=AB/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=${DOMAIN}" -addext "subjectAltName=DNS:${DOMAIN},DNS:${DOMAIN}"
fi
if [[ ! -f tls/gitea.$DOMAIN.key || ! -f tls/gitea.$DOMAIN.crt ]]; then
openssl req -x509 -newkey rsa:4096 -keyout tls/gitea.$DOMAIN.key -out tls/gitea.$DOMAIN.crt -sha256 -days 3650 -nodes -subj "/C=AB/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=gitea.${DOMAIN}" -addext "subjectAltName=DNS:gitea.${DOMAIN},DNS:gitea.${DOMAIN}"
fi
kubectl create secret tls -n argocd argocd-net-tls --key tls/$DOMAIN.key --cert tls/$DOMAIN.crt
kubectl create secret tls -n gitea forgejo-net-tls --key tls/gitea.$DOMAIN.key --cert tls/gitea.$DOMAIN.crt
# add gitea certificate into argocd helm values
yq e -i ".configs.tls.certificates.\"gitea.$DOMAIN\" = load_str(\"tls/gitea.$DOMAIN.crt\")" work/stacks/core/argocd/values.yaml
# create a random giteaAdmin password
echo create giteaAdmin password
kubectl create secret generic -n gitea gitea-credential --from-literal=username=giteaAdmin "--from-literal=password=$(openssl rand -base64 16)"
# patch coredns
echo patch coredns
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns-conf-custom
namespace: kube-system
data:
custom.conf: |
# insert custom rules here
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns-conf-default
namespace: kube-system
data:
default.conf: |
# domain names resolves to ingress IP. e.g. gitea.cnoe.localtest.me becomes ingress-nginx-controller.ingress-nginx.svc.cluster.local
rewrite name exact gitea.cnoe.localtest.me ingress-nginx-controller.ingress-nginx.svc.cluster.local
rewrite name exact cnoe.localtest.me ingress-nginx-controller.ingress-nginx.svc.cluster.local
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
import ../coredns-configs/*.conf
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
EOF
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: kube-dns
name: coredns
namespace: kube-system
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kube-dns
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
k8s-app: kube-dns
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- args:
- -conf
- /etc/coredns/Corefile
image: registry.k8s.io/coredns/coredns:v1.11.1
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: 8181
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/coredns
name: config-volume
readOnly: true
- mountPath: /etc/coredns-configs
name: custom-configs
readOnly: true
dnsPolicy: Default
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: coredns
serviceAccountName: coredns
terminationGracePeriodSeconds: 30
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
volumes:
- configMap:
defaultMode: 420
items:
- key: Corefile
path: Corefile
name: coredns
name: config-volume
- name: custom-configs
projected:
sources:
- configMap:
name: coredns-conf-custom
- configMap:
name: coredns-conf-default
EOF
kubectl rollout restart deployment/coredns -n kube-system
kubectl rollout status deployment/coredns -n kube-system --timeout=90000s
# install ingress-nginx
echo install ingress-nginx
rm -Rf ingress-nginx &> /dev/null
git clone https://github.com/kubernetes/ingress-nginx
cd ingress-nginx
git checkout helm-chart-4.11.3
cd ..
helm dependency update ./ingress-nginx/charts/ingress-nginx/
helm dependency build ./ingress-nginx/charts/ingress-nginx/
helm install -n ingress-nginx -f work/stacks/core/ingress-nginx/values.yaml ingress-nginx ./ingress-nginx/charts/ingress-nginx
rm -Rf ingress-nginx
# wait for ingress
sleep 5
kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=90000s
# install argocd
echo install argocd
rm -Rf argo-helm &> /dev/null
git clone https://github.com/argoproj/argo-helm
cd argo-helm
git checkout argo-cd-7.7.5
cd ..
helm dependency update ./argo-helm/charts/argo-cd/
helm dependency build ./argo-helm/charts/argo-cd/
helm install -n argocd -f work/stacks/core/argocd/values.yaml argocd ./argo-helm/charts/argo-cd
rm -Rf argo-helm
# install forgejo
echo install forgejo
rm -Rf forgejo-helm &> /dev/null
git clone https://code.forgejo.org/forgejo-helm/forgejo-helm.git
cd forgejo-helm
git checkout v10.1.1
cd ..
helm dependency build ./forgejo-helm/
helm install -n gitea -f work/stacks/core/forgejo/values.yaml forgejo ./forgejo-helm
rm -Rf forgejo-helm
# wait for argocd
echo wait for argocd
HOST=$(kubectl get ingress -n argocd argocd-server -o yaml | yq -r .status.loadBalancer.ingress\[0\].hostname)
while [[ "$HOST" == "null" ]]
do
sleep 1
HOST=$(kubectl get ingress -n argocd argocd-server -o yaml | yq -r .status.loadBalancer.ingress\[0\].hostname)
done
# wait for forgejo
echo wait for forgejo
HOST=$(kubectl get ingress -n gitea forgejo -o yaml | yq -r .status.loadBalancer.ingress\[0\].hostname)
while [[ "$HOST" == "null" ]]
do
sleep 1
HOST=$(kubectl get ingress -n gitea forgejo -o yaml | yq -r .status.loadBalancer.ingress\[0\].hostname)
done
until curl -k --output /dev/null --silent --head --fail https://gitea.${DOMAIN}; do
sleep 1
done
# create the target git repository
GIT_USERNAME=giteaAdmin
GIT_PASSWORD=$(kubectl get secret -n gitea gitea-credential --output jsonpath="{.data.password}" | base64 --decode)
GIT_TOKEN=$(curl -sk -H "Content-Type: application/json" -d '{"name":"idpbuilder","scopes":["read:user","write:user","read:repository","write:repository","read:admin","write:admin"]}' -u $GIT_USERNAME:$GIT_PASSWORD https://gitea.$DOMAIN/api/v1/users/$GIT_USERNAME/tokens | jq -r .sha1)
curl -ks -X POST -H 'Content-Type: application/json' -d "{\"name\":\"edfbuilder-$CLUSTER_NAME\"}" "https://gitea.$DOMAIN/api/v1/user/repos?token=$GIT_TOKEN"
# create and apply a forgejo runner token
FORGEJO_RUNNER_TOKEN="$(curl -ks -H 'Content-Type: application/json' "https://gitea.$DOMAIN/api/v1/admin/runners/registration-token?token=$GIT_TOKEN" | jq -r .token)"
kubectl create secret generic -n gitea forgejo-runner-token "--from-literal=token=$FORGEJO_RUNNER_TOKEN"
echo repo created
git config --global user.email "bot@undefined.com"
git config --global user.name "Bot"
# upload templated deployment to git repository
cd work/
git init
git checkout -b main
git add -A
git commit -m "initial commit"
git remote add origin https://$GIT_USERNAME:${GIT_TOKEN}@gitea.$DOMAIN/giteaAdmin/edfbuilder-$CLUSTER_NAME.git
GIT_SSL_NO_VERIFY=true git push -u origin main
cd ..
# upload forgejo docker registry credentials for use in argo-workflows
cat <<EOF | kubectl create secret generic -n argo my-docker-secret --from-file=config.json=/dev/stdin
{
"auths": {
"https://gitea.cnoe.localtest.me": {
"auth": "$(echo -n giteaAdmin:$GIT_PASSWORD | base64)"
}
}
}
EOF
argocd login --grpc-web-root-path argocd cnoe.localtest.me --insecure --username admin --password "$(kubectl get secret -n argocd argocd-initial-admin-secret --output jsonpath="{.data.password}" | base64 --decode)"
# let argocd takeover core stack and install other stacks
kubectl apply -f work/registry/core.yaml
kubectl apply -f work/registry/ref-implementation.yaml
# remove templated deployment
rm -Rf work
# core packages installed, print passwords
echo done
echo
echo -n argocd admin password:\ ; kubectl get secret -n argocd argocd-initial-admin-secret --output jsonpath="{.data.password}" | base64 --decode; echo
echo https://$DOMAIN/argocd
echo
echo forgejo $GIT_USERNAME password: $GIT_PASSWORD token: $GIT_TOKEN
echo https://gitea.$DOMAIN
echo
echo execute ./get-passwords.sh to see all available passwords
echo
# done
exit 0
patches:
- type: FromCompositeFieldPath
fromFieldPath: metadata.name
toFieldPath: spec.providerConfigRef.name

View file

@ -0,0 +1,30 @@
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: edfbuilders.edfbuilder.crossplane.io
spec:
connectionSecretKeys:
- kubeconfig
group: edfbuilder.crossplane.io
names:
kind: EDFBuilder
listKind: EDFBuilderList
plural: edfbuilders
singular: edfbuilders
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
description: A EDFBuilder is a composite resource that represents a K8S Cluster with edfbuilder Installed
type: object
properties:
spec:
type: object
properties:
repoURL:
type: string
description: URL to ArgoCD stack of stacks repo
required:
- repoURL