Let Argo CD manage its own deployment
This commit is contained in:
@@ -10,6 +10,8 @@ container images and then commits the selected image tag here; it never receives
|
||||
hostname, album selection, resources, and encrypted API credential.
|
||||
- `apps/metallb/` pins the upstream native MetalLB installation and owns this
|
||||
cluster's L2 address pool.
|
||||
- `apps/argocd/` wraps the pinned upstream Argo CD chart with this cluster's
|
||||
ingress, Authentik OIDC, RBAC, and secret-reference configuration.
|
||||
- `bootstrap/` contains Argo CD projects and Applications, including the
|
||||
upstream Sealed Secrets controller.
|
||||
- `scripts/update-immich-sync-image.sh` is the single image-promotion interface used by CI.
|
||||
@@ -29,6 +31,19 @@ shape; `apps/immichframe/values.yaml` owns only this cluster's choices.
|
||||
|
||||
The cluster already runs Argo CD in the `argocd` namespace and exposes its UI at <https://cd.brunner.ninja>.
|
||||
|
||||
Argo CD manages its own chart after bootstrap. If the controller does not
|
||||
exist, recover it first from the same canonical chart dependency and values,
|
||||
then recreate the AppProjects and Applications:
|
||||
|
||||
```sh
|
||||
./bootstrap/install-argocd.sh
|
||||
./bootstrap/install.sh
|
||||
```
|
||||
|
||||
The recovery installer preserves `argocd-secret`, `argocd-oidc`,
|
||||
`argocd-notifications-secret`, and `argocd-redis`. Back up or provision those
|
||||
Secrets separately; none of their values belongs in this public repository.
|
||||
|
||||
1. Push this repository so Argo CD can read its public HTTPS URL.
|
||||
2. Configure the `immich-sync` repository's cross-repository token as described below.
|
||||
3. Review the rendered workload:
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Chart.lock
|
||||
charts/
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v2
|
||||
name: argocd
|
||||
description: Self-managed Argo CD deployment for the homelab cluster
|
||||
type: application
|
||||
version: 0.1.0
|
||||
dependencies:
|
||||
- name: argo-cd
|
||||
version: 10.10.5
|
||||
repository: https://argoproj.github.io/argo-helm
|
||||
@@ -0,0 +1,40 @@
|
||||
argo-cd:
|
||||
global:
|
||||
domain: cd.brunner.ninja
|
||||
|
||||
crds:
|
||||
install: true
|
||||
|
||||
configs:
|
||||
cm:
|
||||
oidc.config: |
|
||||
name: Authentik
|
||||
issuer: https://auth.brunner.ninja/application/o/argocd/
|
||||
clientID: argocd
|
||||
clientSecret: $argocd-oidc:client-secret
|
||||
requestedScopes: ["openid", "profile", "email", "groups"]
|
||||
requestedIDTokenClaims:
|
||||
groups:
|
||||
essential: true
|
||||
rbac:
|
||||
policy.csv: |
|
||||
g, authentik Admins, role:admin
|
||||
policy.default: ''
|
||||
scopes: '[groups]'
|
||||
secret:
|
||||
createSecret: false
|
||||
params:
|
||||
server.insecure: true
|
||||
|
||||
notifications:
|
||||
secret:
|
||||
create: false
|
||||
|
||||
redisSecretInit:
|
||||
enabled: false
|
||||
|
||||
server:
|
||||
ingress:
|
||||
enabled: true
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
@@ -0,0 +1,39 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AppProject
|
||||
metadata:
|
||||
name: argocd
|
||||
namespace: argocd
|
||||
spec:
|
||||
description: Argo CD self-management
|
||||
sourceRepos:
|
||||
- https://code.brunner.ninja/feedc0de/homelab-gitops.git
|
||||
- https://argoproj.github.io/argo-helm
|
||||
destinations:
|
||||
- namespace: argocd
|
||||
server: https://kubernetes.default.svc
|
||||
clusterResourceWhitelist:
|
||||
- group: apiextensions.k8s.io
|
||||
kind: CustomResourceDefinition
|
||||
- group: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
- group: rbac.authorization.k8s.io
|
||||
kind: ClusterRoleBinding
|
||||
namespaceResourceWhitelist:
|
||||
- group: ""
|
||||
kind: ConfigMap
|
||||
- group: ""
|
||||
kind: Service
|
||||
- group: ""
|
||||
kind: ServiceAccount
|
||||
- group: apps
|
||||
kind: Deployment
|
||||
- group: apps
|
||||
kind: StatefulSet
|
||||
- group: networking.k8s.io
|
||||
kind: Ingress
|
||||
- group: networking.k8s.io
|
||||
kind: NetworkPolicy
|
||||
- group: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
- group: rbac.authorization.k8s.io
|
||||
kind: RoleBinding
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: argocd
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: argocd
|
||||
source:
|
||||
repoURL: https://code.brunner.ninja/feedc0de/homelab-gitops.git
|
||||
targetRevision: main
|
||||
path: apps/argocd
|
||||
helm:
|
||||
releaseName: argocd
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: argocd
|
||||
syncPolicy:
|
||||
automated:
|
||||
enabled: true
|
||||
prune: false
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- ServerSideApply=true
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
expected_context=kubernetes-admin@kubernetes
|
||||
current_context=$(kubectl config current-context)
|
||||
if [ "${current_context}" != "${expected_context}" ]; then
|
||||
echo "Refusing recovery through unexpected context: ${current_context}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
repository_dir=$(dirname -- "${script_dir}")
|
||||
app_dir="${repository_dir}/apps/argocd"
|
||||
|
||||
for command in helm kubectl openssl base64 ruby; do
|
||||
if ! command -v "${command}" >/dev/null 2>&1; then
|
||||
echo "Required command not found: ${command}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
kubectl create namespace argocd --dry-run=client --output=yaml \
|
||||
| kubectl apply --filename=-
|
||||
|
||||
if ! kubectl --namespace argocd get secret argocd-secret >/dev/null 2>&1; then
|
||||
kubectl --namespace argocd create secret generic argocd-secret
|
||||
fi
|
||||
if [ -z "$(kubectl --namespace argocd get secret argocd-secret --output='jsonpath={.data.server\.secretkey}')" ]; then
|
||||
secret_key=$(openssl rand -hex 32)
|
||||
secret_key_base64=$(printf '%s' "${secret_key}" | base64 | tr -d '\n')
|
||||
secret_patch=$(mktemp)
|
||||
chmod 600 "${secret_patch}"
|
||||
printf '{"data":{"server.secretkey":"%s"}}\n' "${secret_key_base64}" > "${secret_patch}"
|
||||
kubectl --namespace argocd patch secret argocd-secret \
|
||||
--type=merge --patch-file="${secret_patch}" >/dev/null
|
||||
rm -f "${secret_patch}"
|
||||
unset secret_key secret_key_base64
|
||||
fi
|
||||
kubectl --namespace argocd label secret argocd-secret \
|
||||
app.kubernetes.io/name=argocd-secret \
|
||||
app.kubernetes.io/part-of=argocd --overwrite >/dev/null
|
||||
|
||||
if ! kubectl --namespace argocd get secret argocd-notifications-secret >/dev/null 2>&1; then
|
||||
kubectl --namespace argocd create secret generic argocd-notifications-secret
|
||||
fi
|
||||
if ! kubectl --namespace argocd get secret argocd-redis >/dev/null 2>&1; then
|
||||
redis_password=$(openssl rand -hex 32)
|
||||
kubectl --namespace argocd create secret generic argocd-redis \
|
||||
--from-literal="auth=${redis_password}"
|
||||
unset redis_password
|
||||
fi
|
||||
if ! kubectl --namespace argocd get secret argocd-oidc >/dev/null 2>&1; then
|
||||
echo "Missing argocd/argocd-oidc; restore or provision it before recovery." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
helm dependency update "${app_dir}"
|
||||
set -- "${app_dir}"/charts/argo-cd-*.tgz
|
||||
if [ "$#" -ne 1 ] || [ ! -f "$1" ]; then
|
||||
echo "Expected exactly one downloaded argo-cd chart archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
chart_archive=$1
|
||||
|
||||
child_values=$(mktemp)
|
||||
trap 'rm -f "${child_values}"' EXIT HUP INT TERM
|
||||
ruby -ryaml -e 'puts YAML.dump(YAML.load_file(ARGV.fetch(0)).fetch("argo-cd"))' \
|
||||
"${app_dir}/values.yaml" > "${child_values}"
|
||||
|
||||
helm upgrade --install argocd "${chart_archive}" \
|
||||
--namespace argocd \
|
||||
--values "${child_values}" \
|
||||
--wait \
|
||||
--timeout 10m
|
||||
@@ -35,3 +35,9 @@ kubectl --namespace argocd wait application/metallb \
|
||||
kubectl --namespace argocd wait application/metallb \
|
||||
--for=jsonpath='{.status.health.status}'=Healthy \
|
||||
--timeout=5m
|
||||
kubectl --namespace argocd wait application/argocd \
|
||||
--for=jsonpath='{.status.sync.status}'=Synced \
|
||||
--timeout=10m
|
||||
kubectl --namespace argocd wait application/argocd \
|
||||
--for=jsonpath='{.status.health.status}'=Healthy \
|
||||
--timeout=10m
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- argocd-project.yaml
|
||||
- argocd.yaml
|
||||
- project.yaml
|
||||
- platform-project.yaml
|
||||
- sealed-secrets.yaml
|
||||
|
||||
Reference in New Issue
Block a user