diff --git a/scripts/LOCAL_TESTING.md b/scripts/LOCAL_TESTING.md new file mode 100644 index 00000000..6b3c5a00 --- /dev/null +++ b/scripts/LOCAL_TESTING.md @@ -0,0 +1,435 @@ +# Local Testing Guide + +This guide explains how to build, deploy, test, and teardown StreamSpace in your local Kubernetes environment (e.g., Docker Desktop). + +## Prerequisites + +Before using these scripts, ensure you have: + +- **Docker Desktop** with Kubernetes enabled +- **kubectl** configured to access your local cluster +- **Helm 3+** installed +- **Git** (for version information) + +To verify your setup: + +```bash +docker info +kubectl cluster-info +helm version +``` + +## Quick Start + +### 1. Build All Images Locally + +Build all three StreamSpace Docker images (controller, API, UI): + +```bash +./scripts/local-build.sh +``` + +This will: +- Build `streamspace/streamspace-controller:local` +- Build `streamspace/streamspace-api:local` +- Build `streamspace/streamspace-ui:local` +- Tag each image with `latest` as well +- Display a summary of built images + +**Build individual components:** + +```bash +./scripts/local-build.sh controller # Build only controller +./scripts/local-build.sh api # Build only API +./scripts/local-build.sh ui # Build only UI +``` + +### 2. Deploy to Local Kubernetes + +Deploy StreamSpace to your local cluster using Helm: + +```bash +./scripts/local-deploy.sh +``` + +This will: +- Check that all required images exist +- Create the `streamspace` namespace +- Apply Custom Resource Definitions (CRDs) +- Install/upgrade the Helm release +- Wait for all pods to be ready +- Display deployment status and access instructions + +**Environment Variables:** + +```bash +# Customize namespace (default: streamspace) +NAMESPACE=my-namespace ./scripts/local-deploy.sh + +# Customize release name (default: streamspace) +RELEASE_NAME=my-release ./scripts/local-deploy.sh + +# Use different version tag (default: local) +VERSION=v0.2.0 ./scripts/local-deploy.sh +``` + +### 3. Access the Application + +After deployment, access StreamSpace: + +**UI (Web Interface):** + +```bash +kubectl port-forward -n streamspace svc/streamspace-ui 3000:80 +``` + +Then open: http://localhost:3000 + +**API Backend:** + +```bash +kubectl port-forward -n streamspace svc/streamspace-api 8000:8000 +``` + +Then access: http://localhost:8000 + +**View Logs:** + +```bash +# Controller logs +kubectl logs -n streamspace -l app.kubernetes.io/component=controller -f + +# API logs +kubectl logs -n streamspace -l app.kubernetes.io/component=api -f + +# UI logs +kubectl logs -n streamspace -l app.kubernetes.io/component=ui -f +``` + +### 4. Teardown and Cleanup + +When you're done testing, completely remove StreamSpace and clean up Docker artifacts: + +```bash +./scripts/local-teardown.sh +``` + +This will: +- Uninstall the Helm release +- Delete the namespace and all resources +- Remove Custom Resource Definitions (CRDs) +- Delete local Docker images +- Clean up dangling images and stopped containers +- Display remaining resources and disk usage + +**Auto-confirm (skip prompt):** + +```bash +AUTO_CONFIRM=true ./scripts/local-teardown.sh +``` + +**Clean build cache (aggressive cleanup):** + +```bash +CLEAN_CACHE=true ./scripts/local-teardown.sh +``` + +## Complete Development Cycle + +Here's a typical development workflow: + +```bash +# 1. Build images +./scripts/local-build.sh + +# 2. Deploy to cluster +./scripts/local-deploy.sh + +# 3. Test your changes +kubectl port-forward -n streamspace svc/streamspace-ui 3000:80 + +# 4. Make code changes, rebuild specific component +./scripts/local-build.sh api + +# 5. Upgrade deployment +./scripts/local-deploy.sh + +# 6. Repeat testing... + +# 7. When done, clean up everything +./scripts/local-teardown.sh +``` + +## Troubleshooting + +### Images Not Found + +If deployment fails with "image not found": + +```bash +# Check if images exist +docker images | grep streamspace + +# Rebuild images +./scripts/local-build.sh + +# Verify version tag matches +VERSION=local ./scripts/local-deploy.sh +``` + +### Pods Not Starting + +Check pod status and logs: + +```bash +# Check pod status +kubectl get pods -n streamspace + +# Describe pod for events +kubectl describe pod -n streamspace + +# View pod logs +kubectl logs -n streamspace +``` + +### Helm Installation Fails + +Check Helm release status: + +```bash +# List Helm releases +helm list -n streamspace + +# Check release status +helm status streamspace -n streamspace + +# Uninstall and retry +helm uninstall streamspace -n streamspace +./scripts/local-deploy.sh +``` + +### Namespace Stuck in Terminating + +If namespace deletion hangs: + +```bash +# Force delete namespace (use with caution) +kubectl get namespace streamspace -o json \ + | jq '.spec.finalizers = []' \ + | kubectl replace --raw /api/v1/namespaces/streamspace/finalize -f - +``` + +### Docker Out of Disk Space + +Clean up Docker resources: + +```bash +# Remove all unused images, containers, networks +docker system prune -a + +# Or use aggressive cleanup +CLEAN_CACHE=true ./scripts/local-teardown.sh +docker system prune -a --volumes +``` + +## Advanced Usage + +### Using Make Targets + +The root `Makefile` also provides targets for local development: + +```bash +# Build all components +make docker-build + +# Deploy with Helm +make helm-install + +# Uninstall +make helm-uninstall + +# Clean Docker images +make clean-docker +``` + +### Custom Helm Values + +Deploy with custom Helm values: + +```bash +# Create custom values file +cat > custom-values.yaml <` +3. Check pod status: `kubectl describe pod -n streamspace ` +4. Open an issue on GitHub with detailed error messages + +## License + +MIT License - See LICENSE file for details diff --git a/scripts/local-build.sh b/scripts/local-build.sh new file mode 100755 index 00000000..2ce9611e --- /dev/null +++ b/scripts/local-build.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env bash +# +# local-build.sh - Build all StreamSpace Docker images locally +# +# This script builds the controller, API, and UI Docker images for local testing. +# Images are tagged with 'local' and 'latest' tags for easy identification. +# + +set -euo pipefail + +# Colors for output +COLOR_RESET='\033[0m' +COLOR_BOLD='\033[1m' +COLOR_GREEN='\033[32m' +COLOR_YELLOW='\033[33m' +COLOR_BLUE='\033[34m' +COLOR_RED='\033[31m' + +# Project configuration +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VERSION="${VERSION:-local}" +GIT_COMMIT="${GIT_COMMIT:-$(git -C "$PROJECT_ROOT" rev-parse --short HEAD 2>/dev/null || echo "unknown")}" +BUILD_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + +# Image names (matching Helm chart expectations) +CONTROLLER_IMAGE="streamspace/streamspace-controller" +API_IMAGE="streamspace/streamspace-api" +UI_IMAGE="streamspace/streamspace-ui" + +# Build arguments +BUILD_ARGS="--build-arg VERSION=${VERSION} --build-arg COMMIT=${GIT_COMMIT} --build-arg BUILD_DATE=${BUILD_DATE}" + +# Helper functions +log() { + echo -e "${COLOR_BOLD}==>${COLOR_RESET} $*" +} + +log_success() { + echo -e "${COLOR_GREEN}✓${COLOR_RESET} $*" +} + +log_error() { + echo -e "${COLOR_RED}✗${COLOR_RESET} $*" >&2 +} + +log_info() { + echo -e "${COLOR_BLUE}→${COLOR_RESET} $*" +} + +log_warning() { + echo -e "${COLOR_YELLOW}⚠${COLOR_RESET} $*" +} + +# Check prerequisites +check_prerequisites() { + log "Checking prerequisites..." + + if ! command -v docker &> /dev/null; then + log_error "Docker is not installed or not in PATH" + exit 1 + fi + + if ! docker info &> /dev/null; then + log_error "Docker daemon is not running" + exit 1 + fi + + log_success "Docker is available and running" +} + +# Build controller image +build_controller() { + log "Building controller image..." + log_info "Image: ${CONTROLLER_IMAGE}:${VERSION}" + log_info "Context: ${PROJECT_ROOT}/controller" + + docker build ${BUILD_ARGS} \ + -t "${CONTROLLER_IMAGE}:${VERSION}" \ + -t "${CONTROLLER_IMAGE}:latest" \ + -f "${PROJECT_ROOT}/controller/Dockerfile" \ + "${PROJECT_ROOT}/controller/" + + log_success "Controller image built successfully" +} + +# Build API image +build_api() { + log "Building API image..." + log_info "Image: ${API_IMAGE}:${VERSION}" + log_info "Context: ${PROJECT_ROOT}/api" + + docker build ${BUILD_ARGS} \ + -t "${API_IMAGE}:${VERSION}" \ + -t "${API_IMAGE}:latest" \ + -f "${PROJECT_ROOT}/api/Dockerfile" \ + "${PROJECT_ROOT}/api/" + + log_success "API image built successfully" +} + +# Build UI image +build_ui() { + log "Building UI image..." + log_info "Image: ${UI_IMAGE}:${VERSION}" + log_info "Context: ${PROJECT_ROOT}/ui" + + docker build ${BUILD_ARGS} \ + -t "${UI_IMAGE}:${VERSION}" \ + -t "${UI_IMAGE}:latest" \ + -f "${PROJECT_ROOT}/ui/Dockerfile" \ + "${PROJECT_ROOT}/ui/" + + log_success "UI image built successfully" +} + +# List built images +list_images() { + log "Built images:" + echo "" + docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}" | \ + grep -E "REPOSITORY|streamspace/streamspace-(controller|api|ui)" || true + echo "" +} + +# Main execution +main() { + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo -e "${COLOR_BOLD} StreamSpace Local Build${COLOR_RESET}" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo "" + echo -e "${COLOR_BLUE}Version:${COLOR_RESET} ${VERSION}" + echo -e "${COLOR_BLUE}Commit:${COLOR_RESET} ${GIT_COMMIT}" + echo -e "${COLOR_BLUE}Build Date:${COLOR_RESET} ${BUILD_DATE}" + echo "" + + check_prerequisites + + # Allow building individual components + if [ $# -eq 0 ]; then + # Build all components + build_controller + build_api + build_ui + else + # Build specific components + for component in "$@"; do + case "$component" in + controller) + build_controller + ;; + api) + build_api + ;; + ui) + build_ui + ;; + *) + log_error "Unknown component: $component" + log_info "Valid components: controller, api, ui" + exit 1 + ;; + esac + done + fi + + list_images + + echo "" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + log_success "All images built successfully!" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo "" + log_info "Next steps:" + echo " 1. Deploy to local cluster: ./scripts/local-deploy.sh" + echo " 2. Access the UI via port-forward or ingress" + echo " 3. Teardown when done: ./scripts/local-teardown.sh" + echo "" +} + +# Run main function +main "$@" diff --git a/scripts/local-deploy.sh b/scripts/local-deploy.sh new file mode 100755 index 00000000..833e3ef9 --- /dev/null +++ b/scripts/local-deploy.sh @@ -0,0 +1,256 @@ +#!/usr/bin/env bash +# +# local-deploy.sh - Deploy StreamSpace to local Kubernetes cluster +# +# This script deploys StreamSpace to a local Kubernetes cluster (e.g., Docker Desktop K8s). +# It uses the locally built images and configures the environment for testing. +# + +set -euo pipefail + +# Colors for output +COLOR_RESET='\033[0m' +COLOR_BOLD='\033[1m' +COLOR_GREEN='\033[32m' +COLOR_YELLOW='\033[33m' +COLOR_BLUE='\033[34m' +COLOR_RED='\033[31m' + +# Project configuration +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +NAMESPACE="${NAMESPACE:-streamspace}" +RELEASE_NAME="${RELEASE_NAME:-streamspace}" +VERSION="${VERSION:-local}" + +# Helm chart location +CHART_PATH="${PROJECT_ROOT}/chart" + +# Helper functions +log() { + echo -e "${COLOR_BOLD}==>${COLOR_RESET} $*" +} + +log_success() { + echo -e "${COLOR_GREEN}✓${COLOR_RESET} $*" +} + +log_error() { + echo -e "${COLOR_RED}✗${COLOR_RESET} $*" >&2 +} + +log_info() { + echo -e "${COLOR_BLUE}→${COLOR_RESET} $*" +} + +log_warning() { + echo -e "${COLOR_YELLOW}⚠${COLOR_RESET} $*" +} + +# Check prerequisites +check_prerequisites() { + log "Checking prerequisites..." + + if ! command -v kubectl &> /dev/null; then + log_error "kubectl is not installed or not in PATH" + exit 1 + fi + + if ! command -v helm &> /dev/null; then + log_error "Helm is not installed or not in PATH" + exit 1 + fi + + if ! kubectl cluster-info &> /dev/null; then + log_error "Cannot connect to Kubernetes cluster" + log_info "Make sure your kubeconfig is properly configured" + exit 1 + fi + + local context=$(kubectl config current-context 2>/dev/null || echo "unknown") + log_success "Connected to cluster: ${context}" +} + +# Check if images exist locally +check_images() { + log "Checking for locally built images..." + + local missing_images=0 + + for image in "streamspace/streamspace-controller" "streamspace/streamspace-api" "streamspace/streamspace-ui"; do + if docker images "${image}:${VERSION}" --format "{{.Repository}}:{{.Tag}}" | grep -q "${image}:${VERSION}"; then + log_success "Found ${image}:${VERSION}" + else + log_error "Missing ${image}:${VERSION}" + missing_images=$((missing_images + 1)) + fi + done + + if [ $missing_images -gt 0 ]; then + log_error "Missing ${missing_images} image(s). Run './scripts/local-build.sh' first." + exit 1 + fi +} + +# Create namespace +create_namespace() { + log "Creating namespace: ${NAMESPACE}" + + if kubectl get namespace "${NAMESPACE}" &> /dev/null; then + log_warning "Namespace ${NAMESPACE} already exists" + else + kubectl create namespace "${NAMESPACE}" + log_success "Namespace created" + fi +} + +# Apply CRDs manually (before Helm install) +apply_crds() { + log "Applying Custom Resource Definitions..." + + kubectl apply -f "${CHART_PATH}/crds/" 2>/dev/null || { + log_warning "CRD directory not found in chart, will rely on Helm CRD installation" + return 0 + } + + log_success "CRDs applied" +} + +# Install/Upgrade with Helm +deploy_helm() { + log "Deploying StreamSpace with Helm..." + + # Check if release exists + if helm status "${RELEASE_NAME}" -n "${NAMESPACE}" &> /dev/null; then + log_info "Release exists, upgrading..." + helm upgrade "${RELEASE_NAME}" "${CHART_PATH}" \ + --namespace "${NAMESPACE}" \ + --set controller.image.tag="${VERSION}" \ + --set controller.image.pullPolicy=Never \ + --set api.image.tag="${VERSION}" \ + --set api.image.pullPolicy=Never \ + --set ui.image.tag="${VERSION}" \ + --set ui.image.pullPolicy=Never \ + --set postgresql.enabled=true \ + --set postgresql.auth.password=streamspace \ + --wait \ + --timeout 5m + else + log_info "Installing fresh release..." + helm install "${RELEASE_NAME}" "${CHART_PATH}" \ + --namespace "${NAMESPACE}" \ + --create-namespace \ + --set controller.image.tag="${VERSION}" \ + --set controller.image.pullPolicy=Never \ + --set api.image.tag="${VERSION}" \ + --set api.image.pullPolicy=Never \ + --set ui.image.tag="${VERSION}" \ + --set ui.image.pullPolicy=Never \ + --set postgresql.enabled=true \ + --set postgresql.auth.password=streamspace \ + --wait \ + --timeout 5m + fi + + log_success "Helm deployment complete" +} + +# Wait for pods to be ready +wait_for_pods() { + log "Waiting for pods to be ready..." + + local timeout=300 # 5 minutes + local elapsed=0 + local interval=5 + + while [ $elapsed -lt $timeout ]; do + local ready_pods=$(kubectl get pods -n "${NAMESPACE}" -o json | \ + jq -r '.items[] | select(.status.phase == "Running") | .metadata.name' | wc -l) + local total_pods=$(kubectl get pods -n "${NAMESPACE}" --no-headers | wc -l) + + if [ "$ready_pods" -eq "$total_pods" ] && [ "$total_pods" -gt 0 ]; then + log_success "All pods are ready" + return 0 + fi + + log_info "Waiting... (${ready_pods}/${total_pods} pods ready)" + sleep $interval + elapsed=$((elapsed + interval)) + done + + log_warning "Timeout waiting for pods to be ready" + log_info "Check pod status with: kubectl get pods -n ${NAMESPACE}" +} + +# Show deployment status +show_status() { + log "Deployment Status:" + echo "" + + log_info "Pods:" + kubectl get pods -n "${NAMESPACE}" -o wide + echo "" + + log_info "Services:" + kubectl get svc -n "${NAMESPACE}" + echo "" + + log_info "Helm Release:" + helm status "${RELEASE_NAME}" -n "${NAMESPACE}" +} + +# Show access instructions +show_access_info() { + echo "" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo -e "${COLOR_BOLD} Access Instructions${COLOR_RESET}" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo "" + + log_info "Port-forward UI (in a separate terminal):" + echo " kubectl port-forward -n ${NAMESPACE} svc/${RELEASE_NAME}-ui 3000:80" + echo " Then access: http://localhost:3000" + echo "" + + log_info "Port-forward API (in a separate terminal):" + echo " kubectl port-forward -n ${NAMESPACE} svc/${RELEASE_NAME}-api 8000:8000" + echo " Then access: http://localhost:8000" + echo "" + + log_info "View logs:" + echo " Controller: kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/component=controller -f" + echo " API: kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/component=api -f" + echo " UI: kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/component=ui -f" + echo "" + + log_info "When finished testing:" + echo " ./scripts/local-teardown.sh" + echo "" +} + +# Main execution +main() { + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo -e "${COLOR_BOLD} StreamSpace Local Deployment${COLOR_RESET}" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo "" + echo -e "${COLOR_BLUE}Namespace:${COLOR_RESET} ${NAMESPACE}" + echo -e "${COLOR_BLUE}Release:${COLOR_RESET} ${RELEASE_NAME}" + echo -e "${COLOR_BLUE}Version:${COLOR_RESET} ${VERSION}" + echo "" + + check_prerequisites + check_images + create_namespace + apply_crds + deploy_helm + wait_for_pods + show_status + show_access_info + + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + log_success "Deployment complete!" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" +} + +# Run main function +main "$@" diff --git a/scripts/local-teardown.sh b/scripts/local-teardown.sh new file mode 100755 index 00000000..c7bb68fd --- /dev/null +++ b/scripts/local-teardown.sh @@ -0,0 +1,284 @@ +#!/usr/bin/env bash +# +# local-teardown.sh - Teardown StreamSpace and cleanup Docker artifacts +# +# This script completely removes StreamSpace from your local Kubernetes cluster +# and cleans up all Docker images and containers created during testing. +# + +set -euo pipefail + +# Colors for output +COLOR_RESET='\033[0m' +COLOR_BOLD='\033[1m' +COLOR_GREEN='\033[32m' +COLOR_YELLOW='\033[33m' +COLOR_BLUE='\033[34m' +COLOR_RED='\033[31m' + +# Project configuration +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +NAMESPACE="${NAMESPACE:-streamspace}" +RELEASE_NAME="${RELEASE_NAME:-streamspace}" +VERSION="${VERSION:-local}" + +# Helper functions +log() { + echo -e "${COLOR_BOLD}==>${COLOR_RESET} $*" +} + +log_success() { + echo -e "${COLOR_GREEN}✓${COLOR_RESET} $*" +} + +log_error() { + echo -e "${COLOR_RED}✗${COLOR_RESET} $*" >&2 +} + +log_info() { + echo -e "${COLOR_BLUE}→${COLOR_RESET} $*" +} + +log_warning() { + echo -e "${COLOR_YELLOW}⚠${COLOR_RESET} $*" +} + +# Confirm teardown +confirm_teardown() { + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo -e "${COLOR_BOLD} StreamSpace Local Teardown${COLOR_RESET}" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo "" + echo -e "${COLOR_YELLOW}This will:${COLOR_RESET}" + echo " • Uninstall Helm release: ${RELEASE_NAME}" + echo " • Delete namespace: ${NAMESPACE} (and all resources)" + echo " • Remove CRDs (Custom Resource Definitions)" + echo " • Delete local Docker images" + echo " • Clean up Docker build cache" + echo "" + + if [ "${AUTO_CONFIRM:-}" != "true" ]; then + read -p "Continue? (yes/no): " -r + echo + if [[ ! $REPLY =~ ^[Yy](es)?$ ]]; then + log_info "Teardown cancelled" + exit 0 + fi + fi +} + +# Uninstall Helm release +uninstall_helm() { + log "Uninstalling Helm release..." + + if helm status "${RELEASE_NAME}" -n "${NAMESPACE}" &> /dev/null; then + helm uninstall "${RELEASE_NAME}" -n "${NAMESPACE}" --wait + log_success "Helm release uninstalled" + else + log_warning "Helm release ${RELEASE_NAME} not found in namespace ${NAMESPACE}" + fi +} + +# Delete namespace +delete_namespace() { + log "Deleting namespace: ${NAMESPACE}" + + if kubectl get namespace "${NAMESPACE}" &> /dev/null; then + kubectl delete namespace "${NAMESPACE}" --wait=false + log_success "Namespace deletion initiated (may take a few moments)" + + # Wait for namespace deletion (with timeout) + log_info "Waiting for namespace deletion to complete..." + local timeout=120 # 2 minutes + local elapsed=0 + local interval=5 + + while kubectl get namespace "${NAMESPACE}" &> /dev/null && [ $elapsed -lt $timeout ]; do + sleep $interval + elapsed=$((elapsed + interval)) + done + + if kubectl get namespace "${NAMESPACE}" &> /dev/null; then + log_warning "Namespace deletion taking longer than expected" + log_info "You may need to manually check: kubectl get namespace ${NAMESPACE}" + else + log_success "Namespace deleted" + fi + else + log_warning "Namespace ${NAMESPACE} not found" + fi +} + +# Delete CRDs +delete_crds() { + log "Deleting Custom Resource Definitions..." + + local crds=( + "sessions.stream.streamspace.io" + "templates.stream.streamspace.io" + "templaterepositories.stream.streamspace.io" + "connections.stream.streamspace.io" + ) + + local deleted=0 + for crd in "${crds[@]}"; do + if kubectl get crd "${crd}" &> /dev/null; then + kubectl delete crd "${crd}" --wait=false + deleted=$((deleted + 1)) + fi + done + + if [ $deleted -gt 0 ]; then + log_success "Deleted ${deleted} CRD(s)" + log_info "Waiting for CRD deletion to finalize..." + sleep 5 + else + log_warning "No StreamSpace CRDs found" + fi +} + +# Clean Docker images +clean_docker_images() { + log "Cleaning Docker images..." + + # Remove StreamSpace images + local images=( + "streamspace/streamspace-controller:${VERSION}" + "streamspace/streamspace-controller:latest" + "streamspace/streamspace-api:${VERSION}" + "streamspace/streamspace-api:latest" + "streamspace/streamspace-ui:${VERSION}" + "streamspace/streamspace-ui:latest" + ) + + local removed=0 + for image in "${images[@]}"; do + if docker images -q "${image}" 2>/dev/null | grep -q .; then + docker rmi "${image}" &> /dev/null || true + removed=$((removed + 1)) + fi + done + + if [ $removed -gt 0 ]; then + log_success "Removed ${removed} Docker image(s)" + else + log_warning "No StreamSpace Docker images found" + fi +} + +# Clean dangling images +clean_dangling_images() { + log "Cleaning dangling images..." + + local dangling_count=$(docker images -f "dangling=true" -q | wc -l) + + if [ "$dangling_count" -gt 0 ]; then + docker image prune -f &> /dev/null + log_success "Removed ${dangling_count} dangling image(s)" + else + log_info "No dangling images to clean" + fi +} + +# Clean build cache +clean_build_cache() { + log "Cleaning Docker build cache..." + + # Only clean buildx cache if user confirms (can be large) + if [ "${CLEAN_CACHE:-}" = "true" ]; then + docker builder prune -af &> /dev/null || true + log_success "Build cache cleaned" + else + log_info "Skipping build cache cleanup (set CLEAN_CACHE=true to enable)" + fi +} + +# Clean stopped containers +clean_containers() { + log "Cleaning stopped containers..." + + local stopped_count=$(docker ps -aq -f status=exited | wc -l) + + if [ "$stopped_count" -gt 0 ]; then + docker container prune -f &> /dev/null + log_success "Removed ${stopped_count} stopped container(s)" + else + log_info "No stopped containers to clean" + fi +} + +# Show remaining resources +show_remaining() { + echo "" + log "Remaining StreamSpace resources:" + echo "" + + # Check for any remaining pods + local remaining_pods=$(kubectl get pods -A -l app.kubernetes.io/name=streamspace 2>/dev/null | tail -n +2 | wc -l) + if [ "$remaining_pods" -gt 0 ]; then + log_warning "Found ${remaining_pods} remaining pod(s)" + kubectl get pods -A -l app.kubernetes.io/name=streamspace + else + log_success "No remaining pods" + fi + + # Check for any remaining images + local remaining_images=$(docker images | grep -c "streamspace/streamspace-" || echo "0") + if [ "$remaining_images" -gt 0 ]; then + log_warning "Found ${remaining_images} remaining image(s)" + docker images | grep "streamspace/streamspace-" || true + else + log_success "No remaining Docker images" + fi +} + +# Show Docker disk usage +show_docker_usage() { + echo "" + log_info "Docker disk usage:" + docker system df +} + +# Main execution +main() { + confirm_teardown + + echo "" + log "Starting teardown process..." + echo "" + + # Check if kubectl is available + if command -v kubectl &> /dev/null; then + uninstall_helm + delete_namespace + delete_crds + else + log_warning "kubectl not found, skipping Kubernetes cleanup" + fi + + # Check if docker is available + if command -v docker &> /dev/null; then + clean_docker_images + clean_dangling_images + clean_containers + clean_build_cache + else + log_warning "docker not found, skipping Docker cleanup" + fi + + show_remaining + show_docker_usage + + echo "" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + log_success "Teardown complete!" + echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" + echo "" + log_info "To rebuild and redeploy:" + echo " 1. ./scripts/local-build.sh" + echo " 2. ./scripts/local-deploy.sh" + echo "" +} + +# Run main function +main "$@"