374 lines
9.5 KiB
Bash
Executable File
374 lines
9.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Silo Deployment Script
|
|
# Pulls from git and deploys silod on the local machine
|
|
#
|
|
# Usage:
|
|
# sudo ./scripts/deploy.sh [options]
|
|
#
|
|
# Options:
|
|
# --no-pull Skip git pull (use current checkout)
|
|
# --no-build Skip build (use existing binary)
|
|
# --restart-only Only restart the service
|
|
# --status Show service status and exit
|
|
# --help Show this help message
|
|
#
|
|
# This script should be run on silo.kindred.internal as root or with sudo.
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
REPO_URL="${SILO_REPO_URL:-https://gitea.kindred.internal/kindred/silo-0062.git}"
|
|
REPO_BRANCH="${SILO_BRANCH:-main}"
|
|
INSTALL_DIR="/opt/silo"
|
|
CONFIG_DIR="/etc/silo"
|
|
BINARY_NAME="silod"
|
|
SERVICE_NAME="silod"
|
|
|
|
# Flags
|
|
DO_PULL=true
|
|
DO_BUILD=true
|
|
RESTART_ONLY=false
|
|
STATUS_ONLY=false
|
|
|
|
# Functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $*"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[OK]${NC} $*"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $*"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $*" >&2
|
|
}
|
|
|
|
die() {
|
|
log_error "$*"
|
|
exit 1
|
|
}
|
|
|
|
show_help() {
|
|
head -18 "$0" | grep -E '^#' | sed 's/^# *//'
|
|
exit 0
|
|
}
|
|
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
die "This script must be run as root (use sudo)"
|
|
fi
|
|
}
|
|
|
|
check_dependencies() {
|
|
log_info "Checking dependencies..."
|
|
|
|
local missing=()
|
|
|
|
command -v git >/dev/null 2>&1 || missing+=("git")
|
|
command -v go >/dev/null 2>&1 || missing+=("go (golang)")
|
|
command -v systemctl >/dev/null 2>&1 || missing+=("systemctl")
|
|
|
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
die "Missing required commands: ${missing[*]}"
|
|
fi
|
|
|
|
# Check Go version
|
|
local go_version
|
|
go_version=$(go version | grep -oP 'go\d+\.\d+' | head -1)
|
|
log_info "Found Go version: ${go_version}"
|
|
|
|
log_success "All dependencies available"
|
|
}
|
|
|
|
setup_directories() {
|
|
log_info "Setting up directories..."
|
|
|
|
# Create directories if they don't exist
|
|
mkdir -p "${INSTALL_DIR}/bin"
|
|
mkdir -p "${INSTALL_DIR}/src"
|
|
mkdir -p "${CONFIG_DIR}/schemas"
|
|
mkdir -p /var/log/silo
|
|
|
|
# Create silo user if it doesn't exist
|
|
if ! id -u silo >/dev/null 2>&1; then
|
|
useradd -r -m -d "${INSTALL_DIR}" -s /sbin/nologin -c "Silo Service" silo
|
|
log_info "Created silo user"
|
|
fi
|
|
|
|
log_success "Directories ready"
|
|
}
|
|
|
|
git_pull() {
|
|
log_info "Pulling latest code from ${REPO_BRANCH}..."
|
|
|
|
local src_dir="${INSTALL_DIR}/src"
|
|
|
|
if [[ -d "${src_dir}/.git" ]]; then
|
|
# Existing checkout - pull updates
|
|
cd "${src_dir}"
|
|
git fetch origin
|
|
git checkout "${REPO_BRANCH}"
|
|
git reset --hard "origin/${REPO_BRANCH}"
|
|
log_success "Updated to $(git rev-parse --short HEAD)"
|
|
else
|
|
# Fresh clone
|
|
log_info "Cloning repository..."
|
|
rm -rf "${src_dir}"
|
|
git clone --branch "${REPO_BRANCH}" "${REPO_URL}" "${src_dir}"
|
|
cd "${src_dir}"
|
|
log_success "Cloned $(git rev-parse --short HEAD)"
|
|
fi
|
|
|
|
# Show version info
|
|
local version
|
|
version=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
|
|
log_info "Version: ${version}"
|
|
}
|
|
|
|
build_binary() {
|
|
log_info "Building ${BINARY_NAME}..."
|
|
|
|
local src_dir="${INSTALL_DIR}/src"
|
|
cd "${src_dir}"
|
|
|
|
# Get version from git
|
|
local version
|
|
version=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
|
|
|
|
local ldflags="-w -s -X main.Version=${version}"
|
|
|
|
# Build
|
|
CGO_ENABLED=0 go build -ldflags="${ldflags}" -o "${INSTALL_DIR}/bin/${BINARY_NAME}" ./cmd/silod
|
|
|
|
if [[ ! -f "${INSTALL_DIR}/bin/${BINARY_NAME}" ]]; then
|
|
die "Build failed: binary not found"
|
|
fi
|
|
|
|
chmod 755 "${INSTALL_DIR}/bin/${BINARY_NAME}"
|
|
|
|
local size
|
|
size=$(du -h "${INSTALL_DIR}/bin/${BINARY_NAME}" | cut -f1)
|
|
log_success "Built ${BINARY_NAME} (${size})"
|
|
}
|
|
|
|
install_config() {
|
|
log_info "Installing configuration..."
|
|
|
|
local src_dir="${INSTALL_DIR}/src"
|
|
|
|
# Install config file if it doesn't exist or is different
|
|
if [[ ! -f "${CONFIG_DIR}/config.yaml" ]]; then
|
|
cp "${src_dir}/deployments/config.prod.yaml" "${CONFIG_DIR}/config.yaml"
|
|
chmod 644 "${CONFIG_DIR}/config.yaml"
|
|
chown root:silo "${CONFIG_DIR}/config.yaml"
|
|
log_info "Installed config.yaml"
|
|
else
|
|
log_info "Config file exists, not overwriting"
|
|
fi
|
|
|
|
# Install schemas (always update)
|
|
rm -rf "${CONFIG_DIR}/schemas/"*
|
|
cp -r "${src_dir}/schemas/"* "${CONFIG_DIR}/schemas/"
|
|
chmod -R 644 "${CONFIG_DIR}/schemas/"*
|
|
chown -R root:silo "${CONFIG_DIR}/schemas"
|
|
log_success "Schemas installed"
|
|
|
|
# Check environment file
|
|
if [[ ! -f "${CONFIG_DIR}/silod.env" ]]; then
|
|
cp "${src_dir}/deployments/systemd/silod.env.example" "${CONFIG_DIR}/silod.env"
|
|
chmod 600 "${CONFIG_DIR}/silod.env"
|
|
chown root:silo "${CONFIG_DIR}/silod.env"
|
|
log_warn "Created ${CONFIG_DIR}/silod.env - EDIT THIS FILE WITH CREDENTIALS!"
|
|
fi
|
|
}
|
|
|
|
install_systemd() {
|
|
log_info "Installing systemd service..."
|
|
|
|
local src_dir="${INSTALL_DIR}/src"
|
|
local service_file="${src_dir}/deployments/systemd/silod.service"
|
|
|
|
if [[ -f "${service_file}" ]]; then
|
|
cp "${service_file}" /etc/systemd/system/silod.service
|
|
chmod 644 /etc/systemd/system/silod.service
|
|
systemctl daemon-reload
|
|
log_success "Systemd service installed"
|
|
else
|
|
die "Service file not found: ${service_file}"
|
|
fi
|
|
}
|
|
|
|
set_permissions() {
|
|
log_info "Setting permissions..."
|
|
|
|
chown -R silo:silo "${INSTALL_DIR}"
|
|
chown root:silo "${CONFIG_DIR}"
|
|
chmod 750 "${CONFIG_DIR}"
|
|
chown silo:silo /var/log/silo
|
|
chmod 750 /var/log/silo
|
|
|
|
# Binary should be owned by root but executable by silo
|
|
chown root:root "${INSTALL_DIR}/bin/${BINARY_NAME}"
|
|
chmod 755 "${INSTALL_DIR}/bin/${BINARY_NAME}"
|
|
|
|
log_success "Permissions set"
|
|
}
|
|
|
|
restart_service() {
|
|
log_info "Restarting ${SERVICE_NAME} service..."
|
|
|
|
# Enable if not already
|
|
systemctl enable "${SERVICE_NAME}" >/dev/null 2>&1 || true
|
|
|
|
# Restart
|
|
systemctl restart "${SERVICE_NAME}"
|
|
|
|
# Wait for startup
|
|
sleep 2
|
|
|
|
if systemctl is-active --quiet "${SERVICE_NAME}"; then
|
|
log_success "Service started successfully"
|
|
else
|
|
log_error "Service failed to start"
|
|
journalctl -u "${SERVICE_NAME}" -n 20 --no-pager || true
|
|
die "Deployment failed: service not running"
|
|
fi
|
|
}
|
|
|
|
verify_deployment() {
|
|
log_info "Verifying deployment..."
|
|
|
|
# Wait a moment for service to fully start
|
|
sleep 2
|
|
|
|
# Check health endpoint
|
|
local health_status
|
|
health_status=$(curl -sf http://localhost:8080/health 2>/dev/null || echo "FAILED")
|
|
|
|
if [[ "${health_status}" == *"ok"* ]] || [[ "${health_status}" == *"healthy"* ]] || [[ "${health_status}" == "{}" ]]; then
|
|
log_success "Health check passed"
|
|
else
|
|
log_warn "Health check returned: ${health_status}"
|
|
fi
|
|
|
|
# Check ready endpoint (includes DB and MinIO)
|
|
local ready_status
|
|
ready_status=$(curl -sf http://localhost:8080/ready 2>/dev/null || echo "FAILED")
|
|
|
|
if [[ "${ready_status}" == *"ok"* ]] || [[ "${ready_status}" == *"ready"* ]] || [[ "${ready_status}" == "{}" ]]; then
|
|
log_success "Readiness check passed (DB and MinIO connected)"
|
|
else
|
|
log_warn "Readiness check returned: ${ready_status}"
|
|
log_warn "Check credentials in ${CONFIG_DIR}/silod.env"
|
|
fi
|
|
|
|
# Show version
|
|
log_info "Deployed version: $("${INSTALL_DIR}/bin/${BINARY_NAME}" --version 2>/dev/null || echo 'unknown')"
|
|
}
|
|
|
|
show_status() {
|
|
echo ""
|
|
log_info "Service Status"
|
|
echo "============================================"
|
|
systemctl status "${SERVICE_NAME}" --no-pager -l || true
|
|
echo ""
|
|
echo "Recent logs:"
|
|
journalctl -u "${SERVICE_NAME}" -n 10 --no-pager || true
|
|
}
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--no-pull)
|
|
DO_PULL=false
|
|
shift
|
|
;;
|
|
--no-build)
|
|
DO_BUILD=false
|
|
shift
|
|
;;
|
|
--restart-only)
|
|
RESTART_ONLY=true
|
|
shift
|
|
;;
|
|
--status)
|
|
STATUS_ONLY=true
|
|
shift
|
|
;;
|
|
--help|-h)
|
|
show_help
|
|
;;
|
|
*)
|
|
die "Unknown option: $1"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Main execution
|
|
main() {
|
|
echo ""
|
|
log_info "Silo Deployment Script"
|
|
log_info "======================"
|
|
echo ""
|
|
|
|
check_root
|
|
|
|
if [[ "${STATUS_ONLY}" == "true" ]]; then
|
|
show_status
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "${RESTART_ONLY}" == "true" ]]; then
|
|
restart_service
|
|
verify_deployment
|
|
exit 0
|
|
fi
|
|
|
|
check_dependencies
|
|
setup_directories
|
|
|
|
if [[ "${DO_PULL}" == "true" ]]; then
|
|
git_pull
|
|
else
|
|
log_info "Skipping git pull (--no-pull)"
|
|
cd "${INSTALL_DIR}/src"
|
|
fi
|
|
|
|
if [[ "${DO_BUILD}" == "true" ]]; then
|
|
build_binary
|
|
else
|
|
log_info "Skipping build (--no-build)"
|
|
fi
|
|
|
|
install_config
|
|
install_systemd
|
|
set_permissions
|
|
restart_service
|
|
verify_deployment
|
|
|
|
echo ""
|
|
log_success "============================================"
|
|
log_success "Deployment complete!"
|
|
log_success "============================================"
|
|
echo ""
|
|
echo "Useful commands:"
|
|
echo " sudo systemctl status silod # Check service status"
|
|
echo " sudo journalctl -u silod -f # Follow logs"
|
|
echo " curl http://localhost:8080/health # Health check"
|
|
echo ""
|
|
}
|
|
|
|
main "$@"
|