556 lines
22 KiB
Makefile
556 lines
22 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 (delete_volumes='false' to preserve PVCs and namespace)
|
|
uninstall delete-volumes='true':
|
|
#!/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=90s --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
|
|
if [ "{{ delete-volumes }}" = "true" ]; then
|
|
just delete-namespace
|
|
else
|
|
echo "Keeping namespace and PVCs for data preservation"
|
|
echo "PVCs in namespace '${CLICKHOUSE_NAMESPACE}':"
|
|
kubectl get pvc -n ${CLICKHOUSE_NAMESPACE}
|
|
fi
|
|
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
|