#!/bin/bash set -euo pipefail # LakehouseCat Installer for macOS and Linux # Usage: curl -fsSL https://install.lakehousecat.com | bash VERSION="0.1.0" # Colors BOLD='\033[1m' GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Configuration HELM_REPO_URL="https://charts.lakehousecat.com" HELM_REPO_NAME="lakehousecat" CHART_NAME="lakehousecat-operator" RELEASE_NAME="lakehousecat" NAMESPACE="lakehousecat-system" # Flags DRY_RUN=${LAKEHOUSECAT_DRY_RUN:-0} SKIP_DOCKER_CHECK=${LAKEHOUSECAT_SKIP_DOCKER_CHECK:-0} NON_INTERACTIVE=${LAKEHOUSECAT_NON_INTERACTIVE:-0} VERBOSE=${LAKEHOUSECAT_VERBOSE:-0} print_banner() { echo "" echo -e "${CYAN}${BOLD}" echo " ╔═══════════════════════════════════════════════════════╗" echo " ║ ║" echo " ║ 🚀 LakehouseCat Installer v${VERSION} ║" echo " ║ ║" echo " ║ AI-powered Data Platform for Kubernetes ║" echo " ║ ║" echo " ╚═══════════════════════════════════════════════════════╝" echo -e "${NC}" echo "" } ui_info() { echo -e "${BLUE}ℹ${NC} $*" } ui_success() { echo -e "${GREEN}✓${NC} $*" } ui_warn() { echo -e "${YELLOW}⚠${NC} $*" } ui_error() { echo -e "${RED}✗${NC} $*" >&2 } ui_step() { echo "" echo -e "${BOLD}$*${NC}" } detect_os() { OS="unknown" if [[ "$OSTYPE" == "darwin"* ]]; then OS="macos" elif [[ "$OSTYPE" == "linux-gnu"* ]] || [[ -n "${WSL_DISTRO_NAME:-}" ]]; then OS="linux" fi if [[ "$OS" == "unknown" ]]; then ui_error "Unsupported operating system: $OSTYPE" echo "" echo "LakehouseCat installer supports:" echo " • macOS (Intel and Apple Silicon)" echo " • Linux (x86_64 and ARM64)" echo " • Windows via WSL2" echo "" exit 1 fi ui_success "Detected OS: $OS" } detect_arch() { ARCH=$(uname -m) case "$ARCH" in x86_64|amd64) ARCH="amd64" ;; arm64|aarch64) ARCH="arm64" ;; *) ui_error "Unsupported architecture: $ARCH" echo "Supported architectures: x86_64 (amd64), arm64 (aarch64)" exit 1 ;; esac ui_success "Detected architecture: $ARCH" } check_command() { local cmd="$1" command -v "$cmd" &> /dev/null } check_docker() { ui_step "[1/7] Checking Docker Desktop..." if [[ "$SKIP_DOCKER_CHECK" == "1" ]]; then ui_info "Docker check skipped (LAKEHOUSECAT_SKIP_DOCKER_CHECK=1)" return 0 fi if ! check_command docker; then ui_warn "Docker not found" echo "" echo "LakehouseCat requires Docker Desktop for local development." echo "" echo "Download Docker Desktop:" if [[ "$OS" == "macos" ]]; then echo " • https://www.docker.com/products/docker-desktop/" elif [[ "$OS" == "linux" ]]; then echo " • https://docs.docker.com/desktop/install/linux-install/" fi echo "" if [[ "$NON_INTERACTIVE" == "1" ]]; then ui_error "Docker required but not found (non-interactive mode)" exit 1 fi read -p "Continue without Docker? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Installation cancelled. Please install Docker Desktop and try again." exit 1 fi ui_warn "Continuing without Docker (some features may not work)" return 0 fi # Check if Docker is running if ! docker info &> /dev/null; then ui_warn "Docker is installed but not running" echo "" echo "Please start Docker Desktop and run this installer again." echo "" if [[ "$NON_INTERACTIVE" == "1" ]]; then ui_error "Docker not running (non-interactive mode)" exit 1 fi read -p "Continue anyway? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi ui_warn "Continuing with Docker not running" return 0 fi DOCKER_VERSION=$(docker --version | cut -d' ' -f3 | cut -d',' -f1) ui_success "Docker Desktop running (v${DOCKER_VERSION})" } check_kubectl() { ui_step "[2/7] Checking kubectl..." if ! check_command kubectl; then ui_error "kubectl not found" echo "" echo "kubectl is required to manage Kubernetes clusters." echo "" echo "Install kubectl:" if [[ "$OS" == "macos" ]]; then echo " brew install kubectl" elif [[ "$OS" == "linux" ]]; then echo " https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/" fi echo "" exit 1 fi KUBECTL_VERSION=$(kubectl version --client -o json 2>/dev/null | grep -o '"gitVersion":"[^"]*"' | cut -d'"' -f4 || echo "unknown") ui_success "kubectl installed (${KUBECTL_VERSION})" # Check if kubectl can connect to a cluster if kubectl cluster-info &> /dev/null; then CLUSTER_INFO=$(kubectl config current-context 2>/dev/null || echo "unknown") ui_success "Connected to cluster: ${CLUSTER_INFO}" else ui_warn "No Kubernetes cluster configured" echo "" echo "Please ensure you have a Kubernetes cluster running." echo "For local development, you can use:" echo " • Docker Desktop Kubernetes (recommended)" echo " • Minikube" echo " • kind" echo "" if [[ "$NON_INTERACTIVE" == "1" ]]; then ui_error "No Kubernetes cluster available (non-interactive mode)" exit 1 fi read -p "Continue without cluster connection? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi ui_warn "Continuing without cluster connection" fi } check_helm() { ui_step "[3/7] Checking Helm..." if ! check_command helm; then ui_error "Helm not found" echo "" echo "Helm is required to install LakehouseCat." echo "" echo "Install Helm:" if [[ "$OS" == "macos" ]]; then echo " brew install helm" elif [[ "$OS" == "linux" ]]; then echo " curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash" fi echo "" exit 1 fi HELM_VERSION=$(helm version --short 2>/dev/null | cut -d'v' -f2 | cut -d'+' -f1 || echo "unknown") ui_success "Helm installed (v${HELM_VERSION})" } setup_helm_repo() { ui_step "[4/7] Setting up Helm repository..." # Check if repo already exists if helm repo list 2>/dev/null | grep -q "^${HELM_REPO_NAME}"; then ui_info "Helm repository already added, updating..." if [[ "$DRY_RUN" == "0" ]]; then helm repo update "${HELM_REPO_NAME}" &> /dev/null fi else ui_info "Adding Helm repository: ${HELM_REPO_URL}" if [[ "$DRY_RUN" == "0" ]]; then helm repo add "${HELM_REPO_NAME}" "${HELM_REPO_URL}" &> /dev/null fi fi ui_success "Helm repository configured" # Search for chart if helm search repo "${HELM_REPO_NAME}/${CHART_NAME}" &> /dev/null; then CHART_VERSION=$(helm search repo "${HELM_REPO_NAME}/${CHART_NAME}" -o json | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4) ui_success "Found chart: ${CHART_NAME} (v${CHART_VERSION})" else ui_error "Chart not found: ${HELM_REPO_NAME}/${CHART_NAME}" exit 1 fi } install_lakehousecat() { ui_step "[5/7] Installing LakehouseCat..." # Check if already installed if helm list -n "${NAMESPACE}" 2>/dev/null | grep -q "^${RELEASE_NAME}"; then ui_warn "LakehouseCat is already installed" if [[ "$NON_INTERACTIVE" == "1" ]]; then ui_info "Skipping installation (already installed, non-interactive mode)" return 0 fi echo "" read -p "Upgrade existing installation? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then ui_info "Skipping installation" return 0 fi ui_info "Upgrading LakehouseCat..." if [[ "$DRY_RUN" == "0" ]]; then helm upgrade "${RELEASE_NAME}" "${HELM_REPO_NAME}/${CHART_NAME}" \ --namespace "${NAMESPACE}" \ --create-namespace \ --wait \ --timeout 5m || { ui_error "Upgrade failed" exit 1 } fi ui_success "LakehouseCat upgraded successfully" return 0 fi # Create namespace if [[ "$DRY_RUN" == "0" ]]; then kubectl create namespace "${NAMESPACE}" &> /dev/null || true fi ui_info "Installing to namespace: ${NAMESPACE}" if [[ "$DRY_RUN" == "1" ]]; then ui_info "DRY RUN: Would install ${CHART_NAME} to ${NAMESPACE}" else helm install "${RELEASE_NAME}" "${HELM_REPO_NAME}/${CHART_NAME}" \ --namespace "${NAMESPACE}" \ --create-namespace \ --wait \ --timeout 5m || { ui_error "Installation failed" echo "" echo "Check logs:" echo " kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/name=${CHART_NAME}" echo "" exit 1 } fi ui_success "LakehouseCat installed successfully" } verify_installation() { ui_step "[6/7] Verifying installation..." if [[ "$DRY_RUN" == "1" ]]; then ui_info "DRY RUN: Skipping verification" return 0 fi # Check if pods are running ui_info "Checking pods in namespace ${NAMESPACE}..." sleep 2 POD_STATUS=$(kubectl get pods -n "${NAMESPACE}" -o json 2>/dev/null | grep -o '"phase":"[^"]*"' | head -1 | cut -d'"' -f4 || echo "unknown") if [[ "$POD_STATUS" == "Running" ]]; then ui_success "Pods are running" else ui_warn "Pods status: ${POD_STATUS}" echo "" echo "Check pod status:" echo " kubectl get pods -n ${NAMESPACE}" echo "" fi # Check helm release if helm list -n "${NAMESPACE}" 2>/dev/null | grep -q "^${RELEASE_NAME}.*deployed"; then ui_success "Helm release deployed" else ui_warn "Helm release status check failed" fi } show_next_steps() { ui_step "[7/7] Installation complete!" echo "" echo -e "${GREEN}${BOLD}✓ LakehouseCat has been installed successfully!${NC}" echo "" echo -e "${BOLD}Next steps:${NC}" echo "" echo "1. Check the installation:" echo -e " ${CYAN}kubectl get pods -n ${NAMESPACE}${NC}" echo "" echo "2. View operator logs:" echo -e " ${CYAN}kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/name=${CHART_NAME} -f${NC}" echo "" echo "3. Access the LakehouseCat dashboard:" echo -e " ${CYAN}kubectl port-forward -n ${NAMESPACE} svc/lakehousecat-ui 8080:80${NC}" echo -e " ${CYAN}open http://localhost:8080${NC}" echo "" echo "4. Create your first PortalInstance:" echo -e " ${CYAN}kubectl apply -f https://charts.lakehousecat.com/examples/portal-instance.yaml${NC}" echo "" echo -e "${BOLD}Documentation:${NC}" echo " https://docs.lakehousecat.com" echo "" echo -e "${BOLD}Support:${NC}" echo " https://github.com/lakehousecat/lakehousecat/issues" echo "" } print_usage() { cat <