#!/bin/bash # tin service deploy - Deploy service from catalog set -euo pipefail # Get tinsnip root and source libraries SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TINSNIP_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" source "$TINSNIP_ROOT/lib/core.sh" source "$TINSNIP_ROOT/lib/uid.sh" source "$TINSNIP_ROOT/lib/metadata.sh" # Helper function to calculate port numbers get_service_port() { local service_name="$1" local service_env="$2" local port_index="${3:-0}" local service_uid=$(calculate_service_uid "$service_name" "$service_env") echo $((service_uid + port_index)) } # Deploy service from catalog deploy_service() { local service_env="$1" local catalog_service="$2" # Parse service-environment local parsed_output if ! parsed_output=$(parse_machine_name "$service_env" 2>/dev/null); then error_with_prefix "Service Deploy" "Invalid machine environment format: '$service_env'" echo "Expected: - (e.g., gazette-prod, bsky-pds-dev)" >&2 exit 1 fi local machine_service=$(echo "$parsed_output" | sed -n '1p') local environment=$(echo "$parsed_output" | sed -n '2p') local service_user="$service_env" log_with_prefix "Service Deploy" "Deploying service: $catalog_service → $service_env" echo # Debug: Show current TIN_SHEET log_with_prefix "Service Deploy" "Using sheet: ${TIN_SHEET:-topsheet}" # Verify machine environment exists local service_uid if ! service_uid=$(calculate_service_uid "$machine_service" "$environment" 2>/dev/null); then error_with_prefix "Service Deploy" "Cannot calculate UID for machine environment" exit 1 fi log_with_prefix "Service Deploy" "Calculated UID: $service_uid" if ! getent passwd "$service_uid" >/dev/null 2>&1; then error_with_prefix "Service Deploy" "Machine environment '$service_env' not found" echo "Create with: tin machine create $machine_service $environment" >&2 exit 1 fi # Check if service catalog exists local service_catalog_path="$HOME/.local/opt/dynamicalsystem.service" if [[ ! -d "$service_catalog_path/$catalog_service" ]]; then error_with_prefix "Service Deploy" "Service '$catalog_service' not found in catalog" echo "Available services:" >&2 if [[ -d "$service_catalog_path" ]]; then ls -1 "$service_catalog_path" | grep -v README | head -10 >&2 fi exit 1 fi log_with_prefix "Service Deploy" "Machine environment verified: $service_env" log_with_prefix "Service Deploy" "Service catalog found: $catalog_service" # Show deployment plan echo "Deployment Plan:" echo " Machine: $service_env" echo " Service: $catalog_service" echo " User: $service_user" echo " UID: $service_uid" echo " Source: $service_catalog_path/$catalog_service" echo " Target: /mnt/$service_env/service/$catalog_service" echo # Confirm deployment read -p "Deploy $catalog_service to $service_env? [Y/n]: " confirm case "${confirm:-y}" in [Yy]*|"") log_with_prefix "Service Deploy" "Starting deployment..." ;; [Nn]*) log_with_prefix "Service Deploy" "Deployment cancelled" exit 1 ;; *) error_with_prefix "Service Deploy" "Invalid input. Deployment cancelled" exit 1 ;; esac # Copy service files log_with_prefix "Service Deploy" "Copying service files..." sudo -u "$service_user" mkdir -p "/mnt/$service_env/service" # Copy as current user (to avoid permission issues reading from home directory) # then fix ownership for the service user if cp -r "$service_catalog_path/$catalog_service" "/mnt/$service_env/service/"; then # Fix ownership to service user (NFS all_squash will handle actual ownership) sudo chown -R "$service_uid:$service_uid" "/mnt/$service_env/service/$catalog_service" 2>/dev/null || true log_with_prefix "Service Deploy" "Service files copied successfully" else error_with_prefix "Service Deploy" "Failed to copy service files" exit 1 fi # Allocate ports and generate service .env log_with_prefix "Service Deploy" "Allocating ports..." local compose_file="/mnt/$service_env/service/$catalog_service/docker-compose.yml" local port_count port_count=$(get_service_port_count "$compose_file") local start_port if ! start_port=$(allocate_service_ports "$machine_service" "$environment" "$catalog_service" "$port_count"); then error_with_prefix "Service Deploy" "Port allocation failed" # Clean up copied files sudo rm -rf "/mnt/$service_env/service/$catalog_service" exit 1 fi if ! generate_service_env "$service_env" "$catalog_service" "$start_port" "$port_count"; then error_with_prefix "Service Deploy" "Failed to generate service .env" # Clean up sudo rm -rf "/mnt/$service_env/service/$catalog_service" exit 1 fi local end_port=$((start_port + port_count - 1)) log_with_prefix "Service Deploy" "✓ Ports allocated: $start_port-$end_port ($port_count ports)" # Run service setup if it exists local setup_script="/mnt/$service_env/service/$catalog_service/setup.sh" if [[ -f "$setup_script" ]]; then log_with_prefix "Service Deploy" "Running service setup script..." # Ensure script is executable chmod +x "$setup_script" # Run as service user with machine and service env sourced if sudo -u "$service_user" bash -c "source /mnt/$service_env/.machine/machine.env && source /mnt/$service_env/service/$catalog_service/.env && bash $setup_script"; then log_with_prefix "Service Deploy" "Service setup completed" else warn_with_prefix "Service Deploy" "Service setup script failed" fi fi # Start services local compose_file="/mnt/$service_env/service/$catalog_service/docker-compose.yml" if [[ -f "$compose_file" ]]; then log_with_prefix "Service Deploy" "Starting services..." # Change to service directory and start # Source env files with auto-export for Docker Compose YAML interpolation, but unset XDG vars that break rootless Docker # Containers get all vars via env_file directive in docker-compose.yml if sudo -u "$service_user" bash -c "set -a && source /mnt/$service_env/.machine/machine.env && source /mnt/$service_env/service/$catalog_service/.env && set +a && unset XDG_DATA_HOME XDG_CONFIG_HOME XDG_STATE_HOME && cd /mnt/$service_env/service/$catalog_service && docker compose up -d"; then log_with_prefix "Service Deploy" "✅ Service '$catalog_service' deployed successfully to '$service_env'" else error_with_prefix "Service Deploy" "Failed to start services" exit 1 fi else warn_with_prefix "Service Deploy" "No docker-compose.yml found - service copied but not started" fi echo log_with_prefix "Service Deploy" "Deployment complete!" echo " Service: $catalog_service" echo " Ports: $start_port-$end_port" echo "" echo "Check status with: tin service status $service_env" echo "View logs with: tin service logs $service_env $catalog_service" } show_help() { cat << EOF tin service deploy - Deploy service from catalog USAGE: tin service deploy tin service # Shorthand DESCRIPTION: Deploy a service from the catalog to a prepared machine environment. The service is copied from the catalog and configured for the target environment with proper UID mapping and port allocation. ARGUMENTS: Target machine environment (e.g., gazette-prod) Service name from catalog to deploy EXAMPLES: tin service deploy gazette-prod lldap # Deploy LLDAP to gazette-prod tin service lldap-test redis # Deploy Redis to lldap-test tin service station-prod prometheus # Deploy Prometheus to station NOTES: - The target machine environment must exist (created with tin machine) - The catalog service must be available in the service catalog - Services are deployed as the service user with proper UID/port mapping EOF } # Handle help flags case "${1:-}" in --help|-h|help) show_help exit 0 ;; esac # Main execution if [[ $# -lt 2 ]]; then error_with_prefix "Service Deploy" "Service environment and catalog service required" echo "Usage: tin service deploy " >&2 exit 1 fi service_env="$1" catalog_service="$2" deploy_service "$service_env" "$catalog_service"