helm/dev-deploy.sh
Rama newjitsu · réplica de lectura del repositorio original.
#!/bin/bash
set -e
# Jitsu Dev K8s Deployment Script for Minikube
# Services are built inside containers via init containers
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
CHART_DIR="$SCRIPT_DIR"
DEPS_CHART_DIR="$PROJECT_ROOT/helm-deps"
DEPS_RELEASE_NAME="${DEPS_RELEASE_NAME:-jitsu-deps}"
NAMESPACE="${NAMESPACE:-default}"
RELEASE_NAME="${RELEASE_NAME:-jitsu}"
MOUNT_PATH="/project"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Ensure kubectl is using minikube context
ensure_minikube_context() {
local current_context
current_context=$(kubectl config current-context 2>/dev/null || echo "")
if [ "$current_context" != "minikube" ]; then
log_warn "kubectl context is '$current_context', switching to 'minikube'"
kubectl config use-context minikube
fi
}
# Check if minikube is running
check_minikube() {
local host_status
host_status=$(minikube status --format='{{.Host}}' 2>/dev/null || echo "")
if [ -z "$host_status" ] || [ "$host_status" = "Stopped" ] || [ "$host_status" = "Nonexistent" ]; then
log_error "Minikube is not running. Start it with: minikube start"
exit 1
fi
if [ "$host_status" != "Running" ]; then
log_warn "Minikube host status: $host_status"
fi
ensure_minikube_context
}
# Start minikube mount in background
start_mount() {
log_info "Starting minikube mount: $PROJECT_ROOT -> $MOUNT_PATH"
# Check if already mounted
if pgrep -f "minikube mount.*$PROJECT_ROOT" > /dev/null; then
log_warn "Mount already running"
return
fi
# Start mount in background
nohup minikube mount "$PROJECT_ROOT:$MOUNT_PATH" > /tmp/minikube-mount.log 2>&1 &
local mount_pid=$!
echo $mount_pid > /tmp/minikube-mount.pid
# Wait a moment and check if mount started successfully
sleep 2
if ! kill -0 $mount_pid 2>/dev/null; then
log_error "Failed to start minikube mount. Check /tmp/minikube-mount.log"
cat /tmp/minikube-mount.log
exit 1
fi
log_success "Mount started (PID: $mount_pid)"
log_info "Mount logs: /tmp/minikube-mount.log"
}
# Stop minikube mount
stop_mount() {
log_info "Stopping minikube mount..."
if [ -f /tmp/minikube-mount.pid ]; then
local pid=$(cat /tmp/minikube-mount.pid)
if kill -0 $pid 2>/dev/null; then
kill $pid
log_success "Mount stopped (PID: $pid)"
fi
rm -f /tmp/minikube-mount.pid
fi
# Also kill any other mount processes for this project
pkill -f "minikube mount.*$PROJECT_ROOT" 2>/dev/null || true
}
# Check mount status
mount_status() {
if pgrep -f "minikube mount.*$PROJECT_ROOT" > /dev/null; then
local pid=$(pgrep -f "minikube mount.*$PROJECT_ROOT")
log_success "Mount is running (PID: $pid)"
log_info "Mount: $PROJECT_ROOT -> $MOUNT_PATH"
else
log_warn "Mount is not running"
log_info "Start with: $0 mount"
fi
}
# Create namespace if it doesn't exist
ensure_namespace() {
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
log_info "Creating namespace $NAMESPACE..."
kubectl create namespace "$NAMESPACE"
fi
}
# Deploy using Helm
deploy() {
check_minikube
# Check if mount is running
if ! pgrep -f "minikube mount.*$PROJECT_ROOT" > /dev/null; then
log_warn "Minikube mount is not running. Starting it..."
start_mount
fi
# Verify mount is actually accessible inside the VM
if ! minikube ssh "test -f $MOUNT_PATH/package.json" 2>/dev/null; then
log_error "Project is not accessible at $MOUNT_PATH inside minikube VM."
log_error "The mount process may be stale. Restarting mount..."
stop_mount
sleep 1
start_mount
# Re-check after restart
sleep 3
if ! minikube ssh "test -f $MOUNT_PATH/package.json" 2>/dev/null; then
log_error "Mount still not working. Check 'minikube ssh ls $MOUNT_PATH' manually."
exit 1
fi
fi
log_success "Project mount verified (package.json accessible in VM)"
log_info "Deploying to Kubernetes..."
ensure_namespace
ensure_secrets
ensure_cache_pvcs
# Detect dependency URL changes so we can restart services that consumed
# the old values via envFrom (read once at pod start).
local dep_urls_before dep_urls_after
dep_urls_before=$(snapshot_dep_urls)
# Build helm args
local helm_args=()
# Check for custom config file (non-secret overrides)
if [ -f "$CHART_DIR/values-custom.yaml" ]; then
log_info "Using custom config from values-custom.yaml"
helm_args+=("-f" "$CHART_DIR/values-custom.yaml")
fi
deploy_deps
check_dep_urls "${helm_args[@]}" "$@"
dep_urls_after=$(snapshot_dep_urls)
helm upgrade --install "$RELEASE_NAME" "$CHART_DIR" \
--namespace "$NAMESPACE" \
--set projectRoot="$MOUNT_PATH" \
"${helm_args[@]}" \
"$@"
log_success "Deployed to namespace $NAMESPACE"
# Services read the deps URLs via envFrom at pod start — if they changed,
# a plain helm upgrade won't roll pods whose spec is unchanged.
if [ -n "$dep_urls_before" ] && [ "$dep_urls_before" != "$dep_urls_after" ]; then
log_warn "Dependency URLs changed — restarting services to pick them up"
for svc in console ingest bulker rotor profiles syncctl operator; do
kubectl get deployment "$svc" -n "$NAMESPACE" &>/dev/null && \
kubectl rollout restart deployment/"$svc" -n "$NAMESPACE" > /dev/null
done
fi
wait_for_ready
}
# Deterministic snapshot of the dependency URLs published by helm-deps —
# per-key, in fixed order, so string comparison can't produce false diffs
# from map-ordering differences. Empty when the secret doesn't exist.
snapshot_dep_urls() {
kubectl get secret jitsu-deps-urls -n "$NAMESPACE" &>/dev/null || return 0
local key out=""
for key in KAFKA_BOOTSTRAP_SERVERS DATABASE_URL CLICKHOUSE_URL MONGODB_URL; do
out="$out$key=$(kubectl get secret jitsu-deps-urls -n "$NAMESPACE" -o jsonpath="{.data.$key}" 2>/dev/null);"
done
printf '%s' "$out"
}
# Fail fast when a dependency is disabled in helm-deps but no replacement URL
# is configured — otherwise services boot with an empty KAFKA_BOOTSTRAP_SERVERS
# / DATABASE_URL / ... and crash-loop at runtime instead.
# Args: the same helm args the main-chart upgrade will use — the check renders
# the chart with them, so values files and --set overrides are both honored
# (and commented-out placeholders are not, since comments never render).
# Best-effort: a single rendered occurrence counts, so a service-scoped
# override (env.<service>.<KEY>) satisfies it even though other services
# still lack the var — use env.common.<KEY> for external deps. A miss isn't
# silent either way: the readiness wait below surfaces the failing service.
check_dep_urls() {
# Intentionally partial stack (dependency disabled AND every service that
# uses it scaled to 0)? The check can't know which services need which
# dependency, so skip it explicitly:
if [ -n "${SKIP_DEP_URL_CHECK:-}" ]; then
log_warn "SKIP_DEP_URL_CHECK is set — skipping dependency URL validation"
return 0
fi
local rendered
rendered=$(helm template "$RELEASE_NAME" "$CHART_DIR" \
--namespace "$NAMESPACE" --set projectRoot="$MOUNT_PATH" "$@" 2>/dev/null || true)
local key missing=""
for key in KAFKA_BOOTSTRAP_SERVERS DATABASE_URL CLICKHOUSE_URL MONGODB_URL; do
# provided by the deps chart?
if [ -n "$(kubectl get secret jitsu-deps-urls -n "$NAMESPACE" -o jsonpath="{.data.$key}" 2>/dev/null)" ]; then
continue
fi
# or configured as an explicit env entry with a non-empty value?
if printf '%s\n' "$rendered" | grep -A1 -E "^[[:space:]]*- name: $key\$" | grep -E "value:" | grep -qv 'value: ""'; then
continue
fi
# or carried in the legacy secret?
if [ -n "$(kubectl get secret jitsu-secrets -n "$NAMESPACE" -o jsonpath="{.data.$key}" 2>/dev/null)" ]; then
continue
fi
missing="$missing $key"
done
if [ -n "$missing" ]; then
log_error "Missing dependency configuration:$missing"
log_error "The corresponding dependency is disabled in helm-deps values, but no replacement is set."
log_error "Set env.common.<VAR> in $CHART_DIR/values-custom.yaml (see values-custom.example.yaml)."
log_error "Running an intentionally partial stack? Bypass with: SKIP_DEP_URL_CHECK=1 $0 deploy"
exit 1
fi
}
# Interactively wait for every deployed component to become ready, printing a
# checkmark as each one goes green. Overall timeout: 5 minutes (a first-ever
# build can take longer — a timeout here is a warning, not a failure).
wait_for_ready() {
log_info "Waiting for components to become ready (timeout 5m)..."
local components="postgres clickhouse mongodb kafka console ingest bulker rotor profiles syncctl operator"
local pending="" c
for c in $components; do
if kubectl get deployment "$c" -n "$NAMESPACE" &>/dev/null; then
pending="$pending $c"
fi
done
local deadline=$(( $(date +%s) + 300 ))
while :; do
local still="" desired ready
for c in $pending; do
desired=$(kubectl get deployment "$c" -n "$NAMESPACE" -o jsonpath='{.spec.replicas}' 2>/dev/null || echo 1)
ready=$(kubectl get deployment "$c" -n "$NAMESPACE" -o jsonpath='{.status.readyReplicas}' 2>/dev/null)
if [ -n "$ready" ] && [ "$ready" -ge "${desired:-1}" ]; then
echo -e " ${GREEN}✓${NC} $c"
else
still="$still $c"
fi
done
pending="$still"
if [ -z "${pending// /}" ]; then
log_success "All components are ready"
log_info "Run '$0 tunnel' in a separate terminal to expose the endpoints on localhost"
return 0
fi
if [ "$(date +%s)" -ge "$deadline" ]; then
log_warn "Timed out waiting for:$pending"
log_warn "First-time builds can take longer — they continue in the background."
log_warn "Watch with '$0 watch', inspect with '$0 build-logs <service>' or '$0 logs <service>'."
log_info "Once everything is ready, run '$0 tunnel' to expose the endpoints on localhost"
return 0
fi
sleep 5
done
}
# Build-cache PVCs, applied outside Helm on purpose: the pre-install
# install-job hook needs them to exist already, and Helm-4 hook lifecycle
# (delete-before-create, pre-install-only) proved unreliable for resources
# that must simply always be there. kubectl apply is idempotent; the PVCs
# survive uninstall so caches persist across reinstalls. Delete them with
# './dev-deploy.sh clear-cache'.
ensure_cache_pvcs() {
kubectl apply -n "$NAMESPACE" -f - > /dev/null <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: go-cache
labels:
app.kubernetes.io/part-of: jitsu
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: node-cache
labels:
app.kubernetes.io/part-of: jitsu
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
EOF
log_success "Build-cache PVCs are in place"
}
# Deploy the dependencies chart (Kafka, Postgres, ClickHouse, MongoDB) and
# wait until everything is healthy, so the main services never start against
# missing dependencies.
deploy_deps() {
log_info "Deploying dependencies (helm-deps) and waiting for them to become healthy..."
local helm_args=()
if [ -f "$DEPS_CHART_DIR/values-custom.yaml" ]; then
log_info "Using custom deps config from helm-deps/values-custom.yaml"
helm_args+=("-f" "$DEPS_CHART_DIR/values-custom.yaml")
fi
helm upgrade --install "$DEPS_RELEASE_NAME" "$DEPS_CHART_DIR" \
--namespace "$NAMESPACE" \
--wait --timeout 10m \
"${helm_args[@]}"
log_success "Dependencies are healthy"
}
# Restart all pods (triggers rebuild via init containers)
restart_pods() {
log_info "Restarting pods (will trigger rebuild)..."
local deployments=$(kubectl get deployments -n "$NAMESPACE" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null || echo "")
if [ -z "$deployments" ]; then
log_warn "No deployments found in namespace $NAMESPACE"
return
fi
for deploy in $deployments; do
log_info "Restarting $deploy..."
kubectl rollout restart deployment/"$deploy" -n "$NAMESPACE"
done
log_success "All pods restarted (rebuilding in init containers)"
}
# Restart specific service (triggers rebuild)
restart_service() {
local service="$1"
if [ -z "$service" ]; then
log_error "Service name required"
exit 1
fi
log_info "Restarting $service (will trigger rebuild)..."
kubectl rollout restart deployment/"$service" -n "$NAMESPACE"
log_success "$service restarted"
}
# Show pod status
status() {
log_info "Minikube status:"
minikube status || true
echo ""
mount_status
echo ""
log_info "Pod status in namespace $NAMESPACE:"
kubectl get pods -n "$NAMESPACE" -o wide
echo ""
log_info "Services:"
kubectl get services -n "$NAMESPACE"
}
# Show logs for a service
logs() {
local service="$1"
local follow="${2:-false}"
if [ -z "$service" ]; then
log_error "Service name required"
echo "Usage: $0 logs <service> [follow]"
exit 1
fi
if [ "$follow" = "follow" ] || [ "$follow" = "-f" ]; then
kubectl logs -f -l "app.kubernetes.io/name=$service" -n "$NAMESPACE" --all-containers
else
kubectl logs -l "app.kubernetes.io/name=$service" -n "$NAMESPACE" --all-containers
fi
}
# Show init container logs (build logs)
build_logs() {
local service="$1"
if [ -z "$service" ]; then
log_error "Service name required"
echo "Usage: $0 build-logs <service>"
exit 1
fi
local pod=$(kubectl get pods -n "$NAMESPACE" -l "app.kubernetes.io/name=$service" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
if [ -z "$pod" ]; then
log_error "No pod found for service $service"
exit 1
fi
log_info "Build logs for $service (pod: $pod):"
kubectl logs -n "$NAMESPACE" "$pod" -c build 2>/dev/null || kubectl logs -n "$NAMESPACE" "$pod" -c install 2>/dev/null
}
# Uninstall the releases (services first, then dependencies)
uninstall() {
log_info "Uninstalling $RELEASE_NAME from namespace $NAMESPACE..."
helm uninstall "$RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || true
log_info "Uninstalling $DEPS_RELEASE_NAME from namespace $NAMESPACE..."
helm uninstall "$DEPS_RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || true
log_success "Uninstalled"
}
# Delete a specific pod (forces recreation)
delete_pod() {
local service="$1"
if [ -z "$service" ]; then
log_error "Service name required"
echo "Usage: $0 delete <service>"
exit 1
fi
log_info "Deleting pod for $service..."
kubectl delete pod -n "$NAMESPACE" -l "app.kubernetes.io/name=$service"
log_success "Pod deleted (will be recreated by deployment)"
}
# Stop only the child processes started by this tunnel command.
stop_tunnel() {
local pid
for pid in "$@"; do
kill "$pid" 2>/dev/null || true
done
for pid in "$@"; do
wait "$pid" 2>/dev/null || true
done
}
# A pod restart ends kubectl port-forward; reconnect while the tunnel is open.
forward_syncctl() {
local child_pid="" stopping=false
# Do not exit from a signal trap inside wait, or between launching a child
# and recording $!: finish that command before killing/reaping the child.
# Also signal an already-recorded child: TERM can arrive immediately before
# wait, when setting the flag alone would leave that next wait blocked.
trap 'stopping=true; if [ -n "$child_pid" ]; then kill "$child_pid" 2>/dev/null || true; fi' TERM
trap 'trap "" TERM; if [ -n "$child_pid" ]; then stop_tunnel "$child_pid"; fi' EXIT
while true; do
kubectl --context minikube -n "$NAMESPACE" port-forward --address 127.0.0.1 service/syncctl 3043:3043 &
child_pid=$!
if $stopping; then exit 0; fi
wait "$child_pid" || true
if $stopping; then exit 0; fi
child_pid=""
log_warn "Syncctl port-forward stopped; retrying in 2 seconds (check errors above)."
sleep 2 &
child_pid=$!
if $stopping; then exit 0; fi
wait "$child_pid" || true
if $stopping; then exit 0; fi
child_pid=""
done
}
# Start the LoadBalancer tunnel and the loopback-only syncctl forward together.
tunnel() {
log_info "Starting minikube tunnel (requires sudo)..."
log_info "Services will be accessible at:"
echo ""
echo " Console: http://localhost:3000"
echo " Ingest: http://localhost:3049"
echo " Bulker: http://localhost:3042"
echo " Rotor: http://localhost:3401"
echo " Postgres: localhost:5432 (postgres / see helm-deps/values.yaml postgres.password)"
echo " ClickHouse: http://localhost:8123 (default / see helm-deps/values.yaml clickhouse.password)"
echo " MongoDB: localhost:27017 (admin / see helm-deps/values.yaml mongodb.password)"
echo " Kafka: localhost:19092"
echo " Syncctl: http://127.0.0.1:3043 (loopback port-forward)"
echo ""
log_info "Syncctl stays cluster-internal; forwarding its HTTP port to loopback only."
log_info "Press Ctrl+C to stop both tunnels"
forward_syncctl &
local syncctl_pid=$!
# Capture PID values now: local variables no longer exist at script exit.
trap "stop_tunnel $syncctl_pid" EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
# Preserve stdin for minikube/sudo prompts while allowing signal handling in wait.
minikube tunnel <&0 &
local tunnel_pid=$!
trap "stop_tunnel $syncctl_pid $tunnel_pid" EXIT
wait "$tunnel_pid"
}
# Show URLs for exposed services
expose() {
log_info "LoadBalancer services (requires 'minikube tunnel' running):"
echo ""
echo " Console: http://localhost:3000"
echo " Ingest: http://localhost:3049"
echo " Bulker: http://localhost:3042"
echo " Rotor: http://localhost:3401"
echo " Postgres: localhost:5432"
echo " ClickHouse: http://localhost:8123"
echo " MongoDB: localhost:27017"
echo " Kafka: localhost:19092"
echo ""
log_info "The dev tunnel also forwards syncctl to http://127.0.0.1:3043. To forward it separately:"
echo " kubectl --context minikube -n '$NAMESPACE' port-forward --address 127.0.0.1 service/syncctl 3043:3043"
# Check if tunnel might be needed
local ingest_ip=$(kubectl get svc -n "$NAMESPACE" ingest -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null)
if [ -z "$ingest_ip" ]; then
log_warn "LoadBalancer IPs not assigned. Run './dev-deploy.sh tunnel' in another terminal"
else
log_success "Tunnel is running. Services are accessible."
fi
}
# Ensure the jitsu-secrets Secret exists with an AUTH_TOKEN and its derived
# keys. Idempotent: reuses the existing token when present so a redeploy
# doesn't invalidate running services; generates a fresh one otherwise.
# Connection URLs (DATABASE_URL, CLICKHOUSE_URL, MONGODB_URL) are no longer
# stored here — the chart computes them for the in-cluster dependencies, and
# explicit `env` entries take precedence over this secret's envFrom values.
ensure_secrets() {
local auth_token=""
if kubectl get secret jitsu-secrets -n "$NAMESPACE" &>/dev/null; then
auth_token=$(kubectl get secret jitsu-secrets -n "$NAMESPACE" \
-o jsonpath='{.data.RAW_AUTH_TOKENS}' 2>/dev/null | base64 --decode || true)
fi
if [ -z "$auth_token" ]; then
auth_token=$(openssl rand -hex 16)
log_info "Generated new AUTH_TOKEN for inter-service communication"
else
log_info "Reusing existing AUTH_TOKEN from secret 'jitsu-secrets'"
fi
# Build the payload with --from-literal so arbitrary token characters are
# escaped by kubectl instead of being interpolated into hand-built JSON.
local literals=(
--from-literal="RAW_AUTH_TOKENS=$auth_token"
--from-literal="BULKER_AUTH_KEY=$auth_token"
--from-literal="ROTOR_AUTH_KEY=$auth_token"
--from-literal="SYNCCTL_AUTH_KEY=$auth_token"
--from-literal="CONSOLE_RAW_AUTH_TOKENS=$auth_token"
--from-literal="REPOSITORY_AUTH_TOKEN=service-admin-account:$auth_token"
--from-literal="CONSOLE_TOKEN=service-admin-account:$auth_token"
--from-literal="CONFIG_SOURCE_HTTP_AUTH_TOKEN=service-admin-account:$auth_token"
)
if kubectl get secret jitsu-secrets -n "$NAMESPACE" &>/dev/null; then
# merge-patch so any extra keys a user added to the secret
# (e.g. legacy connection URLs) are preserved
kubectl create secret generic jitsu-secrets --namespace "$NAMESPACE" \
"${literals[@]}" --dry-run=client -o json \
| kubectl patch secret jitsu-secrets -n "$NAMESPACE" --type=merge --patch-file=/dev/stdin > /dev/null
else
kubectl create secret generic jitsu-secrets --namespace "$NAMESPACE" "${literals[@]}" > /dev/null
fi
log_success "Secret 'jitsu-secrets' is configured"
}
# Show secrets status
secrets_status() {
check_minikube
if kubectl get secret jitsu-secrets -n "$NAMESPACE" &>/dev/null; then
log_success "Secret 'jitsu-secrets' exists in namespace $NAMESPACE"
echo ""
log_info "Configured keys:"
kubectl get secret jitsu-secrets -n "$NAMESPACE" -o jsonpath='{.data}' | \
grep -o '"[^"]*":' | tr -d '":' | sed 's/^/ - /'
else
log_warn "Secret 'jitsu-secrets' not found in namespace $NAMESPACE"
log_info "It is generated automatically by '$0 deploy'"
fi
}
# Apply the console Prisma schema to Postgres.
# Every deploy runs this automatically (db-push hook Job); this command
# applies schema changes on demand without a full deploy.
db_push() {
log_info "Applying console Prisma schema (prisma db push)..."
if ! kubectl get deployment console -n "$NAMESPACE" &>/dev/null; then
log_error "No in-cluster console deployment (scaling.console.replicas: 0?)."
log_error "Run '$0 deploy' instead — the db-push hook applies the schema on every deploy."
exit 1
fi
if ! kubectl exec -n "$NAMESPACE" deploy/console -- npx prisma db push; then
log_error "db push failed. Is the console pod running? (kubectl get pods -n $NAMESPACE)"
exit 1
fi
log_success "Schema applied"
}
# Clear build caches
clear_cache() {
local cache_type="${1:-all}"
case "$cache_type" in
go)
log_info "Clearing Go build cache..."
kubectl delete pvc -n "$NAMESPACE" go-cache 2>/dev/null || true
;;
node)
log_info "Clearing Node build cache..."
kubectl delete pvc -n "$NAMESPACE" node-cache 2>/dev/null || true
;;
all)
log_info "Clearing all build caches..."
kubectl delete pvc -n "$NAMESPACE" go-cache node-cache 2>/dev/null || true
;;
*)
log_error "Unknown cache type: $cache_type"
echo "Usage: $0 clear-cache [go|node|all]"
exit 1
;;
esac
log_success "Cache cleared. Run 'deploy' to recreate."
}
# Port forward a service
port_forward() {
local service="$1"
local local_port="$2"
local remote_port="$3"
if [ -z "$service" ] || [ -z "$local_port" ]; then
log_error "Service name and local port required"
echo "Usage: $0 port-forward <service> <local-port> [remote-port]"
exit 1
fi
remote_port="${remote_port:-$local_port}"
log_info "Port forwarding $service: localhost:$local_port -> $remote_port"
kubectl port-forward -n "$NAMESPACE" "svc/$service" "$local_port:$remote_port"
}
# Watch pods
watch_pods() {
log_info "Watching pods in namespace $NAMESPACE (Ctrl+C to exit)..."
kubectl get pods -n "$NAMESPACE" -w
}
# Show help
show_help() {
echo "Jitsu Dev K8s Deployment Script (Minikube)"
echo ""
echo "Services are built inside containers via init containers."
echo "No local build step required - works on any OS."
echo ""
echo "Prerequisites:"
echo " - minikube installed and running (minikube start)"
echo " - helm installed"
echo ""
echo "Dependencies (Postgres, ClickHouse, MongoDB, Kafka) run in-cluster via"
echo "the separate ../helm-deps chart: 'deploy' installs it first and waits"
echo "for it to be healthy before deploying the services. Secrets (AUTH_TOKEN)"
echo "are generated automatically on deploy."
echo ""
echo "Usage: $0 <command> [options]"
echo ""
echo "Commands:"
echo " deploy Deploy deps chart (waits for healthy), then services chart"
echo " secrets (Re)apply auto-generated secrets (also done by deploy)"
echo " secrets-status Show secrets configuration status"
echo " mount Start minikube mount (project -> /project)"
echo " mount-stop Stop minikube mount"
echo " restart Restart all pods (triggers rebuild)"
echo " restart <service> Restart specific service (triggers rebuild)"
echo " status Show minikube, mount, pod and service status"
echo " watch Watch pod status"
echo " logs <service> Show logs for a service"
echo " logs <service> -f Follow logs for a service"
echo " build-logs <svc> Show build/init container logs"
echo " port-forward Port forward a service"
echo " delete <service> Delete pod (forces full recreation with rebuild)"
echo " db-push Apply console Prisma schema (also runs on every deploy)"
echo " clear-cache [type] Clear build caches (go|node|all, default: all)"
echo " expose Show URLs for externally accessible services"
echo " tunnel Start minikube tunnel and loopback syncctl port-forward"
echo " uninstall Uninstall the Helm release"
echo " help Show this help"
echo ""
echo "Environment variables:"
echo " NAMESPACE Kubernetes namespace (default: default)"
echo " RELEASE_NAME Helm release name (default: jitsu)"
echo ""
echo "Examples:"
echo " $0 deploy # Deploy (starts mount if needed)"
echo " $0 status # Check everything"
echo " $0 restart ingest # Restart ingest (triggers rebuild)"
echo " $0 build-logs ingest # Show build logs for ingest"
echo " $0 logs rotor -f # Follow rotor logs"
echo " $0 delete rotor # Delete rotor pod (triggers full rebuild)"
echo " $0 port-forward ingest 3049 # Port forward ingest"
}
# Main — ensure minikube context for all commands that touch kubectl
case "${1:-help}" in
help|--help|-h)
show_help
exit 0
;;
esac
ensure_minikube_context
case "${1:-help}" in
secrets)
log_info "Note: secrets are configured automatically during 'deploy' — this command just (re)applies them."
check_minikube
ensure_namespace
ensure_secrets
;;
secrets-status)
secrets_status
;;
deploy)
shift
deploy "$@"
;;
mount)
check_minikube
start_mount
;;
mount-stop)
stop_mount
;;
restart)
if [ -n "$2" ]; then
restart_service "$2"
else
restart_pods
fi
;;
status)
status
;;
watch)
watch_pods
;;
logs)
logs "$2" "$3"
;;
build-logs)
build_logs "$2"
;;
port-forward)
port_forward "$2" "$3" "$4"
;;
uninstall)
uninstall
;;
delete)
delete_pod "$2"
;;
db-push)
db_push
;;
clear-cache)
clear_cache "$2"
;;
expose)
expose
;;
tunnel)
tunnel
;;
*)
log_error "Unknown command: $1"
show_help
exit 1
;;
esac