Files
Kubernetes-Build/task
T

689 lines
21 KiB
Bash
Executable File

#!/bin/bash
set -e
if [[ -a ".env" ]]; then
source .env
fi
function help() {
echo
echo "task <command> [options]"
echo
echo "commands:"
echo
# Define column widths
CMD_WIDTH=26
OPT_WIDTH=14
DESC_WIDTH=50
COLUMN="| %-${CMD_WIDTH}s | %-${OPT_WIDTH}s | %-${DESC_WIDTH}s |\n"
# Print table header
printf "$COLUMN" "Command" "Option" "Description"
echo "|$(printf '%*s' $((CMD_WIDTH + 2)) '' | tr ' ' '-')|$(printf '%*s' $((OPT_WIDTH + 2)) '' | tr ' ' '-')|$(printf '%*s' $((DESC_WIDTH + 2)) '' | tr ' ' '-')|"
# Env
printf "$COLUMN" "version" "" "Show version of required tools."
printf "$COLUMN" "info" "" "Show info about env."
printf "$COLUMN" "template-dotenv" "" "Generate .env from .env.template."
printf "$COLUMN" "export-kubeconfig" "[user][path]" "Export kubeconfig file for service account."
printf "$COLUMN" "load-kubeconfig" "[ctx]" "Load kubeconfig file from pass."
printf "$COLUMN" "load-dotenv" "" "Restore content to .env from pass entry."
printf "$COLUMN" "save-kubeconfig" "[ctx]" "Save kubeconfig file in pass."
printf "$COLUMN" "save-dotenv" "" "Store content of .env in pass entry."
# Docs
printf "$COLUMN" "install" "" "Install node dependencies."
printf "$COLUMN" "docs" "" "Generate Helm chart readmes."
printf "$COLUMN" "lint" "" "Lint Helm charts."
printf "$COLUMN" "dev-vitepress" "" "Start VitePress dev server."
printf "$COLUMN" "build-vitepress" "" "Build site with VitePress."
printf "$COLUMN" "preview-vitepress" "" "Server VitePress build."
# Host
printf "$COLUMN" "start-kind" "" "Start kind Kubernetes cluster."
printf "$COLUMN" "delete-kind" "" "Delete kind Kubernetes cluster."
printf "$COLUMN" "start-minikube" "" "Start minikube Kubernetes cluster."
printf "$COLUMN" "setup-hosts" "" "Update hosts file."
printf "$COLUMN" "forward" "[name]" "Forward port."
# Chart
printf "$COLUMN" "list-chart" "" "List Helm charts."
printf "$COLUMN" "list-context" "" "List contexts."
printf "$COLUMN" "switch-context" "" "Switch kubeconfig file."
printf "$COLUMN" "show-context" "" "Show active kubeconfig."
printf "$COLUMN" "get-context-alias" "" "Get alias of current context."
printf "$COLUMN" "template-values" "[ctx]" "Template values file."
printf "$COLUMN" "update-dependencies" "[name]" "Update dependencies for a chart."
printf "$COLUMN" "install-chart" "[name]" "Install Helm chart."
printf "$COLUMN" "upgrade-release" "[name]" "Upgrade Helm release."
printf "$COLUMN" "uninstall-release" "" "Uninstall Helm release."
# kubectl
printf "$COLUMN" "get-secrets" "[name]" "Show secrets for chart."
printf "$COLUMN" "exec-bash" "[name]" "Open bash shell in container."
printf "$COLUMN" "logs-odoo" "" "Show pod logs."
printf "$COLUMN" "logs-haproxy-ingress" "" "Show haproxy-ingress logs."
printf "$COLUMN" "logs-ingress-nginx" "" "Show ingress-nginx logs."
printf "$COLUMN" "list" "" "List Helm deployments."
printf "$COLUMN" "get-pods" "" "List all pods."
printf "$COLUMN" "login-oc" "" "Refresh the auth token for OpenShift."
# Helm
printf "$COLUMN" "add-repos" "" "Add the required Helm repos."
printf "$COLUMN" "template-manifest" "[name][ctx]" "Template all charts with Helm."
printf "$COLUMN" "package-repo" "[name]" "Package Helm charts."
printf "$COLUMN" "index-repo" "" "Index Helm chart."
printf "$COLUMN" "publish-repo" "" "Copy Helm chart files to dist."
printf "$COLUMN" "remove-repo" "" "Remove the kubernetes-build repo."
printf "$COLUMN" "commit" "" "Commit .tgz files."
# LLM
printf "$COLUMN" "commit-with-llm" "" "Commit with llm generated commit message."
printf "$COLUMN" "update-with-llm" "[glob][prompt]" "Feed files matching glob to LLM and apply changes."
# Test
printf "$COLUMN" "test-clusterIssuer" "" "Test cluster issuer."
printf "$COLUMN" "test-k8s" "[clean]" "Test Kubernetes environment."
}
# 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:]')
if kubectl &> /dev/null; then
CURRENT_CONTEXT=$(kubectl config current-context 2> /dev/null) || CURRENT_CONTEXT=""
fi
# Import commands
function 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
function version() {
minikube version
kind version
helm version
}
function info() {
echo "Current Context: $CURRENT_CONTEXT"
echo "Context Alias: $(get-context-alias)"
echo "External IP: $EXTERNAL_IP"
}
function install() {
pnpm install
}
function setup-hosts() {
echo "Adding entries to /etc/hosts ..."
grep -qxF '127.0.0.1 knd.local' /etc/hosts || echo "127.0.0.1 knd.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 odoo.local" /etc/hosts || echo "127.0.0.1 odoo.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 restic.local" /etc/hosts || echo "127.0.0.1 restic.local" | sudo tee -a /etc/hosts > /dev/null
}
function test-k8s() {
if [ "$1" = "clean" ]; then
echo "Cleanup environment"
delete-kind
rm values.yaml
else
start-kind
add-repos
info
update-dependencies odoo
install-chart odoo
install-ingress-nginx
fi
}
function save-kubeconfig() {
PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$USER" | tr '[:upper:]' '[:lower:]')
KUBECONFIG_FILE="$HOME/.kube/config"
if [ -n"$1" ]; then
PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$1" | tr '[:upper:]' '[:lower:]')
KUBECONFIG_FILE="$HOME/.kube/config.$1"
fi
if [ -f "$KUBECONFIG_FILE" ]; then
echo "Store $KUBECONFIG_FILE file in pass: $PASS_KUBECONFIG_ENTRY"
echo -e "\n$(cat $KUBECONFIG_FILE)" | pass insert -m -f "$PASS_KUBECONFIG_ENTRY"
else
echo "No $KUBECONFIG_FILE file found."
fi
}
function load-kubeconfig() {
PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$USER" | tr '[:upper:]' '[:lower:]')
KUBECONFIG_FILE="$HOME/.kube/config"
if [ -n"$1" ]; then
PASS_KUBECONFIG_ENTRY=$(echo "env/kubeconfig_$1" | tr '[:upper:]' '[:lower:]')
KUBECONFIG_FILE="$HOME/.kube/config.$1"
fi
if pass find "$PASS_KUBECONFIG_ENTRY" >/dev/null; then
echo "Load $KUBECONFIG_FILE file from pass: $PASS_KUBECONFIG_ENTRY"
mkdir -p "$HOME/.kube/"
pass show "$PASS_KUBECONFIG_ENTRY" | tail -n +2 > "$KUBECONFIG_FILE"
else
echo "Pass entry not found."
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
KUBE_USER=$1
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"
}
# Docs Commands
function docs() {
pnpm run generate-readme --values clusterIssuer/values.yaml -r clusterIssuer/README.md
pnpm run generate-readme --values odoo/values.yaml -r odoo/README.md
pnpm run generate-readme --values postgres/values.yaml -r postgres/README.md
pnpm run generate-readme --values vshnPostgres/values.yaml -r vshnPostgres/README.md
pnpm run generate-readme --values hugo/values.yaml -r hugo/README.md
# pnpm run generate-readme --values deploymentUpdater/values.yaml -r deploymentUpdater/README.md
}
function lint() {
helm lint clusterIssuer
helm lint odoo
helm lint postgres
helm lint vshnPostgres
helm lint hugo
helm lint deploymentUpdater
}
function dev-vitepress() {
cp README.md index.md
pnpm run docs:dev
}
function build-vitepress() {
cp README.md index.md
pnpm run docs:build
}
function preview-vitepress() {
pnpm run docs:preview
}
# Host Commands
function start-kind() {
echo "Start kind."
kind create cluster
# echo "Apply kubelet config"
# cat <<EOF | kubectl apply -f -
# ---
# kind: Cluster
# apiVersion: kind.x-k8s.io/v1alpha4
# nodes:
# - role: control-plane
# extraMounts:
# - containerPath: /var/lib/kubelet/config.json
# hostPath: $CONTAINER_CONFIG/secret.json
# EOF
}
function delete-kind() {
echo "Delete kind."
kind delete cluster
}
function start-minikube() {
echo "Start minikube."
minikube start
}
function forward() {
if test -z "$1"; then echo "\$1 is empty."; exit; fi
"forward-$1"
}
function forward-postgres() {
PORT=5432
echo "Connect to http://localhost:$PORT url in your browser."
kubectl port-forward service/postgres $PORT:5432
}
function forward-odoo() {
PORT=8069
echo "Open http://localhost:$PORT url in your browser."
kubectl port-forward service/odoo $PORT:8069
}
function forward-hugo() {
PORT=1313
echo "Open http://localhost:$PORT url in your browser."
kubectl port-forward service/hugo $PORT:1313
}
function forward-ingress-nginx() {
PORT=8080
echo "Open http://odoo.knd.local:$PORT or http://odoo.local:$PORT url in your browser."
kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller $PORT:80
}
function forward-haproxy-ingress() {
PORT=8080
echo "Open http://odoo.knd.local:$PORT or http://odoo.local:$POR url in your browser."
kubectl port-forward -n haproxy-ingress svc/haproxy-ingress $PORT:80
}
# Chart Commands
function list-chart() {
echo "keel"
echo "deploymentUpdater"
echo "postgresql"
echo "hugo"
echo "postgres"
echo "vshnpostgres"
echo "cert-manager"
echo "clusterIssuer"
echo "ingress-nginx"
echo "haproxy-ingress"
}
function list-context() {
echo "loc (knd.local)"
echo "rpi (k3s.raspberrypi.build)"
echo "chk (chk.mintcloud.ch)"
echo "axo (axo.mintcloud.ch)"
echo "ksi (k8s.sozialinfo.ch)"
}
get-context-alias() {
echo "$(basename "$(readlink "$HOME/.kube/config")" | cut -d '.' -f 2)"
}
function switch-context() {
if test -z "$1"; then echo "\$1 is empty."; exit; fi
LINK_NAME="$HOME/.kube/config"
echo "Remove $LINK_NAME"
rm "$LINK_NAME" || true
TARGET="$HOME/.kube/config.$1"
echo "Setup symlink from $LINK_NAME to $TARGET"
ln -s "$TARGET" "$LINK_NAME"
chmod 0644 "$TARGET"
}
function show-context() {
ls -l "$HOME/.kube/config"
}
function template-values {
ALIAS="$1"
if [ -z "$ALIAS" ]; then
ALIAS="$(get-context-alias)"
fi
export ACME_EMAIL
export INFOMANIAK_API_TOKEN
if [ -n "$ALIAS" ]; then
echo "Template values.yaml from values.$ALIAS.yaml.template"
envsubst < "values.$ALIAS.yaml.template" > "values.yaml"
else
echo "Template values.yaml from values.yaml.template"
envsubst < "values.yaml.template" > "values.yaml"
fi
}
function update-dependencies() {
if test -z "$1"; then echo "\$1 is empty."; exit; fi
helm dependency update "$1"
}
function install-chart() {
if test -z "$1"; then echo "\$1 is empty."; exit; fi
if [ "$(get-context-alias)" = "axo" ]; then
echo "Set oc project to $OC_PROJECT."
oc project "$OC_PROJECT"
fi
template-values
# if docker images -q "janikvonrotz/janikvonrotz.ch:latest" > /dev/null; then
# echo "Load local Hugo image."
# kind load docker-image "janikvonrotz/janikvonrotz.ch:latest"
# fi
# if docker images -q "mintsystem/odoo:18.0.20250725" > /dev/null; then
# echo "Load local Odoo image."
# kind load docker-image "mintsystem/odoo:18.0.20250725"
# fi
if [[ "$1" =~ (postgres|vshnPostgres|deploymentUpdater) ]]; then
echo "Install chart $1 as release $1."
helm install "$1" "$1" \
--values ./values.yaml
else
"install-$1"
fi
}
function install-postgresql() {
echo "Install bitnami/postgresql"
helm install postgresql bitnami/postgresql --values odoo/values.yaml
}
function install-ingress-nginx() {
if [ -n "$EXTERNAL_IP" ]; then
echo "Install ingress-nginx/ingress-nginx with external IP $EXTERNAL_IP:"
helm install ingress-nginx ingress-nginx/ingress-nginx \
--create-namespace --namespace ingress-nginx \
--set controller.service.externalIPs[0]=$EXTERNAL_IP
else
echo "Install ingress-nginx/ingress-nginx:"
helm install ingress-nginx ingress-nginx/ingress-nginx \
--create-namespace --namespace ingress-nginx
fi
}
function 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
}
function install-clusterIssuer() {
echo "Install clusterIssuer."
helm install \
clusterIssuer clusterIssuer \
--namespace cert-manager \
--values ./values.yaml
}
function test-clusterIssuer() {
echo "Apply certificate."
cat <<EOF | kubectl apply -f -
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: chk-mintcloud-ch
spec:
secretName: chk-mintcloud-ch
issuerRef:
name: letsencrypt-dns-staging
kind: ClusterIssuer
dnsNames:
- chk.mintcloud.ch
EOF
}
function install-haproxy-ingress() {
echo "Install haproxy-ingress/haproxy-ingress."
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: haproxy
spec:
controller: haproxy-ingress.github.io/controller
EOF
helm install haproxy-ingress haproxy-ingress/haproxy-ingress \
--create-namespace --namespace haproxy-ingress
}
function upgrade-release() {
if test -z "$1"; then echo "\$1 is empty."; exit; fi
template-values
if [[ "$1" =~ (hugo|odoo|postgres|vshnPostgres|deploymentUpdater) ]]; then
echo "Upgrade $1 chart for release $1."
helm upgrade "$1" "$1" \
--values ./values.yaml
else
"upgrade-$1"
fi
}
function upgrade-postgresql() {
echo "Upgrade bitnami/postgresql"
helm upgrade postgresql bitnami/postgresql -f odoo/values.yaml
}
function 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
}
function upgrade-clusterIssuer() {
echo "Upgrade clusterIssuer."
helm upgrade \
clusterIssuer clusterIssuer \
--namespace cert-manager \
--values ./values.yaml
}
function upgrade-ingress-nginx() {
if [ -n "$EXTERNAL_IP" ]; then
echo "Upgrade ingress-nginx/ingress-nginx with external IP $EXTERNAL_IP:"
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--set controller.service.externalIPs[0]=$EXTERNAL_IP
else
echo "Upgrade ingress-nginx/ingress-nginx:"
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx
fi
}
function uninstall-release() {
if test -z "$1"; then echo "\$1 is empty."; exit; fi
if [[ "$1" =~ (hugo|odoo|postgres|postgresql|vshnPostgres) ]]; then
echo "Uninstall release $1."
helm uninstall "$1"
else
"uninstall-$1"
fi
}
function uninstall-ingress-nginx() {
echo "Uninstall release ingress-nginx."
helm uninstall ingress-nginx --namespace ingress-nginx
}
function uninstall-haproxy-ingress() {
echo "Uninstall release haproxy-ingress."
helm uninstall haproxy-ingress --namespace haproxy-ingress
}
function uninstall-cert-manager() {
echo "Uninstall release cert-manager."
helm uninstall cert-manager --namespace cert-manager
echo "Delete Infomaniak webhook"
kubectl delete -f https://github.com/Infomaniak/cert-manager-webhook-infomaniak/releases/download/v0.3.1/rendered-manifest.yaml
}
function uninstall-clusterIssuer() {
echo "Uninstall release clusterIssuer."
helm uninstall clusterIssuer --namespace cert-manager
}
# kubectl Commands
function get-secrets() {
if [ "$1" = "odoo" ]; then
echo "odoo-postgres/user:"
kubectl get secret odoo-postgres -o jsonpath="{.data.user}" | base64 --decode && echo
echo "odoo-postgres/password:"
kubectl get secret odoo-postgres -o jsonpath="{.data.password}" | base64 --decode && echo
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
fi
if [ "$1" = "postgresql" ]; then
echo "odoo-postgresql/password:"
kubectl get secret odoo-postgresql -o jsonpath="{.data.password}" | base64 --decode && echo
echo "odoo-postgresql/postgres-password:"
kubectl get secret odoo-postgresql -o jsonpath="{.data.postgres-password}" | base64 --decode && echo
fi
}
function exec-bash() {
if test -z "$1"; then echo "\$1 is empty."; exit; fi
kubectl exec -it "$1" -- bash
}
function logs-odoo() {
POD_ID=$(kubectl get pods -o=name --all-namespaces | grep "odoo")
kubectl logs "$POD_ID"
}
function get-pods() {
kubectl get pods
}
function logs-haproxy-ingress() {
POD_ID=$(kubectl get pods -o=name -n haproxy-ingress | grep "haproxy-ingress")
kubectl logs "$POD_ID" -n haproxy-ingress
}
function logs-ingress-nginx() {
POD_ID=$(kubectl get pods -o=name -n ingress-nginx | grep "ingress-nginx")
kubectl logs "$POD_ID" -n ingress-nginx
}
# Helm Commands
function template-manifest() {
template-values "$2"
echo "Template chart $1 into manifest.yaml."
if [[ "$1" =~ (hugo|odoo|postgres) ]]; then
helm --debug template "$1" "$1" \
--values ./values.yaml > manifest.yaml
elif [ "$1" = "clusterIssuer" ]; then
helm --debug template clusterIssuer clusterIssuer \
--namespace cert-manager \
--values ./values.yaml > manifest.yaml
fi
}
function list() {
helm list
}
function package-repo() {
if [ -n "$1" ]; then
echo "Package $1 Helm chart."
helm package "$1"
else
echo "Package all Helm charts."
index-repo
helm package clusterIssuer
helm package postgres
helm package vshnPostgres
helm package odoo
helm package hugo
fi
}
function 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"
}
function index-repo() {
echo "Update index.yaml"
helm repo index .
}
function publish-repo() {
build-vitepress
cp *.tgz .vitepress/dist
cp index.yaml .vitepress/dist
}
function add-repos(){
echo "Add kubernetes-build."
helm repo add kubernetes-build https://kubernetes.build
echo "Add ingress-nginx."
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
echo "Add haproxy-ingress."
helm repo add haproxy-ingress https://haproxy-ingress.github.io/charts
echo "Add jetstack."
helm repo add jetstack https://charts.jetstack.io
echo "Add bitnami."
helm repo add bitnami https://charts.bitnami.com/bitnami
}
function remove-repo(){
echo "Remove the kubernetes-build repo."
helm repo remove kubernetes-build
}
if declare -f "$1" > /dev/null; then
"$1" "${@:2}"
else
case "$1" in
*)
echo "Unknown command: $1"
help
exit 1
;;
esac
fi