prole/etc/final_deployment.sh

392 lines
12 KiB
Bash
Executable File

#!/bin/bash
# usage: etc/final_deployment.sh [-e|--docker-export] [-c|--helm-chart] [-k|--kustomize] [-p|--prepare-k3s-pipeline] [-o|--output-dir DIR]
#
# This script handles post-installation deployment tasks for Knoe.
# 1st use case: -e|--docker-export
# Runs docker image export for each image required to deploy Knoe.
# Artifacts are written to DOCKER_IMPORT_DIR.
# 2nd use case: -c|--helm-chart
# Creates a Helm chart from the current k8s manifests.
# Artifacts are written to HELM_CHART_DIR.
# 3rd use case: -k|--kustomize
# Creates kustomize deployments (one per component).
# Artifacts are written to KUSTOMIZE_DIR.
# 4th use case: -p|--prepare-k3s-pipeline
# Stages all artifacts for k3s delivery via OpenTofu + ArgoCD.
# Copies manifests, generates tfvars, and creates ArgoCD Application manifests.
usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " -e, --docker-export Export required docker images to DOCKER_IMPORT_DIR"
echo " -c, --helm-chart Create Helm chart from k8s manifests"
echo " -k, --kustomize Create kustomize deployments from k8s manifests"
echo " -p, --prepare-k3s-pipeline Stage artifacts for k3s delivery (OpenTofu + ArgoCD)"
echo " -o, --output-dir DIR Base directory for docker-import/, helm-chart/, and kustomize/"
echo ""
}
do_docker_export=0
do_helm_chart=0
do_kustomize=0
do_k3s_pipeline=0
export_dir=""
if [[ $# -eq 0 ]]; then
usage
exit 1
fi
while [[ $# -gt 0 ]]; do
case "$1" in
-e|--docker-export)
do_docker_export=1
shift
;;
-c|--helm-chart)
do_helm_chart=1
shift
;;
-k|--kustomize)
do_kustomize=1
shift
;;
-p|--prepare-k3s-pipeline)
do_k3s_pipeline=1
shift
;;
-o|--output-dir)
if [[ -z "${2:-}" ]]; then
echo "Missing value for $1"
usage
exit 1
fi
export_dir="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done
if [[ $do_docker_export -eq 0 && $do_helm_chart -eq 0 && $do_kustomize -eq 0 && $do_k3s_pipeline -eq 0 ]]; then
usage
exit 1
fi
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
export PROJECT_ROOT
# Load environment if env.sh exists
if [[ -f "${PROJECT_ROOT}/env.sh" ]]; then
source "${PROJECT_ROOT}/env.sh"
fi
default_owner="$(id -un 2>/dev/null || echo knoe)"
knoe_data="${PROLE_DATA:-/opt/knoe/data/${default_owner}}"
knoe_data="${knoe_data%/}"
if [[ -n "$export_dir" ]]; then
export_dir="${export_dir%/}"
DOCKER_IMPORT_DIR="${export_dir}/docker-import"
HELM_CHART_DIR="${export_dir}/helm-chart"
KUSTOMIZE_DIR="${export_dir}/kustomize"
fi
DOCKER_IMPORT_DIR="${DOCKER_IMPORT_DIR:-${knoe_data}/docker-import}"
HELM_CHART_DIR="${HELM_CHART_DIR:-${knoe_data}/helm-chart}"
KUSTOMIZE_DIR="${KUSTOMIZE_DIR:-${knoe_data}/kustomize}"
export_docker_images() {
echo "Exporting Docker images to: ${DOCKER_IMPORT_DIR}"
mkdir -p "${DOCKER_IMPORT_DIR}"
# Collect images from K8s manifests
# Robust extraction of image names from YAML files
find "${PROJECT_ROOT}/k8s/knoe" "${PROJECT_ROOT}/k8s/openbao" "${PROJECT_ROOT}/k8s/opentofu" -name "*.yaml" -exec grep -h "image:" {} + | awk -F'image:' '{print $2}' | awk '{print $1}' | sed "s/['\"]//g" > /tmp/knoe_images.txt
# Also check Supabase compose if it exists
if [[ -f "${PROJECT_ROOT}/supabase/docker/docker-compose.yml" ]]; then
grep -h "image:" "${PROJECT_ROOT}/supabase/docker/docker-compose.yml" | awk -F'image:' '{print $2}' | awk '{print $1}' | sed "s/['\"]//g" >> /tmp/knoe_images.txt
fi
# Add Kerberos proxy image if defined
KRB_IMG="${KRB5_AD_PROXY_IMAGE:-alpine/socat}"
if [[ -n "$KRB_IMG" ]]; then
echo "$KRB_IMG" >> /tmp/knoe_images.txt
fi
# Add Kerberos test image (used by init_kerberos_test.sh)
KRB_TEST_IMG="${KRB5_TEST_IMAGE:-${PROLE_KRB_TEST_IMAGE:-ubuntu:24.04}}"
if [[ -n "$KRB_TEST_IMG" ]]; then
echo "$KRB_TEST_IMG" >> /tmp/knoe_images.txt
fi
# Unique images
sort -u /tmp/knoe_images.txt > /tmp/knoe_images_unique.txt
while read -r image; do
if [[ -z "$image" ]]; then continue; fi
# Ensure we have the image locally
echo "Checking image: $image"
if ! docker image inspect "$image" >/dev/null 2>&1; then
echo "Pulling $image (platform linux/arm64)..."
docker pull --platform linux/arm64 "$image"
fi
safe_name=$(echo "$image" | sed 's/\//_/g' | sed 's/:/_/g')
tar_path="${DOCKER_IMPORT_DIR}/${safe_name}.tar"
echo "Exporting $image to $tar_path..."
docker save "$image" -o "$tar_path"
if [[ $? -eq 0 ]]; then
echo "[OK] Exported $image"
else
echo "[ERROR] Failed to export $image"
fi
done < /tmp/knoe_images_unique.txt
rm -f /tmp/knoe_images.txt /tmp/knoe_images_unique.txt
echo "Docker export complete."
}
create_helm_chart() {
local chart_dir="${HELM_CHART_DIR}"
local chart_name="knoe"
local chart_version="0.1.0"
local app_version
app_version="$(date -u +%Y.%m.%d)"
echo "Creating Helm chart in: ${chart_dir}"
mkdir -p "${chart_dir}/templates"
# Clean managed template subdirs to avoid stale manifests.
rm -rf "${chart_dir}/templates/knoe" "${chart_dir}/templates/openbao" "${chart_dir}/templates/opentofu"
cat > "${chart_dir}/Chart.yaml" <<EOF
apiVersion: v2
name: ${chart_name}
description: Knoe deployment manifests
type: application
version: ${chart_version}
appVersion: "${app_version}"
EOF
cat > "${chart_dir}/values.yaml" <<'EOF'
# Values are unused by default. Manifests are installed as-is.
EOF
copy_manifests() {
local src_dir="$1"
local dest_subdir="$2"
if [[ ! -d "$src_dir" ]]; then
echo "Skipping missing manifest dir: $src_dir"
return
fi
local dest_dir="${chart_dir}/templates/${dest_subdir}"
mkdir -p "${dest_dir}"
local found=0
while IFS= read -r -d '' file; do
if [[ "$(basename "$file")" == "kustomization.yaml" ]]; then
continue
fi
cp "$file" "${dest_dir}/"
found=1
done < <(find "$src_dir" -maxdepth 1 -type f -name "*.yaml" -print0)
if [[ $found -eq 0 ]]; then
echo "No manifests found in $src_dir"
fi
}
copy_manifests "${PROJECT_ROOT}/k8s/knoe" "knoe"
copy_manifests "${PROJECT_ROOT}/k8s/openbao" "openbao"
copy_manifests "${PROJECT_ROOT}/k8s/opentofu" "opentofu"
echo "Helm chart creation complete."
}
create_kustomize_deployments() {
local base_dir="${KUSTOMIZE_DIR}"
echo "Creating kustomize deployments in: ${base_dir}"
mkdir -p "${base_dir}"
create_kustomize_package() {
local name="$1"
local src_dir="$2"
if [[ ! -d "$src_dir" ]]; then
echo "Skipping missing manifest dir: $src_dir"
return
fi
local dest_dir="${base_dir}/${name}"
mkdir -p "${dest_dir}"
# Copy manifests except kustomization.yaml
local files=()
while IFS= read -r -d '' file; do
if [[ "$(basename "$file")" == "kustomization.yaml" ]]; then
continue
fi
cp "$file" "${dest_dir}/"
files+=("$(basename "$file")")
done < <(find "$src_dir" -maxdepth 1 -type f -name "*.yaml" -print0)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No manifests found in $src_dir"
return
fi
{
echo "apiVersion: kustomize.config.k8s.io/v1beta1"
echo "kind: Kustomization"
echo "resources:"
for f in "${files[@]}"; do
echo " - ${f}"
done
} > "${dest_dir}/kustomization.yaml"
}
create_kustomize_package "knoe" "${PROJECT_ROOT}/k8s/knoe"
create_kustomize_package "openbao" "${PROJECT_ROOT}/k8s/openbao"
create_kustomize_package "opentofu" "${PROJECT_ROOT}/k8s/opentofu"
echo "Kustomize deployments creation complete."
}
if [[ $do_docker_export -eq 1 ]]; then
export_docker_images
fi
if [[ $do_helm_chart -eq 1 ]]; then
create_helm_chart
fi
if [[ $do_kustomize -eq 1 ]]; then
create_kustomize_deployments
fi
prepare_k3s_pipeline() {
echo "========================================"
echo "Preparing k3s pipeline (OpenTofu + ArgoCD)"
echo "========================================"
echo ""
local pipeline_dir="${PROJECT_ROOT}/deploy/opentofu/k3s"
local manifest_root="${pipeline_dir}/manifests"
local argocd_dir="${pipeline_dir}/argocd"
mkdir -p "${manifest_root}" "${argocd_dir}"
echo "==> Staging k8s manifests to ${manifest_root}"
local total=0
for component in knoe openbao opentofu; do
local src="${PROJECT_ROOT}/k8s/${component}"
if [[ ! -d "$src" ]]; then
echo " [SKIP] ${component}/ (not found)"
continue
fi
local dst="${manifest_root}/${component}"
mkdir -p "$dst"
local count=0
for f in "${src}"/*.yaml; do
[[ -f "$f" ]] || continue
cp "$f" "$dst/"
count=$((count + 1))
done
total=$((total + count))
echo " [OK] ${component}/ -> manifests/${component}/ (${count} files)"
done
echo " Total manifests staged: ${total}"
# Stage Supabase manifests if present
local supabase_k8s="${PROJECT_ROOT}/supabase/k8s"
if [[ -d "$supabase_k8s" ]]; then
local dst="${manifest_root}/supabase"
mkdir -p "$dst"
local count=0
for f in "${supabase_k8s}"/*.yaml; do
[[ -f "$f" ]] || continue
cp "$f" "$dst/"
count=$((count + 1))
done
if [[ $count -gt 0 ]]; then
echo " [OK] supabase/k8s/ -> manifests/supabase/ (${count} files)"
fi
fi
# Write tfvars
local k3s_server="${K3S_SERVER_URL:-}"
local k3s_token="${K3S_TOKEN:-}"
local namespace="${PROLE_NAMESPACE:-default}"
echo ""
echo "==> Writing opentofu.auto.tfvars"
echo " k3s_server_url = ${k3s_server:-(not set)}"
echo " namespace = ${namespace}"
cat > "${pipeline_dir}/opentofu.auto.tfvars" <<EOF
k3s_server_url = "${k3s_server}"
k3s_token = "${k3s_token}"
namespace = "${namespace}"
EOF
# Generate ArgoCD Application manifests
echo ""
echo "==> Generating ArgoCD Application manifests in argocd/"
local repo_url="${PROLE_GIT_REPO:-}"
if [[ -z "$repo_url" ]]; then
repo_url=$(cd "${PROJECT_ROOT}" && git remote get-url origin 2>/dev/null || true)
fi
repo_url="${repo_url:-https://github.com/knoe-dev/knoe.git}"
local target_revision="${PROLE_GIT_BRANCH:-main}"
for component in knoe openbao opentofu; do
cat > "${argocd_dir}/application-${component}.yaml" <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: knoe-${component}
namespace: argocd
spec:
project: default
source:
repoURL: ${repo_url}
targetRevision: ${target_revision}
path: deploy/opentofu/k3s/manifests/${component}
destination:
server: ${k3s_server:-https://kubernetes.default.svc}
namespace: ${namespace}
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
EOF
echo " [OK] application-${component}.yaml"
done
echo ""
echo "==> k3s pipeline preparation complete."
echo " Pipeline dir: ${pipeline_dir}"
echo " ArgoCD apps: ${argocd_dir}"
echo ""
echo "Next steps:"
echo " cd ${pipeline_dir} && tofu init && tofu plan && tofu apply"
echo " kubectl apply -f ${argocd_dir}/"
}
if [[ $do_k3s_pipeline -eq 1 ]]; then
prepare_k3s_pipeline
fi