From 38a1108a464fdac0b500891d4f730c5b01e480f8 Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 3 Sep 2025 22:12:58 +0200 Subject: [PATCH] feat(deployment): replace monster scripts with modular helper scripts (#87) - Add install.sh orchestrator with upgrade support - Add 6 helper scripts (<100 lines each) replacing 700-800 line monsters - Add deployment/linux/furt.service systemd template - Support both fresh install and upgrade modes - Platform-aware detection (OpenBSD/FreeBSD vs Linux) - Skip user/service creation in upgrade mode - Preserve existing configuration during updates - Remove merkwerk dependency from production install script Helper scripts: - scripts/setup-user.sh - Create system user (_furt/furt) - scripts/setup-directories.sh - Create directory structure - scripts/sync-files.sh - Copy source files to installation - scripts/create-service.sh - Create system service from templates - scripts/validate-config.sh - Validate furt.conf syntax - scripts/health-check.sh - Basic health check functionality Closes DAW/furt#87 --- deployment/linux/furt.service | 18 +++++ install.sh | 122 ++++++++++++++++++++++++++++++++++ scripts/create-service.sh | 41 ++++++++++++ scripts/health-check.sh | 40 +++++++++++ scripts/setup-directories.sh | 29 ++++++++ scripts/setup-user.sh | 20 ++++++ scripts/sync-files.sh | 34 ++++++++++ scripts/validate-config.sh | 49 ++++++++++++++ 8 files changed, 353 insertions(+) create mode 100644 deployment/linux/furt.service create mode 100755 install.sh create mode 100755 scripts/create-service.sh create mode 100755 scripts/health-check.sh create mode 100755 scripts/setup-directories.sh create mode 100755 scripts/setup-user.sh create mode 100755 scripts/sync-files.sh create mode 100755 scripts/validate-config.sh diff --git a/deployment/linux/furt.service b/deployment/linux/furt.service new file mode 100644 index 0000000..d4df3d1 --- /dev/null +++ b/deployment/linux/furt.service @@ -0,0 +1,18 @@ +[Unit] +Description=furt Multi-Tenant API Gateway +After=network.target + +[Service] +Type=simple +User=furt +Group=furt +ExecStart=/usr/local/share/furt/scripts/start.sh start +WorkingDirectory=/usr/local/share/furt +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target + diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..9de2751 --- /dev/null +++ b/install.sh @@ -0,0 +1,122 @@ +#!/bin/sh +# install.sh - furt Installation and Update Orchestrator + +set -e + +# Parse command line arguments +UPGRADE_MODE=false +SKIP_USER=false +SKIP_SERVICE=false + +while [ $# -gt 0 ]; do + case "$1" in + --upgrade) UPGRADE_MODE=true; shift ;; + --skip-user) SKIP_USER=true; shift ;; + --skip-service) SKIP_SERVICE=true; shift ;; + --help) + echo "Usage: $0 [--upgrade] [--skip-user] [--skip-service]" + echo " --upgrade Update existing installation (skip user/service creation)" + echo " --skip-user Skip user creation step" + echo " --skip-service Skip service creation step" + exit 0 + ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +# Validate we're in furt source directory +if [ ! -f "src/main.lua" ] || [ ! -d "scripts" ]; then + echo "Error: Not in furt source directory" + echo "Expected files: src/main.lua, scripts/ directory" + exit 1 +fi + +echo "=== furt Installation ===" +if [ "$UPGRADE_MODE" = "true" ]; then + echo "Mode: Upgrade (preserving config and service)" +else + echo "Mode: Fresh installation" +fi + +# Step 1: Create system user (skip in upgrade mode) +if [ "$UPGRADE_MODE" = "false" ] && [ "$SKIP_USER" = "false" ]; then + echo "\n[1/6] Creating system user..." + ./scripts/setup-user.sh +else + echo "\n[1/6] Skipping user creation (upgrade mode)" +fi + +# Step 2: Setup directories +echo "\n[2/6] Setting up directories..." +./scripts/setup-directories.sh + +# Step 3: Sync source files (always needed for updates) +echo "\n[3/6] Syncing source files..." +./scripts/sync-files.sh + +# Step 4: Create service (skip in upgrade mode unless requested) +if [ "$UPGRADE_MODE" = "false" ] && [ "$SKIP_SERVICE" = "false" ]; then + echo "\n[4/6] Creating system service..." + ./scripts/create-service.sh +else + echo "\n[4/6] Skipping service creation (upgrade mode)" +fi + +# Step 5: Validate configuration +echo "\n[5/6] Validating configuration..." +if ./scripts/validate-config.sh; then + echo "Configuration validation successful" +else + echo "Warning: Configuration validation failed - manual setup may be needed" +fi + +# Step 6: Health check +echo "\n[6/6] Performing health check..." +if ./scripts/health-check.sh >/dev/null 2>&1; then + echo "Health check passed - furt is running" +else + echo "Health check failed - service may need to be started manually" +fi + +# Installation summary +echo "\n=== Installation Summary ===" +if [ "$UPGRADE_MODE" = "true" ]; then + echo "furt upgrade completed successfully" + echo "" + echo "Source code updated to:" + if [ -f "/usr/local/share/furt/VERSION" ]; then + echo " Version: $(cat /usr/local/share/furt/VERSION)" + fi + if [ -f "/usr/local/share/furt/.version_history" ]; then + echo " Version history available (for furt internal tracking)" + fi + echo "" + echo "Service restart required:" + if [ "$(uname)" = "OpenBSD" ]; then + echo " doas rcctl restart furt" + else + echo " sudo systemctl restart furt" + fi +else + echo "furt installation completed successfully" + echo "" + echo "Next steps:" + echo "1. Edit configuration file:" + if [ "$(uname)" = "OpenBSD" ] || [ "$(uname)" = "FreeBSD" ]; then + echo " /usr/local/etc/furt/furt.conf" + else + echo " /etc/furt/furt.conf" + fi + echo "2. Start the service:" + if [ "$(uname)" = "OpenBSD" ]; then + echo " doas rcctl start furt" + else + echo " sudo systemctl start furt" + fi + echo "3. Test the API:" + echo " curl http://127.0.0.1:7811/health" +fi + +echo "" +echo "Installation log available in system logs" + diff --git a/scripts/create-service.sh b/scripts/create-service.sh new file mode 100755 index 0000000..9732479 --- /dev/null +++ b/scripts/create-service.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# scripts/create-service.sh - Create system service for furt using repository templates + +set -e + +# Check if we're in furt source directory +if [ ! -d "deployment" ]; then + echo "Error: deployment/ directory not found - not in furt source directory?" + exit 1 +fi + +if [ "$(uname)" = "OpenBSD" ]; then + # Use OpenBSD rc.d template from repository + if [ ! -f "deployment/openbsd/rc.d-furt" ]; then + echo "Error: deployment/openbsd/rc.d-furt template not found" + exit 1 + fi + + cp deployment/openbsd/rc.d-furt /etc/rc.d/furt + chmod +x /etc/rc.d/furt + echo "furt_flags=" >> /etc/rc.conf.local + rcctl enable furt + echo "OpenBSD service created and enabled using repository template" + +elif [ "$(uname)" = "Linux" ]; then + # Use systemd template from repository + if [ ! -f "deployment/linux/furt.service" ]; then + echo "Error: deployment/linux/furt.service template not found" + exit 1 + fi + + cp deployment/linux/furt.service /etc/systemd/system/ + systemctl daemon-reload + systemctl enable furt + echo "Linux systemd service created and enabled using repository template" + +else + echo "Unsupported operating system for service creation" + exit 1 +fi + diff --git a/scripts/health-check.sh b/scripts/health-check.sh new file mode 100755 index 0000000..c193a84 --- /dev/null +++ b/scripts/health-check.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# scripts/health-check.sh - Basic health check for furt service + +set -e + +# Default values +HOST="127.0.0.1" +PORT="7811" + +# Parse command line arguments +while [ $# -gt 0 ]; do + case "$1" in + --host) HOST="$2"; shift 2 ;; + --port) PORT="$2"; shift 2 ;; + *) echo "Usage: $0 [--host HOST] [--port PORT]"; exit 1 ;; + esac +done + +echo "Checking furt health at $HOST:$PORT..." + +# Check if port is listening +if command -v curl >/dev/null 2>&1; then + if curl -s "http://$HOST:$PORT/health" > /tmp/health_response; then + echo "Health check successful:" + cat /tmp/health_response | sed 's/^/ /' + rm -f /tmp/health_response + else + echo "Health check failed - service not responding" + exit 1 + fi +else + echo "Warning: curl not available, using basic port check" + if nc -z "$HOST" "$PORT" 2>/dev/null; then + echo "Port $PORT is listening on $HOST" + else + echo "Port $PORT is not accessible on $HOST" + exit 1 + fi +fi + diff --git a/scripts/setup-directories.sh b/scripts/setup-directories.sh new file mode 100755 index 0000000..2fdbad6 --- /dev/null +++ b/scripts/setup-directories.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# scripts/setup-directories.sh - Create directory structure for furt + +set -e + +# Detect operating system for config directory +if [ "$(uname)" = "OpenBSD" ] || [ "$(uname)" = "FreeBSD" ]; then + CONFIG_DIR="/usr/local/etc/furt" + USER="_furt" + GROUP="_furt" +else + CONFIG_DIR="/etc/furt" + USER="furt" + GROUP="furt" +fi + +# Create directories +mkdir -p "$CONFIG_DIR" +mkdir -p /usr/local/share/furt +mkdir -p /var/log/furt + +# Set ownership for log directory (service user needs write access) +chown "$USER:$GROUP" /var/log/furt + +echo "Created directories:" +echo " Config: $CONFIG_DIR" +echo " Share: /usr/local/share/furt" +echo " Logs: /var/log/furt (owned by $USER)" + diff --git a/scripts/setup-user.sh b/scripts/setup-user.sh new file mode 100755 index 0000000..29cdb61 --- /dev/null +++ b/scripts/setup-user.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# scripts/setup-user.sh - Create _furt system user and group + +set -e + +# Detect operating system +if [ "$(uname)" = "OpenBSD" ] || [ "$(uname)" = "FreeBSD" ]; then + # BSD systems use _furt user convention + groupadd _furt 2>/dev/null || true + useradd -g _furt -s /bin/false -d /var/empty _furt 2>/dev/null || true + echo "Created BSD system user: _furt" +else + # Linux systems use furt user with --system flag + groupadd --system furt 2>/dev/null || true + useradd --system -g furt -s /bin/false -d /var/empty furt 2>/dev/null || true + echo "Created Linux system user: furt" +fi + +echo "User setup completed successfully" + diff --git a/scripts/sync-files.sh b/scripts/sync-files.sh new file mode 100755 index 0000000..b495a78 --- /dev/null +++ b/scripts/sync-files.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# scripts/sync-files.sh - Copy furt source files to installation directory + +set -e + +# Check if we're in a furt source directory +if [ ! -f "src/main.lua" ]; then + echo "Error: Not in furt source directory (src/main.lua not found)" + exit 1 +fi + +# Target directory +TARGET="/usr/local/share/furt" + +echo "Copying furt files to $TARGET..." + +# Copy main directories +cp -r src/ "$TARGET/" +cp -r config/ "$TARGET/" +cp -r scripts/ "$TARGET/" +cp -r integrations/ "$TARGET/" + +# Copy version files for merkwerk integration +[ -f "VERSION" ] && cp VERSION "$TARGET/" +[ -f ".version_history" ] && cp .version_history "$TARGET/" + +# Set proper permissions +chown -R root:wheel "$TARGET" 2>/dev/null || chown -R root:root "$TARGET" +chmod -R 644 "$TARGET" +find "$TARGET" -type d -exec chmod 755 {} \; +chmod +x "$TARGET/scripts/start.sh" + +echo "Files synced successfully to $TARGET" + diff --git a/scripts/validate-config.sh b/scripts/validate-config.sh new file mode 100755 index 0000000..7b59dc7 --- /dev/null +++ b/scripts/validate-config.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# scripts/validate-config.sh - Validate furt configuration + +set -e + +# Detect config file location +if [ "$(uname)" = "OpenBSD" ] || [ "$(uname)" = "FreeBSD" ]; then + CONFIG_FILE="/usr/local/etc/furt/furt.conf" +else + CONFIG_FILE="/etc/furt/furt.conf" +fi + +echo "Validating configuration: $CONFIG_FILE" + +# Check if config file exists +if [ ! -f "$CONFIG_FILE" ]; then + echo "Error: Configuration file not found: $CONFIG_FILE" + exit 1 +fi + +# Basic INI syntax validation +if ! grep -q '^\[server\]' "$CONFIG_FILE"; then + echo "Error: [server] section missing in config" + exit 1 +fi + +if ! grep -q '^port\s*=' "$CONFIG_FILE"; then + echo "Error: server port not configured" + exit 1 +fi + +if ! grep -q '^host\s*=' "$CONFIG_FILE"; then + echo "Error: server host not configured" + exit 1 +fi + +# Check for at least one API key +if ! grep -q '^\[api_key' "$CONFIG_FILE"; then + echo "Warning: No API keys configured" +fi + +# Check permissions (should not be world-readable due to secrets) +PERMS=$(stat -c '%a' "$CONFIG_FILE" 2>/dev/null || stat -f '%Lp' "$CONFIG_FILE") +if [ "$PERMS" -gt 640 ]; then + echo "Warning: Config file permissions too open ($PERMS), should be 640" +fi + +echo "Configuration validation completed" +