update deployment

This commit is contained in:
Forbes
2026-01-26 21:32:57 -06:00
parent f37052c2f0
commit 5201b35d83
5 changed files with 585 additions and 930 deletions

View File

@@ -1,23 +1,19 @@
#!/usr/bin/env bash
#
# Silo Deployment Script
# Deploys silod to silo.kindred.internal via systemd
# Pulls from git and deploys silod on the local machine
#
# Usage:
# ./scripts/deploy.sh [options]
# sudo ./scripts/deploy.sh [options]
#
# Options:
# --build-only Only build the binary, don't deploy
# --no-build Skip build, deploy existing binary
# --no-pull Skip git pull (use current checkout)
# --no-build Skip build (use existing binary)
# --restart-only Only restart the service
# --dry-run Show what would be done without doing it
# --status Show service status and exit
# --help Show this help message
#
# Environment variables:
# SILO_HOST Target host (default: silo.kindred.internal)
# SILO_USER SSH user (default: deploy)
# SILO_SSH_KEY Path to SSH private key (optional)
# BUILD_VERSION Version string for build (default: git describe)
# This script should be run on silo.kindred.internal as root or with sudo.
set -euo pipefail
@@ -29,27 +25,18 @@ BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
SILO_HOST="${SILO_HOST:-silo.kindred.internal}"
SILO_USER="${SILO_USER:-deploy}"
SILO_SSH_KEY="${SILO_SSH_KEY:-}"
BUILD_VERSION="${BUILD_VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo "dev")}"
# Paths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
BUILD_DIR="${PROJECT_ROOT}/build/out"
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"
# Remote paths
REMOTE_BIN_DIR="/opt/silo/bin"
REMOTE_CONFIG_DIR="/etc/silo"
REMOTE_SCHEMAS_DIR="/etc/silo/schemas"
REMOTE_SERVICE="silod"
SERVICE_NAME="silod"
# Flags
BUILD=true
DEPLOY=true
DRY_RUN=false
DO_PULL=true
DO_BUILD=true
RESTART_ONLY=false
STATUS_ONLY=false
# Functions
log_info() {
@@ -74,33 +61,13 @@ die() {
}
show_help() {
head -20 "$0" | grep -E '^#' | sed 's/^# *//'
head -18 "$0" | grep -E '^#' | sed 's/^# *//'
exit 0
}
ssh_cmd() {
local ssh_opts=(-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10)
if [[ -n "${SILO_SSH_KEY}" ]]; then
ssh_opts+=(-i "${SILO_SSH_KEY}")
fi
if [[ "${DRY_RUN}" == "true" ]]; then
echo "[DRY-RUN] ssh ${ssh_opts[*]} ${SILO_USER}@${SILO_HOST} $*"
else
ssh "${ssh_opts[@]}" "${SILO_USER}@${SILO_HOST}" "$@"
fi
}
scp_cmd() {
local scp_opts=(-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10)
if [[ -n "${SILO_SSH_KEY}" ]]; then
scp_opts+=(-i "${SILO_SSH_KEY}")
fi
if [[ "${DRY_RUN}" == "true" ]]; then
echo "[DRY-RUN] scp ${scp_opts[*]} $*"
else
scp "${scp_opts[@]}" "$@"
check_root() {
if [[ $EUID -ne 0 ]]; then
die "This script must be run as root (use sudo)"
fi
}
@@ -109,174 +76,188 @@ check_dependencies() {
local missing=()
command -v go >/dev/null 2>&1 || missing+=("go")
command -v ssh >/dev/null 2>&1 || missing+=("ssh")
command -v scp >/dev/null 2>&1 || missing+=("scp")
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"
}
check_connectivity() {
log_info "Checking connectivity to ${SILO_HOST}..."
setup_directories() {
log_info "Setting up directories..."
if [[ "${DRY_RUN}" == "true" ]]; then
log_info "[DRY-RUN] Would check SSH connectivity"
return 0
# 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
if ! ssh_cmd "echo 'Connection successful'" >/dev/null 2>&1; then
die "Cannot connect to ${SILO_HOST} as ${SILO_USER}"
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
log_success "SSH connection verified"
# 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} (version: ${BUILD_VERSION})..."
log_info "Building ${BINARY_NAME}..."
mkdir -p "${BUILD_DIR}"
local src_dir="${INSTALL_DIR}/src"
cd "${src_dir}"
local ldflags="-w -s -X main.Version=${BUILD_VERSION}"
# Get version from git
local version
version=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
if [[ "${DRY_RUN}" == "true" ]]; then
echo "[DRY-RUN] CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags=\"${ldflags}\" -o ${BUILD_DIR}/${BINARY_NAME} ./cmd/silod"
return 0
fi
local ldflags="-w -s -X main.Version=${version}"
cd "${PROJECT_ROOT}"
# Build
CGO_ENABLED=0 go build -ldflags="${ldflags}" -o "${INSTALL_DIR}/bin/${BINARY_NAME}" ./cmd/silod
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="${ldflags}" \
-o "${BUILD_DIR}/${BINARY_NAME}" \
./cmd/silod
if [[ ! -f "${BUILD_DIR}/${BINARY_NAME}" ]]; then
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 "${BUILD_DIR}/${BINARY_NAME}" | cut -f1)
size=$(du -h "${INSTALL_DIR}/bin/${BINARY_NAME}" | cut -f1)
log_success "Built ${BINARY_NAME} (${size})"
}
deploy_binary() {
log_info "Deploying binary to ${SILO_HOST}..."
install_config() {
log_info "Installing configuration..."
local binary="${BUILD_DIR}/${BINARY_NAME}"
local src_dir="${INSTALL_DIR}/src"
if [[ ! -f "${binary}" ]] && [[ "${DRY_RUN}" != "true" ]]; then
die "Binary not found: ${binary}"
# 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
# Upload to temp location first
scp_cmd "${binary}" "${SILO_USER}@${SILO_HOST}:/tmp/${BINARY_NAME}.new"
# 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"
# Move to final location with sudo
ssh_cmd "sudo mv /tmp/${BINARY_NAME}.new ${REMOTE_BIN_DIR}/${BINARY_NAME}"
ssh_cmd "sudo chmod 755 ${REMOTE_BIN_DIR}/${BINARY_NAME}"
ssh_cmd "sudo chown root:root ${REMOTE_BIN_DIR}/${BINARY_NAME}"
log_success "Binary deployed to ${REMOTE_BIN_DIR}/${BINARY_NAME}"
}
deploy_config() {
log_info "Deploying configuration..."
local config_file="${PROJECT_ROOT}/deployments/config.prod.yaml"
if [[ ! -f "${config_file}" ]] && [[ "${DRY_RUN}" != "true" ]]; then
die "Config file not found: ${config_file}"
# 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
scp_cmd "${config_file}" "${SILO_USER}@${SILO_HOST}:/tmp/config.yaml"
ssh_cmd "sudo mv /tmp/config.yaml ${REMOTE_CONFIG_DIR}/config.yaml"
ssh_cmd "sudo chmod 644 ${REMOTE_CONFIG_DIR}/config.yaml"
ssh_cmd "sudo chown root:silo ${REMOTE_CONFIG_DIR}/config.yaml"
log_success "Configuration deployed"
}
deploy_schemas() {
log_info "Deploying schemas..."
install_systemd() {
log_info "Installing systemd service..."
local schemas_dir="${PROJECT_ROOT}/schemas"
local src_dir="${INSTALL_DIR}/src"
local service_file="${src_dir}/deployments/systemd/silod.service"
if [[ ! -d "${schemas_dir}" ]] && [[ "${DRY_RUN}" != "true" ]]; then
die "Schemas directory not found: ${schemas_dir}"
fi
# Create temp directory and copy schemas
ssh_cmd "rm -rf /tmp/silo-schemas && mkdir -p /tmp/silo-schemas"
scp_cmd -r "${schemas_dir}/"* "${SILO_USER}@${SILO_HOST}:/tmp/silo-schemas/"
# Move to final location
ssh_cmd "sudo rm -rf ${REMOTE_SCHEMAS_DIR}/*"
ssh_cmd "sudo mv /tmp/silo-schemas/* ${REMOTE_SCHEMAS_DIR}/"
ssh_cmd "sudo chown -R root:silo ${REMOTE_SCHEMAS_DIR}"
ssh_cmd "sudo chmod -R 644 ${REMOTE_SCHEMAS_DIR}/*"
log_success "Schemas deployed"
}
deploy_systemd() {
log_info "Deploying systemd service..."
local service_file="${PROJECT_ROOT}/deployments/systemd/silod.service"
if [[ ! -f "${service_file}" ]] && [[ "${DRY_RUN}" != "true" ]]; then
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
}
scp_cmd "${service_file}" "${SILO_USER}@${SILO_HOST}:/tmp/silod.service"
ssh_cmd "sudo mv /tmp/silod.service /etc/systemd/system/silod.service"
ssh_cmd "sudo chmod 644 /etc/systemd/system/silod.service"
ssh_cmd "sudo systemctl daemon-reload"
set_permissions() {
log_info "Setting permissions..."
log_success "Systemd service deployed"
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 ${REMOTE_SERVICE} service..."
log_info "Restarting ${SERVICE_NAME} service..."
ssh_cmd "sudo systemctl restart ${REMOTE_SERVICE}"
# Enable if not already
systemctl enable "${SERVICE_NAME}" >/dev/null 2>&1 || true
# Wait for service to start
# Restart
systemctl restart "${SERVICE_NAME}"
# Wait for startup
sleep 2
if [[ "${DRY_RUN}" != "true" ]]; then
if ssh_cmd "sudo systemctl is-active --quiet ${REMOTE_SERVICE}"; then
log_success "Service restarted successfully"
else
log_error "Service failed to start"
ssh_cmd "sudo journalctl -u ${REMOTE_SERVICE} -n 20 --no-pager" || true
die "Deployment failed: service not running"
fi
if systemctl is-active --quiet "${SERVICE_NAME}"; then
log_success "Service started successfully"
else
log_info "[DRY-RUN] Would verify service is running"
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..."
if [[ "${DRY_RUN}" == "true" ]]; then
log_info "[DRY-RUN] Would verify health endpoints"
return 0
fi
# Wait a moment for service to fully start
sleep 3
sleep 2
# Check health endpoint
local health_status
health_status=$(ssh_cmd "curl -sf http://localhost:8080/health" 2>/dev/null || echo "FAILED")
health_status=$(curl -sf http://localhost:8080/health 2>/dev/null || echo "FAILED")
if [[ "${health_status}" == *"ok"* ]] || [[ "${health_status}" == *"healthy"* ]]; then
if [[ "${health_status}" == *"ok"* ]] || [[ "${health_status}" == *"healthy"* ]] || [[ "${health_status}" == "{}" ]]; then
log_success "Health check passed"
else
log_warn "Health check returned: ${health_status}"
@@ -284,46 +265,46 @@ verify_deployment() {
# Check ready endpoint (includes DB and MinIO)
local ready_status
ready_status=$(ssh_cmd "curl -sf http://localhost:8080/ready" 2>/dev/null || echo "FAILED")
ready_status=$(curl -sf http://localhost:8080/ready 2>/dev/null || echo "FAILED")
if [[ "${ready_status}" == *"ok"* ]] || [[ "${ready_status}" == *"ready"* ]]; then
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 "Service may still be starting or external services unavailable"
log_warn "Check credentials in ${CONFIG_DIR}/silod.env"
fi
# Show deployed version
local deployed_version
deployed_version=$(ssh_cmd "${REMOTE_BIN_DIR}/${BINARY_NAME} --version" 2>/dev/null || echo "unknown")
log_info "Deployed version: ${deployed_version}"
# Show version
log_info "Deployed version: $("${INSTALL_DIR}/bin/${BINARY_NAME}" --version 2>/dev/null || echo 'unknown')"
}
enable_service() {
log_info "Enabling ${REMOTE_SERVICE} service..."
ssh_cmd "sudo systemctl enable ${REMOTE_SERVICE}"
log_success "Service enabled for auto-start"
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
--build-only)
DEPLOY=false
--no-pull)
DO_PULL=false
shift
;;
--no-build)
BUILD=false
DO_BUILD=false
shift
;;
--restart-only)
BUILD=false
DEPLOY=false
restart_service
exit 0
RESTART_ONLY=true
shift
;;
--dry-run)
DRY_RUN=true
--status)
STATUS_ONLY=true
shift
;;
--help|-h)
@@ -337,36 +318,56 @@ done
# Main execution
main() {
echo ""
log_info "Silo Deployment Script"
log_info "======================"
log_info "Target: ${SILO_USER}@${SILO_HOST}"
log_info "Version: ${BUILD_VERSION}"
if [[ "${DRY_RUN}" == "true" ]]; then
log_warn "DRY-RUN MODE: No changes will be made"
fi
echo ""
check_dependencies
check_root
if [[ "${BUILD}" == "true" ]]; then
build_binary
if [[ "${STATUS_ONLY}" == "true" ]]; then
show_status
exit 0
fi
if [[ "${DEPLOY}" == "true" ]]; then
check_connectivity
deploy_binary
deploy_config
deploy_schemas
deploy_systemd
enable_service
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 "$@"