refactor: reorganize GitOps control plane
This commit is contained in:
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 --context <kube-context>" >&2
|
||||
}
|
||||
|
||||
if [[ "${1:-}" != "--context" || -z "${2:-}" || -n "${3:-}" ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expected_context="$2"
|
||||
|
||||
for cmd in curl kubectl sha256sum; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo "$cmd is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
current_context="$(kubectl config current-context)"
|
||||
if [[ "$current_context" != "$expected_context" ]]; then
|
||||
echo "Refusing bootstrap: current context is ${current_context}, expected ${expected_context}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "${REPO_ROOT}/bootstrap/argocd/version.env"
|
||||
|
||||
manifest="$(mktemp)"
|
||||
cleanup() {
|
||||
rm -f "$manifest"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
curl -fsSL \
|
||||
"https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml" \
|
||||
-o "$manifest"
|
||||
printf '%s %s\n' "$ARGOCD_INSTALL_SHA256" "$manifest" | sha256sum -c -
|
||||
|
||||
kubectl --context "$expected_context" create namespace argocd --dry-run=client -o yaml |
|
||||
kubectl --context "$expected_context" apply -f -
|
||||
kubectl --context "$expected_context" apply \
|
||||
--server-side \
|
||||
--force-conflicts \
|
||||
-n argocd \
|
||||
-f "$manifest"
|
||||
kubectl --context "$expected_context" -n argocd \
|
||||
rollout status deployment/argocd-server --timeout=300s
|
||||
kubectl --context "$expected_context" -n argocd \
|
||||
rollout status deployment/argocd-repo-server --timeout=300s
|
||||
kubectl --context "$expected_context" apply \
|
||||
-f "${REPO_ROOT}/bootstrap/argocd/root-application.yaml"
|
||||
|
||||
echo "Argo CD ${ARGOCD_VERSION} and the dev-k3s root Application are installed."
|
||||
Executable
+138
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
for cmd in bash git helm jq kubectl rg terraform; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo "$cmd is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
forbidden_files="$(
|
||||
git ls-files |
|
||||
rg '(^|/)(\\.terraform|\\.terraform-state|\\.local)(/|$)|(^|/)terraform\\.tfstate($|\\.)|\\.tfplan$' ||
|
||||
true
|
||||
)"
|
||||
if [[ -n "$forbidden_files" ]]; then
|
||||
echo "Generated or state files are tracked:" >&2
|
||||
echo "$forbidden_files" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while IFS= read -r script; do
|
||||
bash -n "$script"
|
||||
done < <(rg --files -g '*.sh')
|
||||
|
||||
while IFS= read -r script; do
|
||||
if [[ ! -x "$script" ]]; then
|
||||
echo "Shell entry point is not executable: ${script}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done < <(rg --files hack -g '*.sh')
|
||||
|
||||
jq empty platform/auth-system/base/files/keycloak/project-auth-realm.json
|
||||
|
||||
overlays=(
|
||||
clusters/dev-k3s
|
||||
clusters/dev-k3s/manifests/api-server
|
||||
clusters/dev-k3s/manifests/auth-server
|
||||
clusters/dev-k3s/manifests/auth-system
|
||||
clusters/dev-k3s/manifests/vault
|
||||
)
|
||||
for overlay in "${overlays[@]}"; do
|
||||
kubectl kustomize "$overlay" >/dev/null
|
||||
done
|
||||
|
||||
sealed_secrets_render="$(
|
||||
helm template sealed-secrets sealed-secrets \
|
||||
--repo https://bitnami.github.io/sealed-secrets \
|
||||
--version 2.17.9 \
|
||||
--namespace kube-system \
|
||||
--set fullnameOverride=sealed-secrets-controller \
|
||||
--set image.repository=bitnami/sealed-secrets-controller \
|
||||
--set-string image.tag=0.33.1@sha256:e7fad65c2d2f47e48d9ca17408ed56961bfa6a6dd74ccd4a1a214664156534bc
|
||||
)"
|
||||
if [[ "$sealed_secrets_render" != *'image: docker.io/bitnami/sealed-secrets-controller:0.33.1@sha256:e7fad65c2d2f47e48d9ca17408ed56961bfa6a6dd74ccd4a1a214664156534bc'* ]]; then
|
||||
echo "Sealed Secrets image digest was not rendered by the Helm chart." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
vault_injector_render="$(
|
||||
helm template vault-agent-injector vault \
|
||||
--repo https://helm.releases.hashicorp.com \
|
||||
--version 0.32.0 \
|
||||
--namespace vault \
|
||||
--set server.enabled=false \
|
||||
--set injector.enabled=true \
|
||||
--set global.externalVaultAddr=http://vault.vault.svc.cluster.local:8200 \
|
||||
--set global.tlsDisable=true \
|
||||
--set injector.image.repository=hashicorp/vault-k8s \
|
||||
--set-string injector.image.tag=1.7.2@sha256:ae3d307658b72a1cf35dab9bdf92c995d45cdc7183af0516857714b5bd0ba84d \
|
||||
--set injector.agentImage.repository=hashicorp/vault \
|
||||
--set-string injector.agentImage.tag=1.18.5@sha256:750bb37c1638fa194ab37053a81618c61bb0491ddec6fccac87c07a8e6cd8166
|
||||
)"
|
||||
if [[ "$vault_injector_render" != *'image: "hashicorp/vault-k8s:1.7.2@sha256:ae3d307658b72a1cf35dab9bdf92c995d45cdc7183af0516857714b5bd0ba84d"'* ||
|
||||
"$vault_injector_render" != *'value: "hashicorp/vault:1.18.5@sha256:750bb37c1638fa194ab37053a81618c61bb0491ddec6fccac87c07a8e6cd8166"'* ]]; then
|
||||
echo "Vault injector or Agent image digest was not rendered by the Helm chart." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rendered_images="$(
|
||||
{
|
||||
kubectl kustomize clusters/dev-k3s/manifests/auth-system
|
||||
kubectl kustomize clusters/dev-k3s/manifests/vault
|
||||
} | rg '^[[:space:]]+image: (hashicorp/vault|postgres|quay\\.io/keycloak)'
|
||||
)"
|
||||
if printf '%s\n' "$rendered_images" | rg -v '@sha256:[a-f0-9]{64}$'; then
|
||||
echo "A third-party runtime image is not pinned by digest." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
terraform fmt -check -recursive iac/terraform
|
||||
terraform_roots=(
|
||||
iac/terraform/live/dev-k3s/vault-core
|
||||
iac/terraform/live/dev-k3s/vault-database
|
||||
)
|
||||
for root in "${terraform_roots[@]}"; do
|
||||
data_dir="$(mktemp -d)"
|
||||
TF_DATA_DIR="$data_dir" terraform -chdir="$root" init \
|
||||
-backend=false \
|
||||
-input=false \
|
||||
-lockfile=readonly >/dev/null
|
||||
TF_DATA_DIR="$data_dir" terraform -chdir="$root" validate
|
||||
rm -rf "$data_dir"
|
||||
done
|
||||
|
||||
if rg -n \
|
||||
'github\\.com/DongHyeonka/Project-Auth-GitOps|bitnami-labs\\.github\\.io/sealed-secrets|/home/donghyeon/dev/Project-Auth-GitOps|terraform/vault(-transit)?/(dev|reconcile)' \
|
||||
--glob '!docs/archive/**' \
|
||||
--glob '!policies/legacy/**' \
|
||||
--glob '!hack/validate.sh' \
|
||||
.; then
|
||||
echo "Current files contain a legacy repository URL, Helm URL, absolute path, or Terraform root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if rg -n 'uses:[[:space:]]+[^#[:space:]]+@v[0-9]' .gitea/workflows; then
|
||||
echo "Gitea Actions must be pinned to an immutable commit SHA." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if rg -n 'ApplyOutOfSyncOnly=true' bootstrap clusters; then
|
||||
echo "ApplyOutOfSyncOnly is incompatible with hook-based migrations and must not be enabled." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if rg -n 'git[[:space:]]+push[^#]*(HEAD:)?main([[:space:]]|$)' .gitea hack; then
|
||||
echo "Automation must promote changes through a branch and review, not push directly to main." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git diff --check
|
||||
echo "Repository validation passed."
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
VAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:8200}"
|
||||
VAULT_INIT_OUTPUT="${VAULT_INIT_OUTPUT:-${REPO_ROOT}/.local/vault/dev-k3s-init.json}"
|
||||
|
||||
for cmd in jq vault; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo "$cmd is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
export VAULT_ADDR
|
||||
|
||||
status_json() {
|
||||
local output=""
|
||||
local status_code=0
|
||||
|
||||
set +e
|
||||
output="$(vault status -format=json 2>/dev/null)"
|
||||
status_code=$?
|
||||
set -e
|
||||
|
||||
if [[ "$status_code" -ne 0 && "$status_code" -ne 2 ]]; then
|
||||
echo "Vault is not reachable at ${VAULT_ADDR}." >&2
|
||||
return "$status_code"
|
||||
fi
|
||||
printf '%s\n' "$output"
|
||||
}
|
||||
|
||||
unseal() {
|
||||
local status=""
|
||||
local unseal_key=""
|
||||
|
||||
status="$(status_json)"
|
||||
if [[ "$(jq -r '.sealed' <<<"$status")" == "false" ]]; then
|
||||
echo "Vault is already unsealed."
|
||||
return
|
||||
fi
|
||||
if [[ ! -f "$VAULT_INIT_OUTPUT" ]]; then
|
||||
echo "Init material is unavailable: ${VAULT_INIT_OUTPUT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unseal_key="$(jq -er '.unseal_keys_b64[0]' "$VAULT_INIT_OUTPUT")"
|
||||
vault operator unseal "$unseal_key" >/dev/null
|
||||
echo "Vault is unsealed."
|
||||
}
|
||||
|
||||
init() {
|
||||
local status=""
|
||||
|
||||
status="$(status_json)"
|
||||
if [[ "$(jq -r '.initialized' <<<"$status")" == "true" ]]; then
|
||||
echo "Refusing initialization: Vault is already initialized." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -e "$VAULT_INIT_OUTPUT" ]]; then
|
||||
echo "Refusing to overwrite existing init material: ${VAULT_INIT_OUTPUT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
umask 077
|
||||
mkdir -p "$(dirname "$VAULT_INIT_OUTPUT")"
|
||||
vault operator init \
|
||||
-key-shares=1 \
|
||||
-key-threshold=1 \
|
||||
-format=json >"$VAULT_INIT_OUTPUT"
|
||||
chmod 0600 "$VAULT_INIT_OUTPUT"
|
||||
unseal
|
||||
|
||||
echo "Dev Vault was initialized with a dev-only 1-of-1 Shamir key."
|
||||
echo "Move ${VAULT_INIT_OUTPUT} to encrypted custody before continuing."
|
||||
}
|
||||
|
||||
revoke_root() {
|
||||
local root_token=""
|
||||
local temporary=""
|
||||
|
||||
if [[ ! -f "$VAULT_INIT_OUTPUT" ]]; then
|
||||
echo "Init material is unavailable: ${VAULT_INIT_OUTPUT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
root_token="$(jq -er '.root_token' "$VAULT_INIT_OUTPUT")"
|
||||
VAULT_TOKEN="$root_token" vault token revoke -self
|
||||
|
||||
temporary="$(mktemp "${VAULT_INIT_OUTPUT}.XXXXXX")"
|
||||
jq 'del(.root_token)' "$VAULT_INIT_OUTPUT" >"$temporary"
|
||||
chmod 0600 "$temporary"
|
||||
mv "$temporary" "$VAULT_INIT_OUTPUT"
|
||||
echo "The initial root token was revoked and removed from the local init file."
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
init)
|
||||
init
|
||||
;;
|
||||
unseal)
|
||||
unseal
|
||||
;;
|
||||
revoke-root)
|
||||
revoke_root
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 <init|unseal|revoke-root>" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user