feat: APPS_DIR environment layer (Plan 14 step 2)
new-service/delete-service read and write $GITOPS_DIR/$APPS_DIR (default apps-homelab) instead of the literal apps/; FORGEJO_URL moves to git.holtlabs.xyz. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5b4987a371
commit
11c68770e1
5 changed files with 23 additions and 19 deletions
|
|
@ -13,7 +13,7 @@ delete-service orders-web --yes && delete-service orders --yes
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://git.lab.holtlabs.xyz/nholtware/platform.git ~/Code/forgejo.local/nholtware/platform
|
git clone https://git.holtlabs.xyz/nholtware/platform.git ~/Code/forgejo.local/nholtware/platform
|
||||||
cd ~/Code/forgejo.local/nholtware/platform
|
cd ~/Code/forgejo.local/nholtware/platform
|
||||||
cp platform.env.example platform.env # gitignored; defaults match the homelab
|
cp platform.env.example platform.env # gitignored; defaults match the homelab
|
||||||
export PATH="$PWD/bin:$PATH" # not installed globally
|
export PATH="$PWD/bin:$PATH" # not installed globally
|
||||||
|
|
@ -40,13 +40,13 @@ new-service NAME --kind backend|frontend [--with postgres,redis] [--api BACKEND]
|
||||||
| 5 | render gitops `templates/<kind>` (+ `postgres`, `redis`; `__APP__`, `__API__`, `__DOMAIN__`) → `manifests/NAME`, `kustomize build` lint, commit, push | dir exists |
|
| 5 | render gitops `templates/<kind>` (+ `postgres`, `redis`; `__APP__`, `__API__`, `__DOMAIN__`) → `manifests/NAME`, `kustomize build` lint, commit, push | dir exists |
|
||||||
| 6 | clone repo to `$CODE_DIR/NAME`, push empty commit `chore: kick ci` | CI already deployed, or a kick is running/succeeded |
|
| 6 | clone repo to `$CODE_DIR/NAME`, push empty commit `chore: kick ci` | CI already deployed, or a kick is running/succeeded |
|
||||||
| 7 | wait for CI's `gitops-bump` to replace `newTag: latest` (fails fast if the CI run fails) | tag already bumped |
|
| 7 | wait for CI's `gitops-bump` to replace `newTag: latest` (fails fast if the CI run fails) | tag already bumped |
|
||||||
| 8 | write `apps/NAME.yaml`, commit, push, refresh ArgoCD `root` | file exists |
|
| 8 | write `$APPS_DIR/NAME.yaml` (`apps-homelab` by default), commit, push, refresh ArgoCD `root` | file exists |
|
||||||
| 9 | `--wait`: ArgoCD app `Synced/Healthy`, then `https://NAME.$INGRESS_DOMAIN/readyz` (backend) or `/healthz` + `/api/readyz` (frontend) → 200 | no `--wait` |
|
| 9 | `--wait`: ArgoCD app `Synced/Healthy`, then `https://NAME.$INGRESS_DOMAIN/readyz` (backend) or `/healthz` + `/api/readyz` (frontend) → 200 | no `--wait` |
|
||||||
|
|
||||||
Then it prints the service, ArgoCD, Kibana (pre-filtered on `service:"NAME"`)
|
Then it prints the service, ArgoCD, Kibana (pre-filtered on `service:"NAME"`)
|
||||||
and Unleash URLs, and the elapsed time.
|
and Unleash URLs, and the elapsed time.
|
||||||
|
|
||||||
**Why `apps/NAME.yaml` comes last:** ArgoCD only sees the service once CI has
|
**Why `$APPS_DIR/NAME.yaml` (`apps-homelab` by default) comes last:** ArgoCD only sees the service once CI has
|
||||||
pushed a real image. Registering it earlier made the backend's migrate Job sit
|
pushed a real image. Registering it earlier made the backend's migrate Job sit
|
||||||
in `ImagePullBackOff` on `:latest` and hold the sync forever (Plan 04 caveat).
|
in `ImagePullBackOff` on `:latest` and hold the sync forever (Plan 04 caveat).
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ re-kicked automatically.
|
||||||
delete-service NAME [--yes] [--dry-run] [--timeout SECONDS]
|
delete-service NAME [--yes] [--dry-run] [--timeout SECONDS]
|
||||||
```
|
```
|
||||||
|
|
||||||
Removes `apps/NAME.yaml` + `manifests/NAME` from gitops (the Application's
|
Removes `$APPS_DIR/NAME.yaml` (`apps-homelab` by default) + `manifests/NAME` from gitops (the Application's
|
||||||
`resources-finalizer` makes ArgoCD delete its workloads when `root` prunes it),
|
`resources-finalizer` makes ArgoCD delete its workloads when `root` prunes it),
|
||||||
waits for the Application to go, deletes the namespace if it is the service's
|
waits for the Application to go, deletes the namespace if it is the service's
|
||||||
own (CNPG PVCs go with it), deletes every version of the container package and
|
own (CNPG PVCs go with it), deletes every version of the container package and
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ OWN_NS=0
|
||||||
|
|
||||||
# Refuse to pull a namespace out from under a frontend co-located with us.
|
# Refuse to pull a namespace out from under a frontend co-located with us.
|
||||||
dependents=()
|
dependents=()
|
||||||
for f in "$GITOPS_DIR"/apps/*.yaml; do
|
for f in "$GITOPS_DIR/$APPS_DIR"/*.yaml; do
|
||||||
other=$(basename "$f" .yaml)
|
other=$(basename "$f" .yaml)
|
||||||
[[ $other != "$NAME" && $(app_namespace "$other") == "$NAME" ]] && dependents+=("$other")
|
[[ $other != "$NAME" && $(app_namespace "$other") == "$NAME" ]] && dependents+=("$other")
|
||||||
done
|
done
|
||||||
|
|
@ -54,7 +54,7 @@ done
|
||||||
APP_DIR=$CODE_DIR/$NAME
|
APP_DIR=$CODE_DIR/$NAME
|
||||||
cat >&2 <<EOF
|
cat >&2 <<EOF
|
||||||
This permanently deletes, for service '$NAME':
|
This permanently deletes, for service '$NAME':
|
||||||
- gitops apps/$NAME.yaml and manifests/$NAME/ (ArgoCD prunes the workloads)
|
- gitops $APPS_DIR/$NAME.yaml and manifests/$NAME/ (ArgoCD prunes the workloads)
|
||||||
- $([[ $OWN_NS == 1 ]] && echo "namespace $NAME, including its Postgres data (PVCs)" || echo "nothing else in shared namespace $NS (left in place)")
|
- $([[ $OWN_NS == 1 ]] && echo "namespace $NAME, including its Postgres data (PVCs)" || echo "nothing else in shared namespace $NS (left in place)")
|
||||||
- Forgejo repo $FORGEJO_OWNER/$NAME and its container package versions
|
- Forgejo repo $FORGEJO_OWNER/$NAME and its container package versions
|
||||||
- local clone $APP_DIR
|
- local clone $APP_DIR
|
||||||
|
|
@ -72,7 +72,7 @@ if kubectl get application -n argocd "$NAME" >/dev/null 2>&1; then
|
||||||
-p '{"metadata":{"finalizers":["resources-finalizer.argocd.argoproj.io"]}}' >/dev/null
|
-p '{"metadata":{"finalizers":["resources-finalizer.argocd.argoproj.io"]}}' >/dev/null
|
||||||
fi
|
fi
|
||||||
paths=()
|
paths=()
|
||||||
[[ -f $GITOPS_DIR/apps/$NAME.yaml ]] && paths+=("apps/$NAME.yaml")
|
[[ -f $GITOPS_DIR/$APPS_DIR/$NAME.yaml ]] && paths+=("$APPS_DIR/$NAME.yaml")
|
||||||
[[ -d $GITOPS_DIR/manifests/$NAME ]] && paths+=("manifests/$NAME")
|
[[ -d $GITOPS_DIR/manifests/$NAME ]] && paths+=("manifests/$NAME")
|
||||||
if ((${#paths[@]})); then
|
if ((${#paths[@]})); then
|
||||||
if is_dry; then
|
if is_dry; then
|
||||||
|
|
|
||||||
|
|
@ -88,13 +88,13 @@ gitops_sync
|
||||||
((!PUBLIC)) || cf_require
|
((!PUBLIC)) || cf_require
|
||||||
|
|
||||||
if [[ -n $API_APP ]]; then
|
if [[ -n $API_APP ]]; then
|
||||||
[[ -f $GITOPS_DIR/apps/$API_APP.yaml && -d $GITOPS_DIR/manifests/$API_APP ]] ||
|
[[ -f $GITOPS_DIR/$APPS_DIR/$API_APP.yaml && -d $GITOPS_DIR/manifests/$API_APP ]] ||
|
||||||
die "--api $API_APP: no such service in gitops (create it first)"
|
die "--api $API_APP: no such service in gitops (create it first)"
|
||||||
[[ $(app_namespace "$API_APP") == "$API_APP" ]] ||
|
[[ $(app_namespace "$API_APP") == "$API_APP" ]] ||
|
||||||
die "--api $API_APP: not a backend (its namespace is '$(app_namespace "$API_APP")')"
|
die "--api $API_APP: not a backend (its namespace is '$(app_namespace "$API_APP")')"
|
||||||
fi
|
fi
|
||||||
if [[ -f $GITOPS_DIR/apps/$NAME.yaml && $(app_namespace "$NAME") != "$NS" ]]; then
|
if [[ -f $GITOPS_DIR/$APPS_DIR/$NAME.yaml && $(app_namespace "$NAME") != "$NS" ]]; then
|
||||||
die "gitops apps/$NAME.yaml exists with namespace '$(app_namespace "$NAME")', expected '$NS'"
|
die "gitops $APPS_DIR/$NAME.yaml exists with namespace '$(app_namespace "$NAME")', expected '$NS'"
|
||||||
fi
|
fi
|
||||||
ok "validated $NAME (kind=$KIND ns=$NS postgres=$WITH_PG redis=$WITH_REDIS api=${API_APP:--})"
|
ok "validated $NAME (kind=$KIND ns=$NS postgres=$WITH_PG redis=$WITH_REDIS api=${API_APP:--})"
|
||||||
|
|
||||||
|
|
@ -148,7 +148,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ------------------------------------------------------------ 5. gitops manifests
|
# ------------------------------------------------------------ 5. gitops manifests
|
||||||
# apps/<name>.yaml is written only after CI's first image exists (step 7), so
|
# $APPS_DIR/<name>.yaml is written only after CI's first image exists (step 7), so
|
||||||
# ArgoCD never syncs `newTag: latest` before there is an image to pull (the
|
# ArgoCD never syncs `newTag: latest` before there is an image to pull (the
|
||||||
# migrate-Job ImagePullBackOff caveat from Plan 04).
|
# migrate-Job ImagePullBackOff caveat from Plan 04).
|
||||||
# public_block DIR: keep (markers only) or strip the `# __PUBLIC_BEGIN__..END__` host rule.
|
# public_block DIR: keep (markers only) or strip the `# __PUBLIC_BEGIN__..END__` host rule.
|
||||||
|
|
@ -281,17 +281,17 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ------------------------------------------------------------ 8. register with ArgoCD
|
# ------------------------------------------------------------ 8. register with ArgoCD
|
||||||
if [[ -f $GITOPS_DIR/apps/$NAME.yaml ]]; then
|
if [[ -f $GITOPS_DIR/$APPS_DIR/$NAME.yaml ]]; then
|
||||||
ok "gitops apps/$NAME.yaml exists"
|
ok "gitops $APPS_DIR/$NAME.yaml exists"
|
||||||
else
|
else
|
||||||
is_dry || git_auth -C "$GITOPS_DIR" pull -q --ff-only origin main
|
is_dry || git_auth -C "$GITOPS_DIR" pull -q --ff-only origin main
|
||||||
if is_dry; then
|
if is_dry; then
|
||||||
dry "write $GITOPS_DIR/apps/$NAME.yaml (namespace $NS)"
|
dry "write $GITOPS_DIR/$APPS_DIR/$NAME.yaml (namespace $NS)"
|
||||||
else
|
else
|
||||||
sed "s/__APP__/$NAME/g; s/__NS__/$NS/g" "$GITOPS_DIR/templates/$KIND/application.yaml.tmpl" \
|
sed "s/__APP__/$NAME/g; s/__NS__/$NS/g" "$GITOPS_DIR/templates/$KIND/application.yaml.tmpl" \
|
||||||
>"$GITOPS_DIR/apps/$NAME.yaml"
|
>"$GITOPS_DIR/$APPS_DIR/$NAME.yaml"
|
||||||
fi
|
fi
|
||||||
gitops_commit_push "feat($NAME): register ArgoCD application" "apps/$NAME.yaml"
|
gitops_commit_push "feat($NAME): register ArgoCD application" "$APPS_DIR/$NAME.yaml"
|
||||||
fi
|
fi
|
||||||
# Don't wait for ArgoCD's 3-min poll.
|
# Don't wait for ArgoCD's 3-min poll.
|
||||||
argocd_refresh root
|
argocd_refresh root
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,9 @@ load_config() {
|
||||||
for v in curl jq git kubectl; do need_cmd "$v"; done
|
for v in curl jq git kubectl; do need_cmd "$v"; done
|
||||||
|
|
||||||
export KUBECONFIG
|
export KUBECONFIG
|
||||||
|
# Environment layer (Plan 14): the gitops dir this service's Application goes in.
|
||||||
|
APPS_DIR=${APPS_DIR:-apps-homelab}
|
||||||
|
[[ -d $GITOPS_DIR/$APPS_DIR ]] || die "platform.env: APPS_DIR=$APPS_DIR does not exist in $GITOPS_DIR"
|
||||||
# Public access is optional config; only --public / delete-service use it.
|
# Public access is optional config; only --public / delete-service use it.
|
||||||
PUBLIC_DOMAIN=${PUBLIC_DOMAIN:-} CF_ZONE_ID=${CF_ZONE_ID:-} CF_TUNNEL_ID=${CF_TUNNEL_ID:-}
|
PUBLIC_DOMAIN=${PUBLIC_DOMAIN:-} CF_ZONE_ID=${CF_ZONE_ID:-} CF_TUNNEL_ID=${CF_TUNNEL_ID:-}
|
||||||
CLOUDFLARE_TOKEN_FILE=${CLOUDFLARE_TOKEN_FILE:-}
|
CLOUDFLARE_TOKEN_FILE=${CLOUDFLARE_TOKEN_FILE:-}
|
||||||
|
|
@ -249,9 +252,9 @@ gitops_commit_push() {
|
||||||
die "gitops push failed after 5 attempts"
|
die "gitops push failed after 5 attempts"
|
||||||
}
|
}
|
||||||
|
|
||||||
# app_namespace NAME: destination namespace from gitops apps/NAME.yaml (empty if absent).
|
# app_namespace NAME: destination namespace from gitops $APPS_DIR/NAME.yaml (empty if absent).
|
||||||
app_namespace() {
|
app_namespace() {
|
||||||
local f=$GITOPS_DIR/apps/$1.yaml
|
local f=$GITOPS_DIR/$APPS_DIR/$1.yaml
|
||||||
[[ -f $f ]] || return 0
|
[[ -f $f ]] || return 0
|
||||||
awk '/^ destination:/{d=1; next} d && /^ namespace:/{print $2; exit}' "$f"
|
awk '/^ destination:/{d=1; next} d && /^ namespace:/{print $2; exit}' "$f"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# Every file below must exist or the scripts refuse to run.
|
# Every file below must exist or the scripts refuse to run.
|
||||||
HOMELAB_SECRETS=$HOME/Code/github.com/nholtware/homelab/.secrets
|
HOMELAB_SECRETS=$HOME/Code/github.com/nholtware/homelab/.secrets
|
||||||
|
|
||||||
FORGEJO_URL=https://git.lab.holtlabs.xyz
|
FORGEJO_URL=https://git.holtlabs.xyz
|
||||||
FORGEJO_OWNER=nholtware
|
FORGEJO_OWNER=nholtware
|
||||||
FORGEJO_ADMIN_TOKEN_FILE=$HOMELAB_SECRETS/forgejo-admin-token # repo create/delete, Actions secrets, git push
|
FORGEJO_ADMIN_TOKEN_FILE=$HOMELAB_SECRETS/forgejo-admin-token # repo create/delete, Actions secrets, git push
|
||||||
REGISTRY_PUSH_TOKEN_FILE=$HOMELAB_SECRETS/registry-push-token # becomes REGISTRY_TOKEN; also lists/deletes packages
|
REGISTRY_PUSH_TOKEN_FILE=$HOMELAB_SECRETS/registry-push-token # becomes REGISTRY_TOKEN; also lists/deletes packages
|
||||||
|
|
@ -11,6 +11,7 @@ GITOPS_TOKEN_FILE=$HOMELAB_SECRETS/ci-bot-gitops-token # becomes GITOPS
|
||||||
GITOPS_DIR=$HOME/Code/forgejo.local/nholtware/gitops # clean clone of nholtware/gitops on main
|
GITOPS_DIR=$HOME/Code/forgejo.local/nholtware/gitops # clean clone of nholtware/gitops on main
|
||||||
CODE_DIR=$HOME/Code/forgejo.local/nholtware # new service repos are cloned here
|
CODE_DIR=$HOME/Code/forgejo.local/nholtware # new service repos are cloned here
|
||||||
KUBECONFIG=$HOME/.kube/homelab.yaml
|
KUBECONFIG=$HOME/.kube/homelab.yaml
|
||||||
|
APPS_DIR=apps-homelab # gitops dir for this environment's Applications (Plan 14; apps-cloud for the cloud env)
|
||||||
|
|
||||||
INGRESS_DOMAIN=lab.holtlabs.xyz # services at https://<name>.$INGRESS_DOMAIN (renders __DOMAIN__)
|
INGRESS_DOMAIN=lab.holtlabs.xyz # services at https://<name>.$INGRESS_DOMAIN (renders __DOMAIN__)
|
||||||
INGRESS_IP=192.168.1.83 # health checks use curl --resolve, no DNS needed (TLS still verified)
|
INGRESS_IP=192.168.1.83 # health checks use curl --resolve, no DNS needed (TLS still verified)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue