diff --git a/CLAUDE.md b/CLAUDE.md index 07381ce..213c3a4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,7 +72,10 @@ export TALOSCONFIG=./talos/talosconfig - **Namespace race condition**: First `kubectl apply` of a new app often fails because namespace isn't ready. Re-apply once. - **Traefik DaemonSet updates**: Requires `updateStrategy.rollingUpdate.maxSurge: 0` because hostPort conflicts prevent surge. - **Forgejo Ingress API version**: Chart renders `extensions/v1beta1`, fixed via `ytt/ingress-fix.ytt.yaml` overlay to `networking.k8s.io/v1`. -- **ArgoCD Phase 3**: Repo not yet pushed to Forgejo, ArgoCD not yet wired. +- **ArgoCD**: Fully wired to Forgejo via App of Apps. Root Application in `default` project syncs `rendered/argocd/production/`. Deploy key provisioned automatically by `argocd-deploy-key-init` Job in forgejo namespace. + +## Container Images +- **Never use bitnami images.** Use `alpine/k8s` or plain `alpine` for utility Jobs instead. ## Secrets (not in git) - `cert-manager/letsencrypt-account-key` — ACME account key (auto-generated) diff --git a/envs/_env/argocd/secret.overlay.ytt.yaml b/envs/_env/argocd/secret.overlay.ytt.yaml deleted file mode 100644 index bcd1cb2..0000000 --- a/envs/_env/argocd/secret.overlay.ytt.yaml +++ /dev/null @@ -1,14 +0,0 @@ -#@ load("@ytt:overlay", "overlay") ---- -#@ def secret_fragment(): -kind: Secret -metadata: - labels: - argocd.argoproj.io/secret-type: cluster -#@ end - -#@overlay/match by=overlay.subset(secret_fragment()), expects="0+" ---- -stringData: - config: ARGOCD_CLUSTER_CONNECT_CONFIG - server: ARGOCD_CLUSTER_SERVER_URL diff --git a/envs/env-data.ytt.yaml b/envs/env-data.ytt.yaml index e8bb4e0..33eee95 100644 --- a/envs/env-data.ytt.yaml +++ b/envs/env-data.ytt.yaml @@ -5,5 +5,13 @@ argocd: app: prefix: app- finalizers: [] + source: + repoURL: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + destination: + server: https://kubernetes.default.svc project: prefix: env- + destination: + server: https://kubernetes.default.svc + env: + generateSecret: false diff --git a/prototypes/argocd/helm/argo-cd.yaml b/prototypes/argocd/helm/argo-cd.yaml index 439f20f..0960723 100644 --- a/prototypes/argocd/helm/argo-cd.yaml +++ b/prototypes/argocd/helm/argo-cd.yaml @@ -9,6 +9,12 @@ global: configs: params: server.insecure: true + cm: + resource.customizations.ignoreDifferences.all: | + managedFieldsManagers: + - kube-controller-manager + jsonPointers: + - /status server: ingress: diff --git a/prototypes/forgejo/ytt/admin-secret-job.ytt.yaml b/prototypes/forgejo/ytt/admin-secret-job.ytt.yaml index ceede1d..2b8a02a 100644 --- a/prototypes/forgejo/ytt/admin-secret-job.ytt.yaml +++ b/prototypes/forgejo/ytt/admin-secret-job.ytt.yaml @@ -41,6 +41,8 @@ kind: Job metadata: name: forgejo-admin-secret-init namespace: #@ ns + annotations: + argocd.argoproj.io/sync-options: Replace=true spec: ttlSecondsAfterFinished: 300 template: @@ -49,7 +51,7 @@ spec: restartPolicy: OnFailure containers: - name: init - image: bitnami/kubectl:latest + image: alpine/k8s:1.32.3 command: - sh - -c diff --git a/prototypes/forgejo/ytt/argocd-deploy-key-job.ytt.yaml b/prototypes/forgejo/ytt/argocd-deploy-key-job.ytt.yaml new file mode 100644 index 0000000..18014d6 --- /dev/null +++ b/prototypes/forgejo/ytt/argocd-deploy-key-job.ytt.yaml @@ -0,0 +1,139 @@ +#@ load("@ytt:data", "data") + +#@ ns = data.values.application.namespace + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: argocd-deploy-key-init + namespace: #@ ns + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: argocd-deploy-key-init +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "create"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: argocd-deploy-key-init +subjects: + - kind: ServiceAccount + name: argocd-deploy-key-init + namespace: #@ ns +roleRef: + kind: ClusterRole + name: argocd-deploy-key-init + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: argocd-deploy-key-init + namespace: #@ ns + annotations: + argocd.argoproj.io/sync-options: Replace=true +spec: + ttlSecondsAfterFinished: 300 + template: + spec: + serviceAccountName: argocd-deploy-key-init + restartPolicy: OnFailure + containers: + - name: init + image: alpine/k8s:1.32.3 + command: + - sh + - -c + - | + set -e + + apk add --no-cache openssh-keygen > /dev/null 2>&1 + + ARGOCD_NS="argocd" + REPO_SECRET="forgejo-repo" + REPO_URL="ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git" + FORGEJO_URL="https://git.tr1ceracop.de" + REPO_OWNER="gitea_admin" + REPO_NAME="k8s-and-chill" + + # Wait for Forgejo to be ready + echo "Waiting for Forgejo to be ready..." + for i in $(seq 1 60); do + if curl -sk "${FORGEJO_URL}/api/v1/version" >/dev/null 2>&1; then + echo "Forgejo HTTPS is ready" + break + fi + if [ "$i" -eq 60 ]; then + echo "Forgejo did not become ready in time" + exit 1 + fi + sleep 5 + done + + # Check if ArgoCD repo secret already exists + if kubectl get secret "${REPO_SECRET}" -n "${ARGOCD_NS}" >/dev/null 2>&1; then + echo "Secret ${REPO_SECRET} already exists in ${ARGOCD_NS}, skipping" + exit 0 + fi + + # Read admin credentials + ADMIN_USER=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.username}' | base64 -d) + ADMIN_PASS=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.password}' | base64 -d) + + # Generate ed25519 SSH keypair + KEYDIR=$(mktemp -d) + ssh-keygen -t ed25519 -f "${KEYDIR}/id_ed25519" -N "" -q + PRIVKEY=$(cat "${KEYDIR}/id_ed25519") + PUBKEY=$(cat "${KEYDIR}/id_ed25519.pub") + rm -rf "${KEYDIR}" + + # Register deploy key via Forgejo API + echo "Registering deploy key..." + HTTP_CODE=$(curl -sk -o /tmp/response.json -w "%{http_code}" \ + -X POST "${FORGEJO_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/keys" \ + -H "Content-Type: application/json" \ + -u "${ADMIN_USER}:${ADMIN_PASS}" \ + -d "{\"title\":\"argocd-deploy-key\",\"key\":\"${PUBKEY}\",\"read_only\":true}") + + if [ "${HTTP_CODE}" = "201" ]; then + echo "Deploy key registered successfully" + elif [ "${HTTP_CODE}" = "422" ]; then + echo "Deploy key already exists in Forgejo (422), continuing" + else + echo "Failed to register deploy key: HTTP ${HTTP_CODE}" + cat /tmp/response.json + exit 1 + fi + + # Create ArgoCD repository secret with insecure flag (skip host key verification) + cat < /dev/null 2>&1 + + ARGOCD_NS="argocd" + REPO_SECRET="forgejo-repo" + REPO_URL="ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git" + FORGEJO_URL="https://git.tr1ceracop.de" + REPO_OWNER="gitea_admin" + REPO_NAME="k8s-and-chill" + + # Wait for Forgejo to be ready + echo "Waiting for Forgejo to be ready..." + for i in $(seq 1 60); do + if curl -sk "${FORGEJO_URL}/api/v1/version" >/dev/null 2>&1; then + echo "Forgejo HTTPS is ready" + break + fi + if [ "$i" -eq 60 ]; then + echo "Forgejo did not become ready in time" + exit 1 + fi + sleep 5 + done + + # Check if ArgoCD repo secret already exists + if kubectl get secret "${REPO_SECRET}" -n "${ARGOCD_NS}" >/dev/null 2>&1; then + echo "Secret ${REPO_SECRET} already exists in ${ARGOCD_NS}, skipping" + exit 0 + fi + + # Read admin credentials + ADMIN_USER=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.username}' | base64 -d) + ADMIN_PASS=$(kubectl get secret forgejo-admin-secret -n "${NAMESPACE}" -o jsonpath='{.data.password}' | base64 -d) + + # Generate ed25519 SSH keypair + KEYDIR=$(mktemp -d) + ssh-keygen -t ed25519 -f "${KEYDIR}/id_ed25519" -N "" -q + PRIVKEY=$(cat "${KEYDIR}/id_ed25519") + PUBKEY=$(cat "${KEYDIR}/id_ed25519.pub") + rm -rf "${KEYDIR}" + + # Register deploy key via Forgejo API + echo "Registering deploy key..." + HTTP_CODE=$(curl -sk -o /tmp/response.json -w "%{http_code}" \ + -X POST "${FORGEJO_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/keys" \ + -H "Content-Type: application/json" \ + -u "${ADMIN_USER}:${ADMIN_PASS}" \ + -d "{\"title\":\"argocd-deploy-key\",\"key\":\"${PUBKEY}\",\"read_only\":true}") + + if [ "${HTTP_CODE}" = "201" ]; then + echo "Deploy key registered successfully" + elif [ "${HTTP_CODE}" = "422" ]; then + echo "Deploy key already exists in Forgejo (422), continuing" + else + echo "Failed to register deploy key: HTTP ${HTTP_CODE}" + cat /tmp/response.json + exit 1 + fi + + # Create ArgoCD repository secret with insecure flag (skip host key verification) + cat <