- Convert `docs/<chart>/README.md` files from copies to symlinks pointing to `../../<chart>/README.md` for the following charts: - cluster-issuer - deployment-updater - forgejo-runner - nextcloud - odoo - postgres - prometheus-agent - taskfile-build - vshn-postgres - Remove the `sync-docs` task command from `task` as it is no longer needed - Update `prompt.md` to mark the task as completed with new token statistics
864 lines
27 KiB
Bash
Executable File
864 lines
27 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -e
|
|
|
|
help-table() {
|
|
local -a rows
|
|
local max_cmd=7 max_opt=6 max_desc=11
|
|
|
|
rows+=("Command|Option|Description")
|
|
rows+=("===ENV===| | ")
|
|
rows+=("version||Show version of required tools.")
|
|
rows+=("info||Show info about env.")
|
|
rows+=("template-dotenv||Generate .env from .env.template.")
|
|
rows+=("export-kubeconfig|[user][path]|Export kubeconfig file for service account.")
|
|
rows+=("generate-kubconfig|[name]|Generate kubeconfig for deployment-updater.")
|
|
rows+=("load-kubeconfig-from-keepass|[ctx]|Load kubeconfig file from KeePass.")
|
|
rows+=("save-kubeconfig-to-keepass|[ctx]|Save kubeconfig file in KeePass.")
|
|
rows+=("===DOCS===| | ")
|
|
rows+=("init-zensical-docs||Initialize Zensical documentation build dependencies.")
|
|
rows+=("docs||Generate Helm chart readmes.")
|
|
rows+=("lint||Lint Helm charts.")
|
|
rows+=("dev-zensical-docs||Run Zensical development server.")
|
|
rows+=("build-zensical-docs||Build the Zensical documentation website.")
|
|
rows+=("serve-zensical-docs||Serve the Zensical documentation build folder.")
|
|
rows+=("===HOST===| | ")
|
|
rows+=("start||Start kind Kubernetes cluster.")
|
|
rows+=("start-and-prepare||Start and prepare Kubernetes cluster.")
|
|
rows+=("load-image|[tag]|Load Docker image into kind cluster.")
|
|
rows+=("delete-kind||Delete kind Kubernetes cluster.")
|
|
rows+=("setup-hosts||Update hosts file.")
|
|
rows+=("forward|[chart]|Forward port.")
|
|
rows+=("===CHART===| | ")
|
|
rows+=("list-charts||List local Helm charts.")
|
|
rows+=("list-external-charts||List external Helm charts.")
|
|
rows+=("list-cluster||List clusters.")
|
|
rows+=("list-deployment||List deployments.")
|
|
rows+=("list-context||List kubeconfig files.")
|
|
rows+=("switch-context||Switch kubeconfig file.")
|
|
rows+=("get-context||Show active kubeconfig.")
|
|
rows+=("get-cluster||Show active cluster.")
|
|
rows+=("update-dependencies|[chart]|Update dependencies for a chart.")
|
|
rows+=("install-chart|[chart][values]|Install Helm chart.")
|
|
rows+=("upgrade-release|[chart][values]|Upgrade Helm release.")
|
|
rows+=("uninstall-release|[release]|Uninstall Helm release.")
|
|
rows+=("===KUBECTL===| | ")
|
|
rows+=("show-secrets|[chart]|Show secrets for chart.")
|
|
rows+=("exec-bash|[chart]|Open bash shell in container.")
|
|
rows+=("logs|[grep]|Select pods with grep and show logs.")
|
|
rows+=("list||List Helm deployments.")
|
|
rows+=("get-pods||List all pods.")
|
|
rows+=("get-namespace||Show active namespace.")
|
|
rows+=("login-oc||Refresh the auth token for OpenShift.")
|
|
rows+=("run-job|[name]|Make a copy of job.")
|
|
rows+=("===HELM===| | ")
|
|
rows+=("add-repos||Add the required Helm repos.")
|
|
rows+=("template-manifest|[chart][values]|Template all charts with Helm.")
|
|
rows+=("package-repo|[chart]|Package Helm charts.")
|
|
rows+=("index-repo||Index Helm chart.")
|
|
rows+=("remove-repo||Remove the kubernetes-build repo.")
|
|
rows+=("commit||Commit .tgz files.")
|
|
rows+=("===LLM===| | ")
|
|
rows+=("commit-with-llm||Commit with llm generated commit message.")
|
|
rows+=("update-with-llm|[glob][prompt]|Feed files matching glob to LLM and apply changes.")
|
|
rows+=("===TEST===| | ")
|
|
rows+=("test-chart-cluster-issuer||Test cluster issuer.")
|
|
rows+=("test-chart-odoo|[cleanup]|Setup Odoo chart and dependencies.")
|
|
rows+=("test-chart-nextcloud|[cleanup]|Setup Nextcloud chart and dependencies.")
|
|
|
|
for row in "${rows[@]}"; do
|
|
IFS='|' read -r cmd opt desc <<< "$row"
|
|
(( ${#cmd} > max_cmd )) && max_cmd=${#cmd}
|
|
(( ${#opt} > max_opt )) && max_opt=${#opt}
|
|
(( ${#desc} > max_desc )) && max_desc=${#desc}
|
|
done
|
|
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_cmd" "Command" \
|
|
"$max_opt" "Option" \
|
|
"$max_desc" "Description"
|
|
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
|
|
"$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
|
|
"$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
|
|
|
|
local first_row=true
|
|
for row in "${rows[@]:1}"; do
|
|
IFS='|' read -r cmd opt desc <<< "$row"
|
|
if [[ "$cmd" =~ ^===.*===$ ]]; then
|
|
group_name="${cmd/===/}"
|
|
group_name="${group_name/===/}"
|
|
|
|
if [ "$first_row" = true ]; then
|
|
first_row=false
|
|
else
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
|
|
"$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
|
|
"$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
|
|
fi
|
|
|
|
local left_pad=$(( (max_cmd - ${#group_name}) / 2 ))
|
|
local right_pad=$(( max_cmd - left_pad - ${#group_name} ))
|
|
printf '| %*s%s%*s | %*s | %*s |\n' \
|
|
"$left_pad" "" \
|
|
"$group_name" \
|
|
"$right_pad" "" \
|
|
"$max_opt" "" \
|
|
"$max_desc" ""
|
|
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
|
|
"$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
|
|
"$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
|
|
else
|
|
first_row=false
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_cmd" "$cmd" \
|
|
"$max_opt" "$opt" \
|
|
"$max_desc" "$desc"
|
|
fi
|
|
done
|
|
}
|
|
|
|
help() {
|
|
echo
|
|
if [[ -n "$1" ]]; then
|
|
help-table | grep -i "$1" | column -t -s'|'
|
|
else
|
|
echo 'task <command> [options]'
|
|
echo
|
|
echo 'commands:'
|
|
echo
|
|
help-table
|
|
fi
|
|
echo
|
|
}
|
|
|
|
# Default vars
|
|
|
|
OC_PROJECT=${OC_PROJECT:="odoo"}
|
|
CONTAINER_REGISTRY=${CONTAINER_REGISTRY:="mintsystem/"}
|
|
CONTAINER_CONFIG="$HOME/.docker/$CONTAINER_REGISTRY"
|
|
|
|
# Dynamic vars
|
|
|
|
pass_entry=$(echo "env/${PWD##*/}" | tr '[:upper:]' '[:lower:]')
|
|
keepass_entry=$(echo "env/${PWD##*/}" | tr '[:upper:]' '[:lower:]')
|
|
|
|
get-namespace() {
|
|
if kubectl-ctx &> /dev/null; then
|
|
echo $(kubectl-ns -c 2> /dev/null)
|
|
fi
|
|
}
|
|
namespace="$(get-namespace)"
|
|
|
|
get-context() {
|
|
local alias="$(basename "$(readlink "$HOME/.kube/config" 2>/dev/null)" 2>/dev/null | cut -d '.' -f 2)"
|
|
echo "${alias:-loc}"
|
|
}
|
|
context_alias="$(get-context)"
|
|
|
|
# Import commands
|
|
|
|
clone-taskfile(){
|
|
if [[ ! -d "$HOME/taskfile.build" ]]; then
|
|
echo -e "\033[38;5;214mGit\033[0m: Clone taskfile repo"
|
|
git clone https://git.taskfile.build "$HOME/taskfile.build"
|
|
else
|
|
echo -e "\033[38;5;214mGit\033[0m: Pull taskfile repo"
|
|
git -C "$HOME/taskfile.build" pull
|
|
fi
|
|
}
|
|
|
|
if [[ -d "$HOME/taskfile.build/bin" ]]; then
|
|
for file in "$HOME/taskfile.build/bin/"*; do
|
|
if [[ -f "$file" ]]; then
|
|
source "$file"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Env Commands
|
|
|
|
version() {
|
|
kind version
|
|
helm version
|
|
}
|
|
|
|
info() {
|
|
echo "OpenShift-Project: $OC_PROJECT"
|
|
echo "Context-Alias: $(get-context)"
|
|
echo "Namespace: $namespace"
|
|
}
|
|
|
|
setup-hosts() {
|
|
echo 'Adding entries to /etc/hosts ...'
|
|
# Cluster
|
|
grep -qxF '127.0.0.1 knd.local' /etc/hosts || echo '127.0.0.1 knd.local' | sudo tee -a /etc/hosts > /dev/null
|
|
# Odoo
|
|
grep -qxF '127.0.0.1 odoo.local' /etc/hosts || echo '127.0.0.1 odoo.local' | sudo tee -a /etc/hosts > /dev/null
|
|
grep -qxF '127.0.0.1 odoo.knd.local' /etc/hosts || echo '127.0.0.1 odoo.knd.local' | sudo tee -a /etc/hosts > /dev/null
|
|
grep -qxF '127.0.0.1 staging.odoo.knd.local' /etc/hosts || echo '127.0.0.1 staging.odoo.knd.local' | sudo tee -a /etc/hosts > /dev/null
|
|
# Odoo with different namespace
|
|
grep -qxF '127.0.0.1 restic.local' /etc/hosts || echo '127.0.0.1 restic.local' | sudo tee -a /etc/hosts > /dev/null
|
|
grep -qxF '127.0.0.1 restic.knd.local' /etc/hosts || echo '127.0.0.1 restic.knd.local' | sudo tee -a /etc/hosts > /dev/null
|
|
grep -qxF '127.0.0.1 staging.restic.knd.local' /etc/hosts || echo '127.0.0.1 staging.restic.knd.local' | sudo tee -a /etc/hosts > /dev/null
|
|
# Nextcloud
|
|
grep -qxF '127.0.0.1 nextcloud.local' /etc/hosts || echo '127.0.0.1 nextcloud.local' | sudo tee -a /etc/hosts > /dev/null
|
|
grep -qxF '127.0.0.1 nextcloud.knd.local' /etc/hosts || echo '127.0.0.1 nextcloud.knd.local' | sudo tee -a /etc/hosts > /dev/null
|
|
# Collabora
|
|
grep -qxF '127.0.0.1 doc.nextcloud.knd.local' /etc/hosts || echo '127.0.0.1 doc.nextcloud.knd.local' | sudo tee -a /etc/hosts > /dev/null
|
|
# Removed hugo and vuepress hosts (replaced by taskfile-build)
|
|
}
|
|
|
|
test-chart-odoo() {
|
|
if [[ "$1" = 'cleanup' ]]; then
|
|
echo 'Cleanup environment'
|
|
delete-kind
|
|
else
|
|
start-and-prepare
|
|
|
|
update-dependencies odoo
|
|
install-chart odoo test_values/knd/odoo.yaml
|
|
kubectl wait --for=condition=Available --timeout=300s deployments/odoo-odoo
|
|
fi
|
|
}
|
|
|
|
test-chart-nextcloud() {
|
|
if [[ "$1" = 'cleanup' ]]; then
|
|
echo 'Cleanup environment'
|
|
delete-kind
|
|
else
|
|
start
|
|
add-repos
|
|
info
|
|
install-chart traefik
|
|
kubectl wait --namespace traefik --for=condition=Available --timeout=60s deployments/traefik
|
|
install-chart cnpg
|
|
kubectl wait --namespace cnpg-system --for=condition=Available --timeout=60s deployments/cnpg-cloudnative-pg
|
|
kubectl create secret generic nextcloud-creds --from-literal=NEXTCLOUD_ADMIN_USER="test" --from-literal=NEXTCLOUD_ADMIN_PASSWORD="test"
|
|
install-chart nextcloud test_values/knd/nextcloud.yaml
|
|
fi
|
|
}
|
|
|
|
export-kubeconfig() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
if test -z "$2"; then echo '\$2 is empty.'; exit; fi
|
|
|
|
local kube_user=$1
|
|
local kube_output_file=$2
|
|
kubectl config set-cluster default --server=https://k3s.raspberrypi.build --kubeconfig=$kube_output_file
|
|
kubectl config set-context default --cluster=default --user=$kube_user --kubeconfig=$kube_output_file
|
|
kubectl config set-credentials "$kube_user" --token=$(kubectl create token $kube_user --duration=87600h) --kubeconfig=$kube_output_file
|
|
kubectl config use-context default --kubeconfig=$kube_output_file
|
|
|
|
echo "Kubeconfig exported for user $kube_user to file $kube_output_file"
|
|
}
|
|
|
|
generate-kubconfig() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
|
|
local name="$1"
|
|
local cluster=$(get-cluster)
|
|
local kubeconfig_file="values/.kube/config.${cluster}-${name}"
|
|
local secret_name="${name}-sa-token"
|
|
|
|
local server=$(kubectl config view --minify --output 'jsonpath={.clusters[0].cluster.server}')
|
|
local ca=$(kubectl get secret/$secret_name -o jsonpath='{.data.ca\.crt}')
|
|
local token=$(kubectl get secret/$secret_name -o jsonpath='{.data.token}' | base64 --decode)
|
|
local namespace=$(kubectl get secret/$secret_name -o jsonpath='{.data.namespace}' | base64 --decode)
|
|
|
|
echo "apiVersion: v1
|
|
kind: Config
|
|
clusters:
|
|
- name: default-cluster
|
|
cluster:
|
|
certificate-authority-data: ${ca}
|
|
server: ${server}
|
|
contexts:
|
|
- name: default-context
|
|
context:
|
|
cluster: default-cluster
|
|
namespace: ${namespace}
|
|
user: default-user
|
|
current-context: default-context
|
|
users:
|
|
- name: default-user
|
|
user:
|
|
token: ${token}" > "$kubeconfig_file"
|
|
|
|
echo "Kubeconfig generated for $name at $kubeconfig_file"
|
|
}
|
|
|
|
# Host Commands
|
|
|
|
start() {
|
|
if [[ "$context_alias" != "loc" ]]; then
|
|
echo "Current context alias is '$context_alias' and not 'loc'."
|
|
exit 1
|
|
fi
|
|
|
|
echo 'Start kind.'
|
|
kind create cluster
|
|
}
|
|
|
|
start-and-prepare() {
|
|
start
|
|
add-repos
|
|
info
|
|
|
|
echo "Install traefik."
|
|
install-chart traefik
|
|
kubectl wait --namespace traefik --for=condition=Available --timeout=60s deployments/traefik
|
|
|
|
echo "Install the CloudNativePG."
|
|
install-chart cnpg
|
|
kubectl wait --namespace cnpg-system --for=condition=Available --timeout=60s deployments/cnpg-cloudnative-pg
|
|
|
|
echo "Install k8up."
|
|
install-chart k8up
|
|
|
|
#FIXME: This does not work in GitHub runner env.
|
|
echo "Load Odoo image."
|
|
docker pull mintsystem/odoo:19.0.20260926
|
|
load-image mintsystem/odoo:19.0.20260926
|
|
}
|
|
|
|
load-image() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
|
|
if kind &> /dev/null && kind get clusters | grep -q 'kind'; then
|
|
echo 'Kind cluster is available. Checking for local Docker images...'
|
|
|
|
if docker images -q "$1" > /dev/null; then
|
|
echo 'Loading local Odoo image into Kind cluster...'
|
|
kind load docker-image "$1"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
delete-kind() {
|
|
echo 'Delete kind.'
|
|
kind delete cluster
|
|
}
|
|
|
|
forward() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
"forward-$1"
|
|
}
|
|
|
|
forward-postgres() {
|
|
local port=5432
|
|
echo "Connect to http://localhost:$port url in your browser."
|
|
kubectl port-forward service/postgres $port:5432
|
|
}
|
|
|
|
forward-odoo() {
|
|
local port=8080
|
|
echo "Open http://localhost:$port url in your browser."
|
|
kubectl port-forward service/odoo $port:8069
|
|
}
|
|
|
|
forward-odoo-staging() {
|
|
local port=8080
|
|
echo "Open http://localhost:$port url in your browser."
|
|
kubectl port-forward service/odoo-staging $port:8069
|
|
}
|
|
|
|
forward-nextcloud() {
|
|
local port=8080
|
|
echo "Open http://localhost:$port url in your browser."
|
|
kubectl port-forward service/nextcloud $port:8080
|
|
}
|
|
|
|
forward-prometheus-agent() {
|
|
local port=9090
|
|
echo "Connect to http://localhost:$port to access the Prometheus agent."
|
|
kubectl port-forward -n prometheus deployment/prometheus-agent $port:9090
|
|
}
|
|
|
|
forward-traefik() {
|
|
local port=8080
|
|
kubectl get ingresses
|
|
echo "Open http://\$namespace.knd.local:$port url in your browser."
|
|
kubectl port-forward -n traefik svc/traefik $port:80
|
|
}
|
|
|
|
# Chart Commands
|
|
|
|
list-charts() {
|
|
for dir in */; do
|
|
dir_name="${dir%/}"
|
|
if [ -f "$dir/Chart.yaml" ]; then
|
|
echo "$dir_name"
|
|
fi
|
|
done
|
|
}
|
|
|
|
list-external-charts() {
|
|
echo 'cert-manager'
|
|
echo 'cnpg'
|
|
# echo 'haproxy-ingress'
|
|
echo 'k8up'
|
|
echo 'traefik'
|
|
}
|
|
|
|
list-cluster() {
|
|
local -a rows
|
|
local max_name=4 max_alias=5 max_base=4
|
|
|
|
for file in $(grep -l "^kind: cluster$" values/*.yaml 2>/dev/null); do
|
|
local name=$(yq eval '.name' "$file" 2>/dev/null)
|
|
local alias=$(yq eval '.alias' "$file" 2>/dev/null)
|
|
local base=$(basename "$file" .yaml)
|
|
[[ -z "$name" ]] && name="(not set)"
|
|
[[ -z "$alias" ]] && alias="(not set)"
|
|
|
|
rows+=("$name|$alias|$base")
|
|
|
|
(( ${#name} > max_name )) && max_name=${#name}
|
|
(( ${#alias} > max_alias )) && max_alias=${#alias}
|
|
(( ${#base} > max_base )) && max_base=${#base}
|
|
done
|
|
|
|
[[ ${#rows[@]} -eq 0 ]] && {
|
|
echo "No cluster files found."
|
|
return
|
|
}
|
|
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_name" "Name" \
|
|
"$max_alias" "Alias" \
|
|
"$max_base" "Cname"
|
|
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_name" "$(printf '%*s' "$max_name" '' | tr ' ' '-')" \
|
|
"$max_alias" "$(printf '%*s' "$max_alias" '' | tr ' ' '-')" \
|
|
"$max_base" "$(printf '%*s' "$max_base" '' | tr ' ' '-')"
|
|
|
|
printf '%s\n' "${rows[@]}" | sort | while IFS='|' read -r name alias base; do
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_name" "$name" \
|
|
"$max_alias" "$alias" \
|
|
"$max_base" "$base"
|
|
done
|
|
}
|
|
|
|
list-deployment() {
|
|
local -a rows
|
|
local max_namespace=9 max_cluster=7 max_chart=5
|
|
|
|
for file in $(grep -l "^kind: deployment$" values/*.yaml 2>/dev/null); do
|
|
local base=$(basename "$file" .yaml)
|
|
local namespace="${base%%.*}"
|
|
local cluster="${base#*.}"
|
|
local chart=$(yq eval '.chart' "$file" 2>/dev/null)
|
|
[[ "${chart}" == "null" ]] && chart=""
|
|
|
|
rows+=("$namespace|$cluster|$chart")
|
|
|
|
(( ${#namespace} > max_namespace )) && max_namespace=${#namespace}
|
|
(( ${#cluster} > max_cluster )) && max_cluster=${#cluster}
|
|
(( ${#chart} > max_chart )) && max_chart=${#chart}
|
|
done
|
|
|
|
[[ ${#rows[@]} -eq 0 ]] && {
|
|
echo "No deployment files found."
|
|
return
|
|
}
|
|
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_namespace" "Namespace" \
|
|
"$max_cluster" "Cluster" \
|
|
"$max_chart" "Chart"
|
|
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_namespace" "$(printf '%*s' "$max_namespace" '' | tr ' ' '-')" \
|
|
"$max_cluster" "$(printf '%*s' "$max_cluster" '' | tr ' ' '-')" \
|
|
"$max_chart" "$(printf '%*s' "$max_chart" '' | tr ' ' '-')"
|
|
|
|
printf '%s\n' "${rows[@]}" | sort | while IFS='|' read -r namespace cluster chart; do
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_namespace" "$namespace" \
|
|
"$max_cluster" "$cluster" \
|
|
"$max_chart" "$chart"
|
|
done
|
|
}
|
|
|
|
list-context() {
|
|
local -a rows
|
|
local max_name=4 max_path=4
|
|
|
|
for file in values/.kube/config.*; do
|
|
[[ ! -f "$file" ]] && continue
|
|
local name=$(basename "$file" | sed 's/^config\.//')
|
|
|
|
rows+=("$name|$file")
|
|
|
|
(( ${#name} > max_name )) && max_name=${#name}
|
|
(( ${#file} > max_path )) && max_path=${#file}
|
|
done
|
|
|
|
[[ ${#rows[@]} -eq 0 ]] && {
|
|
echo "No context files found in values/.kube/config.*"
|
|
return
|
|
}
|
|
|
|
printf '| %-*s | %-*s |\n' \
|
|
"$max_name" "Name" \
|
|
"$max_path" "Path"
|
|
|
|
printf '|-%*s-|-%*s-|\n' \
|
|
"$max_name" "$(printf '%*s' "$max_name" '' | tr ' ' '-')" \
|
|
"$max_path" "$(printf '%*s' "$max_path" '' | tr ' ' '-')"
|
|
|
|
printf '%s\n' "${rows[@]}" | sort | while IFS='|' read -r name path; do
|
|
printf '| %-*s | %-*s |\n' \
|
|
"$max_name" "$name" \
|
|
"$max_path" "$path"
|
|
done
|
|
}
|
|
|
|
switch-context() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
mkdir -p "$HOME/.kube"
|
|
local link_name="$HOME/.kube/config"
|
|
local target="$PWD/values/.kube/config.$1"
|
|
|
|
if [[ ! -f "$target" ]]; then
|
|
echo "Error: Target config file '$target' does not exist." >&2
|
|
return 1
|
|
fi
|
|
|
|
echo "Remove $link_name"
|
|
rm "$link_name" || true
|
|
|
|
echo "Setup symlink from $link_name to $target"
|
|
touch "$target"
|
|
ln -s "$target" "$link_name"
|
|
chmod 0644 "$target"
|
|
|
|
echo 'Select namespace'
|
|
kubectl-ns
|
|
}
|
|
|
|
get-cluster() {
|
|
basename "$(readlink "$HOME/.kube/config")" | cut -d. -f2 | cut -d- -f1
|
|
}
|
|
|
|
update-dependencies() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
helm dependency update "$1"
|
|
}
|
|
|
|
install-chart() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
if test -z "$2"; then echo '\$2 is empty.'; exit; fi
|
|
|
|
local values_file="$2"
|
|
local expected_namespace=""
|
|
if [[ "$values_file" =~ ^values/[^/]+/[^/]+\.yaml$ ]]; then
|
|
expected_namespace="$(basename "$values_file" .yaml)"
|
|
elif [[ "$values_file" =~ ^values/[^/]+/[^/]+/.+\.yaml$ ]]; then
|
|
expected_namespace="$(echo "$values_file" | cut -d '/' -f3)"
|
|
fi
|
|
if [[ -n "$expected_namespace" && "$namespace" != "$expected_namespace" ]]; then
|
|
echo "Error: Active namespace '$namespace' does not match values file namespace '$expected_namespace'."
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$(get-context)" = 'axo' ]]; then
|
|
echo "Set oc project to $OC_PROJECT."
|
|
oc project "$OC_PROJECT"
|
|
fi
|
|
local release_name="$1"
|
|
|
|
if [[ "$1" = 'cluster-issuer' ]]; then
|
|
"install-$1" "$2"
|
|
elif list-charts | grep -q "$1"; then
|
|
echo "Install chart $1 as release $release_name:"
|
|
helm install "$release_name" "$1" \
|
|
--values "$2"
|
|
else
|
|
"install-$1" "$2"
|
|
fi
|
|
}
|
|
|
|
install-cnpg() {
|
|
echo 'Install cnpg/cloudnative-pg.'
|
|
helm upgrade --install cnpg \
|
|
--namespace cnpg-system \
|
|
--create-namespace \
|
|
cnpg/cloudnative-pg
|
|
}
|
|
|
|
install-k8up() {
|
|
echo 'Install k8up-io/k8up.'
|
|
helm install k8up k8up-io/k8up \
|
|
--create-namespace --namespace k8up
|
|
}
|
|
|
|
install-cert-manager() {
|
|
echo 'Install jetstack/cert-manager.'
|
|
helm install \
|
|
cert-manager jetstack/cert-manager \
|
|
--namespace cert-manager \
|
|
--create-namespace \
|
|
--version v1.18.2 \
|
|
--set crds.enabled=true \
|
|
--set config.featureGates.ACMEHTTP01IngressPathTypeExact=false
|
|
|
|
# https://github.com/Infomaniak/cert-manager-webhook-infomaniak
|
|
echo 'Apply Infomaniak webhook'
|
|
kubectl apply -f https://github.com/Infomaniak/cert-manager-webhook-infomaniak/releases/download/v0.3.1/rendered-manifest.yaml
|
|
}
|
|
|
|
install-cluster-issuer() {
|
|
echo 'Install clusterissuer.'
|
|
helm install clusterissuer cluster-issuer \
|
|
--namespace cert-manager \
|
|
--values "$1"
|
|
}
|
|
|
|
install-traefik() {
|
|
echo 'Install traefik/traefik.'
|
|
helm install traefik traefik/traefik \
|
|
--create-namespace --namespace traefik
|
|
}
|
|
|
|
upgrade-release() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
if test -z "$2"; then echo '\$2 is empty.'; exit; fi
|
|
|
|
local values_file="$2"
|
|
local expected_namespace=""
|
|
if [[ "$values_file" =~ ^values/[^/]+/[^/]+\.yaml$ ]]; then
|
|
expected_namespace="$(basename "$values_file" .yaml)"
|
|
elif [[ "$values_file" =~ ^values/[^/]+/[^/]+/.+\.yaml$ ]]; then
|
|
expected_namespace="$(echo "$values_file" | cut -d '/' -f3)"
|
|
fi
|
|
if [[ -n "$expected_namespace" && "$namespace" != "$expected_namespace" ]]; then
|
|
echo "Error: Active namespace '$namespace' does not match values file namespace '$expected_namespace'."
|
|
exit 1
|
|
fi
|
|
|
|
local release_name="$1"
|
|
|
|
if list-charts | grep -q "$1"; then
|
|
echo "Upgrade $1 chart for release $release_name:"
|
|
helm upgrade "$release_name" "$1" \
|
|
--values "$2"
|
|
else
|
|
"upgrade-$1" "$2"
|
|
fi
|
|
}
|
|
|
|
upgrade-cert-manager() {
|
|
echo 'Upgrade jetstack/cert-manager.'
|
|
helm upgrade \
|
|
cert-manager jetstack/cert-manager \
|
|
--namespace cert-manager \
|
|
--version v1.18.2 \
|
|
--set crds.enabled=true \
|
|
--set config.featureGates.ACMEHTTP01IngressPathTypeExact=false
|
|
|
|
# https://github.com/Infomaniak/cert-manager-webhook-infomaniak
|
|
echo 'Apply Infomaniak webhook'
|
|
kubectl apply -f https://github.com/Infomaniak/cert-manager-webhook-infomaniak/releases/download/v0.3.1/rendered-manifest.yaml
|
|
}
|
|
|
|
upgrade-cluster-issuer() {
|
|
echo 'Upgrade cluster-issuer.'
|
|
helm upgrade \
|
|
clusterissuer cluster-issuer \
|
|
--namespace cert-manager \
|
|
--values "$2"
|
|
}
|
|
|
|
uninstall-release() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
local release_name="$1"
|
|
|
|
echo "Uninstall release $release_name:"
|
|
helm uninstall "$release_name"
|
|
}
|
|
|
|
uninstall-haproxy-ingress() {
|
|
echo 'Uninstall ingress-nginx release.'
|
|
helm uninstall haproxy-ingress/haproxy-ingress --namespace haproxy-ingress
|
|
kubectl delete namespace haproxy-ingress
|
|
}
|
|
|
|
uninstall-ingress-nginx() {
|
|
echo 'Uninstall ingress-nginx release.'
|
|
helm uninstall ingress-nginx --namespace ingress-nginx
|
|
kubectl delete namespace ingress-nginx
|
|
}
|
|
|
|
run-job() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit 1; fi
|
|
local job_name="$1"
|
|
|
|
echo "Run job: $job_name"
|
|
kubectl delete job ${job_name}-run --ignore-not-found
|
|
kubectl create job --from=cronjob/${job_name} ${job_name}-run
|
|
}
|
|
|
|
# kubectl Commands
|
|
|
|
show-secrets() {
|
|
if test -z "$1"; then echo '\$1/chart is empty.'; exit; fi
|
|
|
|
if [[ "$1" = 'postgres' ]]; then
|
|
echo 'postgres/user:'
|
|
kubectl get secret postgres -o jsonpath="{.data.user}" | base64 --decode && echo
|
|
echo 'postgres/password:'
|
|
kubectl get secret postgres -o jsonpath="{.data.password}" | base64 --decode && echo
|
|
|
|
elif [[ "$1" = 'cluster-issuer' ]]; then
|
|
echo 'cert-manager/infomaniak-api-credentials:'
|
|
kubectl get secret -n cert-manager infomaniak-api-token -o jsonpath="{.data.access-token}" | base64 --decode && echo
|
|
|
|
elif [[ "$1" = 'forgejo-runner' ]]; then
|
|
echo 'forgejo-runner/forgejoInstanceToken:'
|
|
kubectl get secret forgejo-runner -o jsonpath="{.data.forgejoInstanceToken}" | base64 --decode && echo
|
|
|
|
elif [[ "$1" = 'nextcloud' ]]; then
|
|
echo 'nextcloud/creds:'
|
|
kubectl get secret nextcloud-creds -o jsonpath="{.data.nextcloud-username}" | base64 --decode && echo
|
|
echo 'nextcloud/password:'
|
|
kubectl get secret nextcloud-creds -o jsonpath="{.data.nextcloud-password}" | base64 --decode && echo
|
|
echo 'nextcloud-postgresql-app:username:'
|
|
kubectl get secret nextcloud-postgresql-app -o jsonpath="{.data.username}" | base64 --decode && echo
|
|
echo 'nextcloud-postgresql-app:password:'
|
|
kubectl get secret nextcloud-postgresql-app -o jsonpath="{.data.password}" | base64 --decode && echo
|
|
fi
|
|
}
|
|
|
|
exec-bash() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
kubectl exec -it "$1" -- bash
|
|
}
|
|
|
|
logs() {
|
|
local pod_id=$(kubectl get pods -o=name --all-namespaces | grep "$1")
|
|
kubectl logs "$pod_id"
|
|
}
|
|
|
|
get-pods() {
|
|
kubectl get pods
|
|
}
|
|
|
|
# Helm Commands
|
|
|
|
template-manifest() {
|
|
if test -z "$1"; then echo '\$1 is empty.'; exit; fi
|
|
if test -z "$2"; then echo '\$2 is empty.'; exit; fi
|
|
local release_name="$1"
|
|
|
|
if [[ "$1" = 'cluster-issuer' ]]; then
|
|
helm --debug template "$release_name" cluster-issuer \
|
|
--namespace cert-manager \
|
|
--values "$2" > manifest.yaml
|
|
elif list-charts | grep -q "$1"; then
|
|
helm --debug template "$release_name" "$1" \
|
|
--values "$2"
|
|
fi
|
|
}
|
|
|
|
list() {
|
|
helm list
|
|
}
|
|
|
|
package-repo() {
|
|
if [[ -n "$1" ]]; then
|
|
echo "Package $1 Helm chart."
|
|
helm package "$1"
|
|
else
|
|
echo 'Package all Helm charts.'
|
|
update-dependencies odoo
|
|
update-dependencies prometheus-agent
|
|
index-repo
|
|
for chart in $(list-charts); do
|
|
helm package "$chart"
|
|
done
|
|
fi
|
|
}
|
|
|
|
commit() {
|
|
git config user.name 'Mint Bot'
|
|
git config user.email 'bot@mint-system.ch'
|
|
git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/Mint-System/Kubernetes-Build"
|
|
git add *.tgz
|
|
git commit -m 'feat(helm): package revision update'
|
|
git push || echo 'No changes to commit'
|
|
}
|
|
|
|
index-repo() {
|
|
echo 'Update index.yaml'
|
|
helm repo index .
|
|
}
|
|
|
|
add-repos(){
|
|
echo 'Add kubernetes-build.'
|
|
helm repo add kubernetes-build https://kubernetes.build
|
|
|
|
echo 'Add jetstack.'
|
|
helm repo add jetstack https://charts.jetstack.io
|
|
|
|
echo 'Add cnpg.'
|
|
helm repo add cnpg https://cloudnative-pg.github.io/charts
|
|
|
|
echo 'Add k8up.'
|
|
helm repo add k8up-io https://k8up-io.github.io/k8up
|
|
|
|
echo 'Add traefik.'
|
|
helm repo add traefik https://helm.traefik.io/traefik
|
|
}
|
|
|
|
remove-repo(){
|
|
echo 'Remove the kubernetes-build repo.'
|
|
helm repo remove kubernetes-build
|
|
}
|
|
|
|
# Docs Commands
|
|
|
|
docs() {
|
|
for repo in $(list-charts | grep -v '^deployment-updater$'); do
|
|
pnpm run generate-readme --values "$repo/values.yaml" -r "$repo/README.md"
|
|
done
|
|
}
|
|
|
|
lint() {
|
|
for chart in $(list-charts); do
|
|
helm lint "$chart"
|
|
done
|
|
}
|
|
|
|
if [[ "$1" == *","* ]]; then
|
|
# Split comma-separated commands
|
|
IFS=',' read -ra COMMANDS <<< "$1"
|
|
for cmd in "${COMMANDS[@]}"; do
|
|
# Trim whitespace
|
|
cmd=$(echo "$cmd" | xargs)
|
|
if declare -f "$cmd" > /dev/null; then
|
|
"$cmd"
|
|
else
|
|
echo "Unknown command: $1"
|
|
help "$@"
|
|
exit 1
|
|
fi
|
|
done
|
|
elif declare -f "$1" > /dev/null; then
|
|
"$1" "${@:2}"
|
|
else
|
|
case "$1" in
|
|
*)
|
|
echo "Unknown command: $1"
|
|
help
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|