251 lines
7.8 KiB
Bash
Executable File
251 lines
7.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ============================================================
|
|
# Fester Bootstrap Installer
|
|
# ============================================================
|
|
# Deploys the Fester codebase to a target directory, runs the
|
|
# first-run config wizard, and writes the systemd unit + cockpit
|
|
# manifest. Does NOT overwrite the real CLI with a stub.
|
|
#
|
|
# Usage:
|
|
# ./install-fester.sh # interactive
|
|
# BASE_DIR=/opt/fester ./install-fester.sh # custom target
|
|
# NONINTERACTIVE=1 ./install-fester.sh # CI mode (defaults)
|
|
# ============================================================
|
|
set -euo pipefail
|
|
|
|
# Resolve the repo root (directory containing this script)
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
BASE_DIR="${BASE_DIR:-/usr/share/cockpit/fester}"
|
|
NONINTERACTIVE="${NONINTERACTIVE:-0}"
|
|
|
|
echo "============================================================"
|
|
echo " Fester Bootstrap Installer"
|
|
echo " Source: $REPO_ROOT"
|
|
echo " Target: $BASE_DIR"
|
|
echo "============================================================"
|
|
|
|
# ------------------------------------------------------------
|
|
# Dependency checks
|
|
# ------------------------------------------------------------
|
|
echo ""
|
|
echo "🔍 Checking dependencies..."
|
|
|
|
check_dep() {
|
|
if command -v "$1" >/dev/null 2>&1; then
|
|
echo " ✓ $1"
|
|
return 0
|
|
else
|
|
echo " ✗ $1 NOT FOUND"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
MISSING=0
|
|
check_dep python3 || MISSING=1
|
|
|
|
# Python packages
|
|
PYTHON_OK=1
|
|
python3 -c "import fastapi" 2>/dev/null && echo " ✓ python: fastapi" || { echo " ✗ python: fastapi missing"; PYTHON_OK=0; }
|
|
python3 -c "import uvicorn" 2>/dev/null && echo " ✓ python: uvicorn" || { echo " ✗ python: uvicorn missing"; PYTHON_OK=0; }
|
|
python3 -c "import websockets" 2>/dev/null && echo " ✓ python: websockets" || { echo " ✗ python: websockets missing"; PYTHON_OK=0; }
|
|
python3 -c "import prometheus_client" 2>/dev/null && echo " ✓ python: prometheus_client" || { echo " ✗ python: prometheus_client missing"; PYTHON_OK=0; }
|
|
|
|
if [ "$MISSING" -eq 1 ]; then
|
|
echo ""
|
|
echo "❌ Required system tools missing. Install them first."
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$PYTHON_OK" -eq 0 ]; then
|
|
echo ""
|
|
echo "⚠️ Some Python packages missing. Installing..."
|
|
pip3 install --break-system-packages fastapi uvicorn websockets prometheus_client minio pydantic 2>/dev/null || true
|
|
fi
|
|
|
|
# ------------------------------------------------------------
|
|
# Create directory structure
|
|
# ------------------------------------------------------------
|
|
echo ""
|
|
echo "📁 Creating directory structure at $BASE_DIR..."
|
|
sudo mkdir -p "$BASE_DIR"
|
|
sudo chown -R "$USER":"$USER" "$BASE_DIR" 2>/dev/null || true
|
|
mkdir -p "$BASE_DIR"/{backend,ui,cockpit/fester-module,cli,docs,config}
|
|
|
|
# ------------------------------------------------------------
|
|
# Copy code from repo
|
|
# ------------------------------------------------------------
|
|
echo "📦 Copying code from repo..."
|
|
|
|
# Backend
|
|
cp -r "$REPO_ROOT/backend/"* "$BASE_DIR/backend/"
|
|
|
|
# UI
|
|
cp "$REPO_ROOT/style.css" "$BASE_DIR/"
|
|
cp "$REPO_ROOT/index.html" "$BASE_DIR/"
|
|
cp -r "$REPO_ROOT/ui/"* "$BASE_DIR/ui/"
|
|
|
|
# Cockpit module
|
|
cp -r "$REPO_ROOT/cockpit/"* "$BASE_DIR/cockpit/"
|
|
|
|
# CLI (the REAL one, not a stub)
|
|
cp "$REPO_ROOT/cli/fester.py" "$BASE_DIR/cli/fester.py"
|
|
chmod +x "$BASE_DIR/cli/fester.py"
|
|
|
|
# Top-level files
|
|
cp "$REPO_ROOT/manifest.json" "$BASE_DIR/"
|
|
cp "$REPO_ROOT/project.schema.json" "$BASE_DIR/"
|
|
cp "$REPO_ROOT/CHEATSHEET.md" "$BASE_DIR/docs/"
|
|
cp "$REPO_ROOT/README" "$BASE_DIR/LICENSE" 2>/dev/null || true
|
|
|
|
# ------------------------------------------------------------
|
|
# Config wizard
|
|
# ------------------------------------------------------------
|
|
echo ""
|
|
echo "⚙️ Configuration"
|
|
|
|
if [ "$NONINTERACTIVE" = "1" ]; then
|
|
CLUSTER_NAME="fester-cluster"
|
|
LIBVIRT="y"
|
|
LXC="y"
|
|
DISTCC="y"
|
|
MINIO="y"
|
|
else
|
|
read -rp "Cluster name [fester-cluster]: " CLUSTER_NAME
|
|
CLUSTER_NAME=${CLUSTER_NAME:-fester-cluster}
|
|
|
|
read -rp "Enable libvirt integration? (y/n) [y]: " LIBVIRT
|
|
LIBVIRT=${LIBVIRT:-y}
|
|
|
|
read -rp "Enable LXC integration? (y/n) [y]: " LXC
|
|
LXC=${LXC:-y}
|
|
|
|
read -rp "Enable distcc integration? (y/n) [y]: " DISTCC
|
|
DISTCC=${DISTCC:-y}
|
|
|
|
read -rp "Enable MinIO cache backend? (y/n) [y]: " MINIO
|
|
MINIO=${MINIO:-y}
|
|
fi
|
|
|
|
# Write the canonical config.yaml — aligned with the project.schema.json
|
|
cat > "$BASE_DIR/config.yaml" <<EOF
|
|
cluster:
|
|
name: $CLUSTER_NAME
|
|
|
|
master:
|
|
name: fester-master
|
|
role: control
|
|
|
|
nodes:
|
|
- name: node-1
|
|
host: 192.168.1.10
|
|
max_jobs: 8
|
|
- name: node-2
|
|
host: 192.168.1.11
|
|
max_jobs: 8
|
|
|
|
integrations:
|
|
libvirt: $LIBVIRT
|
|
lxc: $LXC
|
|
distcc: $DISTCC
|
|
|
|
cache:
|
|
backend: $([ "$MINIO" = "y" ] && echo "minio" || echo "local")
|
|
endpoint: localhost:9000
|
|
|
|
observability:
|
|
prometheus: true
|
|
grafana: true
|
|
|
|
scheduler:
|
|
mode: weighted-thermal-aware
|
|
|
|
projects: []
|
|
EOF
|
|
|
|
echo " ✓ Wrote $BASE_DIR/config.yaml"
|
|
|
|
# ------------------------------------------------------------
|
|
# Role DB location
|
|
# ------------------------------------------------------------
|
|
mkdir -p /etc/fester 2>/dev/null && sudo chown "$USER":"$USER" /etc/fester 2>/dev/null || true
|
|
echo '{}' > /etc/fester/node_roles.json 2>/dev/null || true
|
|
|
|
# ------------------------------------------------------------
|
|
# Cache dir
|
|
# ------------------------------------------------------------
|
|
mkdir -p /var/lib/fester/cache 2>/dev/null && sudo chown "$USER":"$USER" /var/lib/fester 2>/dev/null || true
|
|
|
|
# ------------------------------------------------------------
|
|
# Prometheus scrape config (drop-in)
|
|
# ------------------------------------------------------------
|
|
cat > "$BASE_DIR/add-to-prometheus-config.yml" <<'EOF'
|
|
scrape_configs:
|
|
- job_name: 'fester'
|
|
static_configs:
|
|
- targets: ['localhost:8080']
|
|
metrics_path: /metrics
|
|
EOF
|
|
|
|
# ------------------------------------------------------------
|
|
# Grafana dashboard stub
|
|
# ------------------------------------------------------------
|
|
cat > "$BASE_DIR/add-to-grafana-config.json" <<'EOF'
|
|
{
|
|
"dashboard": {
|
|
"title": "Fester Cluster Overview",
|
|
"panels": [
|
|
{ "type": "stat", "title": "Nodes Online", "targets": [{ "expr": "fester_nodes_online" }] },
|
|
{ "type": "stat", "title": "Active Jobs", "targets": [{ "expr": "fester_cluster_active_jobs" }] },
|
|
{ "type": "graph", "title": "Avg Heat", "targets": [{ "expr": "fester_cluster_avg_heat" }] }
|
|
]
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# ------------------------------------------------------------
|
|
# Systemd unit (optional)
|
|
# ------------------------------------------------------------
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
cat > /tmp/fester.service <<EOF
|
|
[Unit]
|
|
Description=Fester Distributed Build System
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
WorkingDirectory=$BASE_DIR
|
|
ExecStart=$(which python3) -m uvicorn backend.main:app --host 0.0.0.0 --port 8080
|
|
Restart=on-failure
|
|
User=$USER
|
|
Environment=PYTHONPATH=$BASE_DIR
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
echo ""
|
|
echo "📋 Systemd unit written to /tmp/fester.service"
|
|
echo " Install with: sudo mv /tmp/fester.service /etc/systemd/system/ && sudo systemctl enable --now fester"
|
|
fi
|
|
|
|
# ------------------------------------------------------------
|
|
# Done
|
|
# ------------------------------------------------------------
|
|
echo ""
|
|
echo "============================================================"
|
|
echo " ✅ Fester installed at $BASE_DIR"
|
|
echo "============================================================"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Start the server:"
|
|
echo " cd $BASE_DIR && python3 -m uvicorn backend.main:app --host 0.0.0.0 --port 8080"
|
|
echo ""
|
|
echo " 2. Open the UI:"
|
|
echo " http://localhost:8080/"
|
|
echo ""
|
|
echo " 3. Use the CLI:"
|
|
echo " $BASE_DIR/cli/fester.py status"
|
|
echo ""
|
|
echo " 4. (Optional) Install systemd unit (see above)"
|
|
echo ""
|