diff --git a/api/Dockerfile b/api/Dockerfile index be2d5e5..cccbeac 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -61,9 +61,11 @@ USER streamspace # Expose API port EXPOSE 8000 -# Health check +# Health check. NOTE: BusyBox wget's --spider sends HEAD, which gin's +# router answers with 404 because the /health route is registered for +# GET only. Use -O /dev/null to force GET and discard the body. HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:8000/health || exit 1 + CMD wget -q -O /dev/null http://localhost:8000/health || exit 1 # Environment variables with defaults ENV API_PORT=8000 \ diff --git a/api/internal/handlers/sessiontemplates.go b/api/internal/handlers/sessiontemplates.go index 6584afe..aa328b7 100644 --- a/api/internal/handlers/sessiontemplates.go +++ b/api/internal/handlers/sessiontemplates.go @@ -620,7 +620,7 @@ func (h *SessionTemplatesHandler) UseSessionTemplate(c *gin.Context) { PersistentHome: true, } - // Add template configuration for Docker controller + // Add template configuration for the Docker agent (v2; was "Docker controller" pre-v2 rename). if k8sTemplate != nil { // Selkies-GStreamer defaults to port 8080. Templates still carry a // legacy VNC.Port field for backwards-compat with old fixtures; honor diff --git a/docker-compose.yml b/docker-compose.yml index bc3d56d..8915232 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.9' - services: # PostgreSQL database postgres: @@ -10,7 +8,9 @@ services: POSTGRES_USER: streamspace POSTGRES_PASSWORD: streamspace ports: - - "5432:5432" + # Host 5433 → container 5432 to avoid clashing with a host Postgres on 5432. + # Internally, services on the streamspace network still reach it on 5432. + - "5433:5432" volumes: - postgres-data:/var/lib/postgresql/data healthcheck: @@ -71,14 +71,29 @@ services: API_PORT: 8000 GIN_MODE: debug - # JWT configuration - JWT_SECRET: dev-secret-change-in-production + # JWT configuration. Must be >=32 chars per api/internal/auth check. + JWT_SECRET: dev-jwt-secret-change-in-production-please-use-a-real-one + + # Dev admin credentials — bypasses the first-run setup wizard so the + # login form (admin/admin123) works immediately. Override or remove + # for any non-local deployment. + ADMIN_PASSWORD: admin123 + + # Agent bootstrap key — must match the AGENT_API_KEY passed to the + # docker-agent service below. The agent self-registers on first + # connect when its API key matches this bootstrap key. Format must + # be 64 hex characters (validated by api/internal/auth.ValidateAPIKeyFormat). + AGENT_BOOTSTRAP_KEY: deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef # NATS configuration NATS_URL: nats://nats:4222 NATS_USER: "" NATS_PASSWORD: "" - PLATFORM: kubernetes + # Docker-platform mode for the local compose stack — k8sClient stays + # nil (the API tolerates this; see api/cmd/main.go around the k8s + # NewClient call). For kubernetes, mount a kubeconfig and set this + # to "kubernetes". + PLATFORM: docker # Sync configuration SYNC_INTERVAL: 1h @@ -92,7 +107,9 @@ services: # - ~/.kube:/root/.kube:ro - /tmp/streamspace-repos:/tmp/streamspace-repos healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8000/health"] + # `wget --spider` sends HEAD; gin's /health route only answers GET. + # Use -O /dev/null to force a GET that succeeds with the registered route. + test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8000/health"] interval: 30s timeout: 3s retries: 3 @@ -100,29 +117,69 @@ services: - streamspace restart: unless-stopped - # StreamSpace Docker Controller (for Docker platform support) - docker-controller: + # StreamSpace Docker Agent (v2 architecture). + # + # NOTE: The Docker agent fell behind during k8s-agent development — its + # session lifecycle handlers may be incomplete relative to k8s-agent. + # This service is wired here so the v2 control-plane → agent topology + # can boot end-to-end, but launching real Docker-backed sessions through + # the agent is not guaranteed to fully work yet. Use the k8s deployment + # path (manifests/) for verified end-to-end streaming until the docker + # agent catches up. + # + # Self-registers on first connect using AGENT_BOOTSTRAP_KEY (set on the + # API service above) — no manual provisioning needed for dev. + docker-agent: build: - context: ./docker-controller + context: ./agents/docker-agent dockerfile: Dockerfile - container_name: streamspace-docker-controller + container_name: streamspace-docker-agent depends_on: - nats: + api: condition: service_healthy environment: - NATS_URL: nats://nats:4222 - NATS_USER: "" - NATS_PASSWORD: "" - CONTROLLER_ID: streamspace-docker-controller-1 + AGENT_ID: docker-agent-local + CONTROL_PLANE_URL: ws://api:8000/api/v1/agents/connect + AGENT_API_KEY: deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef + REGION: local DOCKER_NETWORK: streamspace + LEADER_ELECTION_BACKEND: file + # Agent needs to manage Docker containers on the host. The :ro flag + # from the previous docker-controller config wouldn't allow create/start. volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro + - /var/run/docker.sock:/var/run/docker.sock networks: - streamspace profiles: - docker restart: unless-stopped + # StreamSpace Web UI. Nginx serves the built React bundle and proxies + # /api/ + /ws/ to the api service (config in ui/nginx.conf). Visit + # http://localhost:3000 in the browser. + ui: + build: + context: ./ui + dockerfile: Dockerfile + args: + VERSION: ${VERSION:-dev} + COMMIT: ${COMMIT:-local} + BUILD_DATE: ${BUILD_DATE} + container_name: streamspace-ui + depends_on: + api: + condition: service_healthy + ports: + - "3000:80" + networks: + - streamspace + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"] + interval: 30s + timeout: 5s + retries: 3 + restart: unless-stopped + # pgAdmin for database management (optional, for development) pgadmin: image: dpage/pgadmin4:latest diff --git a/scripts/README-V2.md b/scripts/README-V2.md index d2ec6da..700b559 100644 --- a/scripts/README-V2.md +++ b/scripts/README-V2.md @@ -45,7 +45,7 @@ These scripts reference v1.0 architecture (CRDs, controller) and need updates: | `local-teardown.sh` | Teardown local env | ⚠️ Minor updates needed | | `local-port-forward.sh` | Port-forward services | ✅ Mostly works, add agent logs | | `local-stop-port-forward.sh` | Stop port-forwards | ✅ Works as-is | -| `build-docker-controller.sh` | Build controller image | ⚠️ Rename to build K8s Agent | +| `local-build.sh docker-agent` | Build Docker agent image | ✅ Replaced legacy `build-docker-controller.sh` | | `docker-dev.sh` | Docker dev environment | ⚠️ Update for Control Plane + Agent | | `docker-dev-stop.sh` | Stop Docker dev | ✅ Works as-is | | `test-nats.sh` | Test NATS connectivity | ⚠️ Update for agent WebSocket | diff --git a/scripts/README.md b/scripts/README.md index 334660c..15214b0 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -51,7 +51,7 @@ For the new event-driven multi-platform architecture, use these scripts: # Start development environment (PostgreSQL, NATS) ./scripts/docker-dev.sh -# Start with Docker controller +# Start with Docker agent ./scripts/docker-dev.sh --with-docker # Start with all services (including monitoring) @@ -73,7 +73,7 @@ Starts the complete development environment using Docker Compose with NATS and P ```bash ./scripts/docker-dev.sh # Core services only ./scripts/docker-dev.sh --with-api # Include API service -./scripts/docker-dev.sh --with-docker # Include Docker controller +./scripts/docker-dev.sh --with-docker # Include Docker agent ./scripts/docker-dev.sh --all # All services and profiles ./scripts/docker-dev.sh --logs # Start and follow logs ``` @@ -86,7 +86,7 @@ Starts the complete development environment using Docker Compose with NATS and P **Optional Services:** - API backend (--with-api) -- Docker controller (--with-docker) +- Docker agent (--with-docker) - pgAdmin (--with-dev) - Prometheus/Grafana (--with-monitor) @@ -101,17 +101,6 @@ Stops the Docker Compose development environment. ./scripts/docker-dev-stop.sh --clean # Stop and remove volumes ``` -### build-docker-controller.sh - -Builds the Docker platform controller for the event-driven architecture. - -**Usage:** - -```bash -./scripts/build-docker-controller.sh # Build Docker image -./scripts/build-docker-controller.sh --binary # Build Go binary only -``` - ### test-nats.sh Tests NATS connectivity and can publish/subscribe to test events. diff --git a/scripts/build-docker-controller.sh b/scripts/build-docker-controller.sh deleted file mode 100755 index 9d909a8..0000000 --- a/scripts/build-docker-controller.sh +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env bash -# -# build-docker-controller.sh - Build the StreamSpace Docker platform controller -# -# This script builds the Docker controller which handles session management -# on Docker platforms via NATS events. -# -# Usage: -# ./scripts/build-docker-controller.sh # Build Docker image -# ./scripts/build-docker-controller.sh --binary # Build binary only -# - -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)" -CONTROLLER_DIR="${PROJECT_ROOT}/docker-controller" -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 name -DOCKER_CONTROLLER_IMAGE="streamspace/docker-controller" - -# Build mode -BUILD_BINARY_ONLY=false - -# 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} $*" -} - -# Show usage -usage() { - cat << EOF -Usage: $(basename "$0") [OPTIONS] - -Build the StreamSpace Docker platform controller. - -Options: - --binary Build Go binary only (no Docker image) - --push Push image to registry after building - -h, --help Show this help message - -Environment Variables: - VERSION Image tag (default: local) - REGISTRY Docker registry prefix (default: none) - -Examples: - $(basename "$0") # Build Docker image - $(basename "$0") --binary # Build binary only - VERSION=v1.0.0 $(basename "$0") # Build with specific version - -EOF - exit 0 -} - -# Parse arguments -PUSH_IMAGE=false -parse_args() { - while [[ $# -gt 0 ]]; do - case $1 in - --binary) - BUILD_BINARY_ONLY=true - shift - ;; - --push) - PUSH_IMAGE=true - shift - ;; - -h|--help) - usage - ;; - *) - log_error "Unknown option: $1" - usage - ;; - esac - done -} - -# Check prerequisites -check_prerequisites() { - log "Checking prerequisites..." - - if [ ! -d "$CONTROLLER_DIR" ]; then - log_error "Docker controller directory not found: $CONTROLLER_DIR" - exit 1 - fi - - if [ "$BUILD_BINARY_ONLY" = true ]; then - if ! command -v go &> /dev/null; then - log_error "Go is not installed or not in PATH" - exit 1 - fi - log_success "Go is available: $(go version)" - else - 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" - fi -} - -# Build binary -build_binary() { - log "Building Docker controller binary..." - log_info "Version: $VERSION" - log_info "Commit: $GIT_COMMIT" - - cd "$CONTROLLER_DIR" - - # Download dependencies - log_info "Downloading dependencies..." - go mod download - - # Build binary - log_info "Compiling..." - CGO_ENABLED=0 go build \ - -ldflags "-X main.version=${VERSION} -X main.commit=${GIT_COMMIT} -X main.buildDate=${BUILD_DATE}" \ - -o bin/docker-controller \ - ./cmd/main.go - - log_success "Binary built: $CONTROLLER_DIR/bin/docker-controller" -} - -# Build Docker image -build_image() { - log "Building Docker controller image..." - log_info "Image: ${DOCKER_CONTROLLER_IMAGE}:${VERSION}" - log_info "Context: $CONTROLLER_DIR" - - docker build \ - --build-arg VERSION="${VERSION}" \ - --build-arg COMMIT="${GIT_COMMIT}" \ - --build-arg BUILD_DATE="${BUILD_DATE}" \ - -t "${DOCKER_CONTROLLER_IMAGE}:${VERSION}" \ - -t "${DOCKER_CONTROLLER_IMAGE}:latest" \ - -f "${CONTROLLER_DIR}/Dockerfile" \ - "${CONTROLLER_DIR}/" - - log_success "Docker image built successfully" - - # Show image info - echo "" - docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}" | \ - grep -E "REPOSITORY|${DOCKER_CONTROLLER_IMAGE}" || true -} - -# Push image -push_image() { - if [ "$PUSH_IMAGE" = true ]; then - log "Pushing image to registry..." - docker push "${DOCKER_CONTROLLER_IMAGE}:${VERSION}" - docker push "${DOCKER_CONTROLLER_IMAGE}:latest" - log_success "Image pushed" - fi -} - -# Main execution -main() { - echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" - echo -e "${COLOR_BOLD} Build StreamSpace Docker Controller${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 "" - - parse_args "$@" - check_prerequisites - - if [ "$BUILD_BINARY_ONLY" = true ]; then - build_binary - else - build_image - push_image - fi - - echo "" - echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" - log_success "Build completed successfully!" - echo -e "${COLOR_BOLD}═══════════════════════════════════════════════════${COLOR_RESET}" - echo "" - - if [ "$BUILD_BINARY_ONLY" = true ]; then - log_info "Run the binary:" - echo " $CONTROLLER_DIR/bin/docker-controller --nats-url=nats://localhost:4222" - else - log_info "Run with docker-compose:" - echo " ./scripts/docker-dev.sh --with-docker" - echo "" - log_info "Or run standalone:" - echo " docker run -d \\" - echo " -e NATS_URL=nats://host.docker.internal:4222 \\" - echo " -v /var/run/docker.sock:/var/run/docker.sock:ro \\" - echo " ${DOCKER_CONTROLLER_IMAGE}:${VERSION}" - fi - echo "" -} - -# Run main function -main "$@" diff --git a/scripts/docker-dev.sh b/scripts/docker-dev.sh index 6010015..25753be 100755 --- a/scripts/docker-dev.sh +++ b/scripts/docker-dev.sh @@ -3,12 +3,12 @@ # docker-dev.sh - Start StreamSpace development environment with Docker Compose # # This script starts the complete development environment using docker-compose, -# including PostgreSQL, NATS with JetStream, and optionally the API and Docker controller. +# including PostgreSQL, NATS with JetStream, and optionally the API and Docker agent. # # Usage: # ./scripts/docker-dev.sh # Start core services (postgres, nats) # ./scripts/docker-dev.sh --with-api # Include API service -# ./scripts/docker-dev.sh --with-docker # Include Docker controller +# ./scripts/docker-dev.sh --with-docker # Include Docker agent # ./scripts/docker-dev.sh --all # Start all services # ./scripts/docker-dev.sh --logs # Start and follow logs # @@ -61,7 +61,7 @@ Start StreamSpace development environment with Docker Compose. Options: --with-api Include the API service - --with-docker Include the Docker controller (profile: docker) + --with-docker Include the Docker agent (profile: docker) --with-dev Include development tools like pgAdmin (profile: dev) --with-monitor Include monitoring stack (profile: monitoring) --all Start all services including all profiles @@ -71,7 +71,7 @@ Options: Examples: $(basename "$0") # Start core services (postgres, nats) $(basename "$0") --with-api # Start with API - $(basename "$0") --with-docker # Start with Docker controller + $(basename "$0") --with-docker # Start with Docker agent $(basename "$0") --all --logs # Start all and follow logs Services: @@ -83,7 +83,7 @@ Services: - api StreamSpace API backend Docker Profile (--with-docker): - - docker-controller Docker platform controller + - docker-agent Docker platform controller Dev Profile (--with-dev): - pgadmin PostgreSQL admin interface diff --git a/scripts/local-build.sh b/scripts/local-build.sh index 6d5a856..decc813 100755 --- a/scripts/local-build.sh +++ b/scripts/local-build.sh @@ -2,7 +2,7 @@ # # local-build.sh - Build all StreamSpace Docker images locally # -# This script builds the controller, API, and UI Docker images for local testing. +# This script builds the API, UI, and agent Docker images for local testing. # Images are tagged with 'local' and 'latest' tags for easy identification. # @@ -27,7 +27,7 @@ KUBERNETES_CONTROLLER_IMAGE="streamspace/streamspace-kubernetes-controller" API_IMAGE="streamspace/streamspace-api" UI_IMAGE="streamspace/streamspace-ui" K8S_AGENT_IMAGE="streamspace/streamspace-k8s-agent" -DOCKER_CONTROLLER_IMAGE="streamspace/streamspace-docker-controller" +DOCKER_AGENT_IMAGE="streamspace/streamspace-docker-agent" # GHCR image names (for local K8s deployment compatibility) GHCR_API_IMAGE="ghcr.io/streamspace-dev/streamspace-api" @@ -135,25 +135,19 @@ build_k8s_agent() { log_success "K8s Agent image built successfully" } -# Build Docker controller image -build_docker_controller() { - log "Building Docker controller image..." - log_info "Image: ${DOCKER_CONTROLLER_IMAGE}:${VERSION}" - log_info "Context: ${PROJECT_ROOT}/docker-controller" - - # Check if docker-controller directory exists - if [ ! -d "${PROJECT_ROOT}/docker-controller" ]; then - log_warning "Docker controller directory not found, skipping (deferred to v2.1)" - return 0 - fi +# Build Docker agent image (v2 architecture; replaces v1 docker-controller). +build_docker_agent() { + log "Building Docker agent image..." + log_info "Image: ${DOCKER_AGENT_IMAGE}:${VERSION}" + log_info "Context: ${PROJECT_ROOT}/agents/docker-agent" docker build ${BUILD_ARGS} \ - -t "${DOCKER_CONTROLLER_IMAGE}:${VERSION}" \ - -t "${DOCKER_CONTROLLER_IMAGE}:latest" \ - -f "${PROJECT_ROOT}/docker-controller/Dockerfile" \ - "${PROJECT_ROOT}/docker-controller/" + -t "${DOCKER_AGENT_IMAGE}:${VERSION}" \ + -t "${DOCKER_AGENT_IMAGE}:latest" \ + -f "${PROJECT_ROOT}/agents/docker-agent/Dockerfile" \ + "${PROJECT_ROOT}/agents/docker-agent/" - log_success "Docker controller image built successfully" + log_success "Docker agent image built successfully" } # List built images @@ -161,7 +155,7 @@ list_images() { log "Built images:" echo "" docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}" | \ - grep -E "REPOSITORY|streamspace/streamspace-(kubernetes-controller|api|ui|k8s-agent|docker-controller)" || true + grep -E "REPOSITORY|streamspace/streamspace-(api|ui|k8s-agent|docker-agent)" || true echo "" } @@ -202,12 +196,12 @@ main() { k8s-agent|agent) build_k8s_agent ;; - docker-controller) - build_docker_controller + docker-agent) + build_docker_agent ;; *) log_error "Unknown component: $component" - log_info "Valid components: controller, api, ui, k8s-agent, docker-controller" + log_info "Valid components: api, ui, k8s-agent, docker-agent" exit 1 ;; esac