Files
buun-stack/clickhouse/justfile

550 lines
21 KiB
Makefile

set fallback := true
export CLICKHOUSE_NAMESPACE := env("CLICKHOUSE_NAMESPACE", "clickhouse")
export CLICKHOUSE_HOST := env("CLICKHOUSE_HOST", "")
export CLICKHOUSE_CHART_VERSION := env("CLICKHOUSE_CHART_VERSION", "0.25.5")
export CLICKHOUSE_IMAGE := env("CLICKHOUSE_IMAGE", "clickhouse/clickhouse-server:25.10")
export EXTERNAL_SECRETS_NAMESPACE := env("EXTERNAL_SECRETS_NAMESPACE", "external-secrets")
# ClickHouse resource settings
export CLICKHOUSE_MEMORY_REQUEST := env("CLICKHOUSE_MEMORY_REQUEST", "1Gi")
export CLICKHOUSE_MEMORY_LIMIT := env("CLICKHOUSE_MEMORY_LIMIT", "8Gi")
export CLICKHOUSE_CPU_REQUEST := env("CLICKHOUSE_CPU_REQUEST", "200m")
export CLICKHOUSE_CPU_LIMIT := env("CLICKHOUSE_CPU_LIMIT", "2")
# ClickHouse memory settings (bytes)
# max_server_memory_usage: Server-wide limit, should be ~75% of MEMORY_LIMIT (default: 0 = auto 90% of RAM)
export CLICKHOUSE_MAX_SERVER_MEMORY := env("CLICKHOUSE_MAX_SERVER_MEMORY", "6442450944")
# max_memory_usage: Per-query limit (default: 10GB)
export CLICKHOUSE_MAX_MEMORY_USAGE := env("CLICKHOUSE_MAX_MEMORY_USAGE", "4294967296")
# max_bytes_before_external_group_by: Spill to disk threshold for GROUP BY (default: 0 = disabled)
export CLICKHOUSE_MAX_BYTES_BEFORE_EXTERNAL_GROUP_BY := env("CLICKHOUSE_MAX_BYTES_BEFORE_EXTERNAL_GROUP_BY", "2147483648")
# max_bytes_before_external_sort: Spill to disk threshold for ORDER BY (default: 0 = disabled)
export CLICKHOUSE_MAX_BYTES_BEFORE_EXTERNAL_SORT := env("CLICKHOUSE_MAX_BYTES_BEFORE_EXTERNAL_SORT", "2147483648")
# ClickHouse log sidecar resource settings
export CLICKHOUSE_LOG_MEMORY_REQUEST := env("CLICKHOUSE_LOG_MEMORY_REQUEST", "64Mi")
export CLICKHOUSE_LOG_MEMORY_LIMIT := env("CLICKHOUSE_LOG_MEMORY_LIMIT", "128Mi")
export CLICKHOUSE_LOG_CPU_REQUEST := env("CLICKHOUSE_LOG_CPU_REQUEST", "10m")
export CLICKHOUSE_LOG_CPU_LIMIT := env("CLICKHOUSE_LOG_CPU_LIMIT", "100m")
# ClickHouse Operator resource settings
export CLICKHOUSE_OPERATOR_MEMORY_REQUEST := env("CLICKHOUSE_OPERATOR_MEMORY_REQUEST", "64Mi")
export CLICKHOUSE_OPERATOR_MEMORY_LIMIT := env("CLICKHOUSE_OPERATOR_MEMORY_LIMIT", "256Mi")
export CLICKHOUSE_OPERATOR_CPU_REQUEST := env("CLICKHOUSE_OPERATOR_CPU_REQUEST", "50m")
export CLICKHOUSE_OPERATOR_CPU_LIMIT := env("CLICKHOUSE_OPERATOR_CPU_LIMIT", "500m")
[private]
default:
@just --list --unsorted --list-submodules
# Add Helm repository
add-helm-repo:
helm repo add clickhouse-operator https://docs.altinity.com/clickhouse-operator/
helm repo update
# Remove Helm repository
remove-helm-repo:
helm repo remove clickhouse-operator
# Create ClickHouse namespace
create-namespace:
#!/bin/bash
set -euo pipefail
if ! kubectl get namespace ${CLICKHOUSE_NAMESPACE} &>/dev/null; then
kubectl create namespace ${CLICKHOUSE_NAMESPACE}
fi
kubectl label namespace ${CLICKHOUSE_NAMESPACE} \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=baseline \
pod-security.kubernetes.io/warn-version=latest \
--overwrite
# Delete ClickHouse namespace
delete-namespace:
@kubectl delete namespace ${CLICKHOUSE_NAMESPACE} --ignore-not-found
# Create ClickHouse credentials secret
create-credentials:
#!/bin/bash
set -euo pipefail
echo "Setting up ClickHouse credentials..."
# Generate admin password
ADMIN_PASSWORD=$(just utils::random-password)
if helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE} &>/dev/null; then
echo "External Secrets available. Storing credentials in Vault and creating ExternalSecret..."
just vault::put clickhouse/credentials admin="$ADMIN_PASSWORD"
gomplate -f clickhouse-credentials-external-secret.gomplate.yaml -o clickhouse-credentials-external-secret.yaml
kubectl apply -f clickhouse-credentials-external-secret.yaml
echo "Waiting for credentials secret to be ready..."
kubectl wait --for=condition=Ready externalsecret/clickhouse-credentials-external-secret \
-n ${CLICKHOUSE_NAMESPACE} --timeout=60s
else
echo "External Secrets not available. Creating Kubernetes Secret directly..."
kubectl delete secret clickhouse-credentials -n ${CLICKHOUSE_NAMESPACE} --ignore-not-found
kubectl create secret generic clickhouse-credentials -n ${CLICKHOUSE_NAMESPACE} \
--from-literal=admin="$ADMIN_PASSWORD"
echo "Credentials secret created directly in Kubernetes"
fi
echo "ClickHouse credentials setup completed"
# Delete ClickHouse credentials secret
delete-credentials-secret:
@kubectl delete secret clickhouse-credentials -n ${CLICKHOUSE_NAMESPACE} --ignore-not-found
@kubectl delete externalsecret clickhouse-credentials-external-secret -n ${CLICKHOUSE_NAMESPACE} --ignore-not-found
# Install ClickHouse
install:
#!/bin/bash
set -euo pipefail
export CLICKHOUSE_HOST=${CLICKHOUSE_HOST:-}
while [ -z "${CLICKHOUSE_HOST}" ]; do
CLICKHOUSE_HOST=$(
gum input --prompt="ClickHouse host (FQDN): " --width=100 \
--placeholder="e.g., clickhouse.example.com"
)
done
echo "Installing ClickHouse..."
just create-namespace
just install-zookeeper
just create-credentials
just add-helm-repo
gomplate -f clickhouse-operator-values.gomplate.yaml -o clickhouse-operator-values.yaml
helm upgrade --install clickhouse-operator clickhouse-operator/altinity-clickhouse-operator \
--version ${CLICKHOUSE_CHART_VERSION} -n ${CLICKHOUSE_NAMESPACE} \
-f clickhouse-operator-values.yaml --wait
gomplate -f clickhouse-installation-template.gomplate.yaml -o clickhouse-installation-template.yaml
gomplate -f clickhouse.gomplate.yaml -o clickhouse.yaml
kubectl apply -n ${CLICKHOUSE_NAMESPACE} -f ./clickhouse-installation-template.yaml
kubectl apply -n ${CLICKHOUSE_NAMESPACE} -f ./clickhouse.yaml
echo "Waiting for ClickHouse installation to be ready..."
kubectl wait --for=jsonpath='{.status.status}'=Completed \
clickhouseinstallation/clickhouse -n ${CLICKHOUSE_NAMESPACE} --timeout=600s
just setup-ingress ${CLICKHOUSE_HOST}
echo "ClickHouse installation completed successfully"
echo "ClickHouse API at: https://${CLICKHOUSE_HOST}"
# Setup ClickHouse Ingress
setup-ingress host:
#!/bin/bash
set -euo pipefail
echo "Setting up ClickHouse Ingress for ${CLICKHOUSE_HOST}..."
export CLICKHOUSE_HOST="{{ host }}"
gomplate -f clickhouse-ingress.gomplate.yaml -o clickhouse-ingress.yaml
kubectl apply -n ${CLICKHOUSE_NAMESPACE} -f clickhouse-ingress.yaml
echo "ClickHouse Ingress configured successfully"
# Uninstall ClickHouse
uninstall:
#!/bin/bash
set -euo pipefail
echo "Uninstalling ClickHouse..."
if kubectl get clickhouseinstallations.clickhouse.altinity.com \
-n ${CLICKHOUSE_NAMESPACE} &>/dev/null; then
echo "Deleting ClickHouseInstallation resources..."
kubectl delete clickhouseinstallations.clickhouse.altinity.com --all \
-n ${CLICKHOUSE_NAMESPACE} --timeout=60s --ignore-not-found || {
echo "Graceful deletion timed out, forcing finalizer removal..."
for chi in $(kubectl get clickhouseinstallations.clickhouse.altinity.com \
-n ${CLICKHOUSE_NAMESPACE} -o name 2>/dev/null); do
kubectl patch "$chi" -n ${CLICKHOUSE_NAMESPACE} \
--type='merge' -p='{"metadata":{"finalizers":null}}' || true
done
}
fi
helm uninstall clickhouse-operator -n ${CLICKHOUSE_NAMESPACE} --wait --ignore-not-found
just uninstall-zookeeper
just delete-credentials-secret
just delete-namespace
echo "ClickHouse uninstalled successfully"
# Get ClickHouse admin password
admin-password:
#!/bin/bash
set -euo pipefail
kubectl get secret clickhouse-credentials -n ${CLICKHOUSE_NAMESPACE} \
-o jsonpath='{.data.admin}' 2>/dev/null | base64 -d | tr -d '\n\r'
echo
# Connect to ClickHouse as admin (interactive)
connect-admin-interactive: check-env
@just utils::check-connection clickhouse-clickhouse.clickhouse 9000
@clickhouse client --host clickhouse-clickhouse.clickhouse --port 9000 \
--user admin --password $(just clickhouse::admin-password)
# Execute SQL as admin via kubectl exec
connect-admin:
#!/bin/bash
set -euo pipefail
ADMIN_PASSWORD=$(just admin-password)
POD_NAME=$(just find-clickhouse-pod)
kubectl exec -n ${CLICKHOUSE_NAMESPACE} "${POD_NAME}" -c clickhouse -- \
clickhouse-client --user admin --password "${ADMIN_PASSWORD}"
# Connect to ClickHouse (interactive)
connect-interactive user: check-env
@just utils::check-connection clickhouse-clickhouse.clickhouse 9000
@clickhouse client --host clickhouse-clickhouse.clickhouse --port 9000 \
--user {{ user }} --ask-password
# Execute SQL as specific user via kubectl exec
connect user password='':
#!/bin/bash
set -euo pipefail
USER="{{ user }}"
PASSWORD="{{ password }}"
if [ -z "${PASSWORD}" ]; then
PASSWORD=$(gum input --prompt="Password for ${USER}: " --password --width=100)
fi
POD_NAME=$(just find-clickhouse-pod)
kubectl exec -n ${CLICKHOUSE_NAMESPACE} "${POD_NAME}" -c clickhouse -- \
clickhouse-client --user "${USER}" --password "${PASSWORD}"
# Find ClickHouse pod name
[private]
find-clickhouse-pod:
#!/bin/bash
set -euo pipefail
for SELECTOR in \
"app=clickhouse-clickhouse" \
"app.kubernetes.io/name=clickhouse" \
"clickhouse.altinity.com/chi=clickhouse" \
"app=clickhouse"; do
POD_NAME=$(kubectl get pods -n ${CLICKHOUSE_NAMESPACE} -l "${SELECTOR}" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -n "${POD_NAME}" ]; then
echo "${POD_NAME}"
exit 0
fi
done
POD_NAME=$(kubectl get pods -n ${CLICKHOUSE_NAMESPACE} -o name | grep -i clickhouse | head -1 | cut -d'/' -f2 2>/dev/null || echo "")
if [ -n "${POD_NAME}" ]; then
echo "${POD_NAME}"
exit 0
fi
echo "No ClickHouse pods found in namespace ${CLICKHOUSE_NAMESPACE}" >&2
echo "Available pods:" >&2
kubectl get pods -n ${CLICKHOUSE_NAMESPACE} >&2
exit 1
# Execute SQL command as admin
exec-sql-admin sql='':
#!/bin/bash
set -euo pipefail
SQL="{{ sql }}"
ADMIN_PASSWORD=$(just admin-password)
POD_NAME=$(just find-clickhouse-pod)
if [ -n "${SQL}" ]; then
# Pass SQL via stdin to avoid quoting issues
echo "${SQL}" | kubectl exec -i -n ${CLICKHOUSE_NAMESPACE} "${POD_NAME}" -c clickhouse -- \
sh -c "export CLICKHOUSE_PASSWORD='${ADMIN_PASSWORD}' && clickhouse-client --user admin"
else
# Read from stdin
kubectl exec -i -n ${CLICKHOUSE_NAMESPACE} "${POD_NAME}" -c clickhouse -- \
sh -c "export CLICKHOUSE_PASSWORD='${ADMIN_PASSWORD}' && clickhouse-client --user admin"
fi
# Create ClickHouse user
create-user username='' password='':
#!/bin/bash
set -euo pipefail
USERNAME=${USERNAME:-"{{ username }}"}
PASSWORD=${PASSWORD:-"{{ password }}"}
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
if just user-exists ${USERNAME} &>/dev/null; then
echo "User ${USERNAME} already exists" >&2
exit
fi
if [ -z "${PASSWORD}" ]; then
PASSWORD=$(gum input --prompt="Password: " --password --width=100 \
--placeholder="Empty to generate a random password")
fi
if [ -z "${PASSWORD}" ]; then
PASSWORD=$(just utils::random-password)
echo "Generated random password: ${PASSWORD}"
fi
echo "Creating ClickHouse user '${USERNAME}'..."
just exec-sql-admin "CREATE USER '${USERNAME}' IDENTIFIED BY '${PASSWORD}';"
echo "User ${USERNAME} created."
# Wait a moment for ClickHouse to process the user creation
sleep 2
# Create new user for ClickHouse (deprecated - use create-user instead)
create-user-old username='' password='' database='':
#!/bin/bash
set -euo pipefail
USERNAME="{{ username }}"
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
PASSWORD="{{ password }}"
while [ -z "${PASSWORD}" ]; do
PASSWORD=$(gum input --prompt="Password: " --password --width=100)
done
DATABASE="{{ database }}"
while [ -z "${DATABASE}" ]; do
DATABASE=$(gum input --prompt="Database: " --width=100)
done
if [ "${DATABASE}" != "default" ]; then
if ! just db-exists ${DATABASE}; then
echo "Database '${DATABASE}' does not exist"
just create-db ${DATABASE}
fi
fi
echo "Creating ClickHouse user '${USERNAME}'..."
just exec-sql-admin "CREATE USER '${USERNAME}' IDENTIFIED BY '${PASSWORD}' DEFAULT DATABASE '${DATABASE}';"
# Delete ClickHouse user
delete-user username='':
#!/bin/bash
set -euo pipefail
USERNAME=${USERNAME:-"{{ username }}"}
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
if ! just user-exists ${USERNAME} &>/dev/null; then
echo "User ${USERNAME} does not exist." >&2
exit
fi
echo "Deleting ClickHouse user '${USERNAME}'..."
just exec-sql-admin "DROP USER '${USERNAME}';"
echo "User ${USERNAME} deleted."
# Grant all privileges on database to user
grant db_name='' username='':
#!/bin/bash
set -euo pipefail
DB_NAME=${DB_NAME:-"{{ db_name }}"}
USERNAME=${USERNAME:-"{{ username }}"}
while [ -z "${DB_NAME}" ]; do
DB_NAME=$(gum input --prompt="Database name: " --width=100)
done
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
if ! just db-exists ${DB_NAME}; then
echo "Database ${DB_NAME} does not exist." >&2
exit 1
fi
if ! just user-exists ${USERNAME}; then
echo "User ${USERNAME} does not exist." >&2
exit 1
fi
echo "Granting all privileges on '${DB_NAME}' to ClickHouse user '${USERNAME}'..."
just exec-sql-admin "GRANT ALL ON ${DB_NAME}.* TO '${USERNAME}';"
# Also grant INFORMATION_SCHEMA access for tools like DLT
just exec-sql-admin "GRANT SELECT ON INFORMATION_SCHEMA.* TO '${USERNAME}';"
echo "Privileges granted."
# Revoke all privileges on database from user
revoke db_name='' username='':
#!/bin/bash
set -euo pipefail
DB_NAME=${DB_NAME:-"{{ db_name }}"}
USERNAME=${USERNAME:-"{{ username }}"}
while [ -z "${DB_NAME}" ]; do
DB_NAME=$(gum input --prompt="Database name: " --width=100)
done
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
if ! just db-exists ${DB_NAME}; then
echo "Database ${DB_NAME} does not exist." >&2
exit 1
fi
if ! just user-exists ${USERNAME}; then
echo "User ${USERNAME} does not exist." >&2
exit 1
fi
echo "Revoking all privileges on '${DB_NAME}' from ClickHouse user '${USERNAME}'..."
just exec-sql-admin "REVOKE ALL ON ${DB_NAME}.* FROM '${USERNAME}';"
echo "Privileges revoked."
# Create ClickHouse database and user
create-user-and-db username='' db_name='' password='':
just create-db "{{ db_name }}"
just create-user "{{ username }}" "{{ password }}"
just grant "{{ db_name }}" "{{ username }}"
# Delete ClickHouse database and user
delete-user-and-db username='' db_name='':
#!/bin/bash
set -euo pipefail
DB_NAME=${DB_NAME:-"{{ db_name }}"}
USERNAME=${USERNAME:-"{{ username }}"}
if just db-exists ${DB_NAME} &>/dev/null; then
if just user-exists ${USERNAME} &>/dev/null; then
just revoke "${DB_NAME}" "${USERNAME}"
else
echo "User ${USERNAME} does not exist, skipping revoke."
fi
just delete-db "${DB_NAME}"
else
echo "Database ${DB_NAME} does not exist, skipping database deletion."
fi
if just user-exists ${USERNAME} &>/dev/null; then
just delete-user "${USERNAME}"
else
echo "User ${USERNAME} does not exist, skipping user deletion."
fi
echo "Cleanup completed."
# List all users in ClickHouse
list-users:
#!/bin/bash
set -euo pipefail
just exec-sql-admin "SHOW USERS;"
# Check if user exists in ClickHouse
[no-exit-message]
user-exists username='':
#!/bin/bash
set -euo pipefail
USERNAME="{{ username }}"
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
# Use exec-sql-admin to check if user exists
result=$(just exec-sql-admin "SELECT name FROM system.users WHERE name = '${USERNAME}';" 2>/dev/null || echo "")
if [[ -n "$result" && "$result" == *"$USERNAME"* ]]; then
echo "User '$USERNAME' exists."
exit 0
else
echo "User '$USERNAME' does not exist."
exit 1
fi
# Create DB in ClickHouse
create-db database='default':
#!/bin/bash
set -euo pipefail
DATABASE="{{ database }}"
while [ -z "${DATABASE}" ]; do
DATABASE=$(gum input --prompt="Database: " --width=100)
done
echo "Creating ClickHouse database '${DATABASE}'..."
just exec-sql-admin "CREATE DATABASE IF NOT EXISTS ${DATABASE};"
# Check if DB exists in ClickHouse
[no-exit-message]
db-exists database='default':
#!/bin/bash
set -euo pipefail
DATABASE="{{ database }}"
while [ -z "${DATABASE}" ]; do
DATABASE=$(gum input --prompt="Database: " --width=100)
done
result=$(just exec-sql-admin "SHOW DATABASES LIKE '${DATABASE}';" 2>/dev/null || echo "")
if [[ -n "$result" && "$result" == *"$DATABASE"* ]]; then
echo "Database '$DATABASE' exists."
exit 0
else
echo "Database '$DATABASE' does not exist."
exit 1
fi
# Print user privilege
user-privilege username='':
#!/bin/bash
set -euo pipefail
USERNAME="{{ username }}"
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
just exec-sql-admin "SHOW GRANTS FOR '${USERNAME}';"
# Change user password
change-password username='' password='':
#!/bin/bash
set -euo pipefail
USERNAME="${USERNAME:-"{{ username }}"}"
PASSWORD="${PASSWORD:-"{{ password }}"}"
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
while [ -z "${PASSWORD}" ]; do
PASSWORD=$(gum input --prompt="New Password: " --password --width=100)
done
if ! just user-exists ${USERNAME} &>/dev/null; then
echo "User ${USERNAME} does not exist." >&2
exit 1
fi
echo "Changing password for user '${USERNAME}'..."
just exec-sql-admin "ALTER USER '${USERNAME}' IDENTIFIED BY '${PASSWORD}';"
echo "Password changed for user '${USERNAME}'"
# Grant admin privileges to user
grant-admin username='':
#!/bin/bash
set -euo pipefail
USERNAME="${USERNAME:-"{{ username }}"}"
while [ -z "${USERNAME}" ]; do
USERNAME=$(gum input --prompt="Username: " --width=100)
done
for i in {1..5}; do
if just user-exists ${USERNAME} &>/dev/null; then
break
fi
if [ $i -lt 5 ]; then
echo "User ${USERNAME} not found, retrying in 1 second... (attempt $i/5)"
sleep 1
else
echo "User ${USERNAME} does not exist after 5 attempts." >&2
exit 1
fi
done
echo "Granting admin privileges to user '${USERNAME}'..."
just exec-sql-admin "
GRANT SOURCES ON *.* TO '${USERNAME}' WITH GRANT OPTION;
GRANT TABLE ENGINE ON * TO '${USERNAME}' WITH GRANT OPTION;
GRANT CHECK, SHOW, SELECT, INSERT, ALTER, CREATE, DROP, UNDROP TABLE, TRUNCATE, OPTIMIZE, BACKUP, KILL QUERY, KILL TRANSACTION, MOVE PARTITION BETWEEN SHARDS, ROLE ADMIN, CREATE ROW POLICY, ALTER ROW POLICY, DROP ROW POLICY, CREATE QUOTA, ALTER QUOTA, DROP QUOTA, CREATE SETTINGS PROFILE, ALTER SETTINGS PROFILE, DROP SETTINGS PROFILE, ALLOW SQL SECURITY NONE, SHOW ACCESS, SYSTEM, dictGet, displaySecretsInShowAndSelect, INTROSPECTION, CLUSTER, FILE, URL, REMOTE, MONGO, REDIS, MYSQL, POSTGRES, SQLITE, ODBC, JDBC, HDFS, S3, HIVE, AZURE, KAFKA, NATS, RABBITMQ, SOURCES ON *.* TO '${USERNAME}' WITH GRANT OPTION;
GRANT SET DEFINER ON * TO '${USERNAME}' WITH GRANT OPTION;
GRANT CREATE USER, ALTER USER, DROP USER, CREATE ROLE, ALTER ROLE, DROP ROLE ON * TO '${USERNAME}' WITH GRANT OPTION;
"
echo "Admin privileges granted to user '${USERNAME}'"
# Install ZooKeeper
install-zookeeper:
#!/bin/bash
set -euo pipefail
if ! kubectl get namespace zookeeper &>/dev/null; then
kubectl create namespace zookeeper
fi
kubectl apply -n ${CLICKHOUSE_NAMESPACE} -f ./zookeeper.yaml
# Uninstall ZooKeeper
uninstall-zookeeper:
kubectl delete -n ${CLICKHOUSE_NAMESPACE} -f ./zookeeper.yaml --ignore-not-found
# Clean up ClickHouse resources
cleanup:
#!/bin/bash
set -euo pipefail
echo "This will delete all ClickHouse resources and secrets."
if gum confirm "Are you sure you want to proceed?"; then
echo "Cleaning up ClickHouse resources..."
just vault::delete clickhouse/credentials || true
echo "Cleanup completed"
else
echo "Cleanup cancelled"
fi
# Check the environment
[private]
check-env:
#!/bin/bash
set -euo pipefail
if ! command -v clickhouse &>/dev/null; then
echo "clickhouse CLI is not installed. Please install it first."
exit 1
fi