update deployment
This commit is contained in:
@@ -1,160 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- 'feature/**'
|
|
||||||
- 'fix/**'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: '1.23'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run go vet
|
|
||||||
run: go vet ./...
|
|
||||||
|
|
||||||
- name: Check go mod tidy
|
|
||||||
run: |
|
|
||||||
go mod tidy
|
|
||||||
git diff --exit-code go.mod go.sum
|
|
||||||
|
|
||||||
- name: Check formatting
|
|
||||||
run: |
|
|
||||||
gofmt -l .
|
|
||||||
test -z "$(gofmt -l .)"
|
|
||||||
|
|
||||||
test:
|
|
||||||
name: Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: go test -v -race -coverprofile=coverage.out ./...
|
|
||||||
|
|
||||||
- name: Upload coverage
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: coverage
|
|
||||||
path: coverage.out
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
goos: [linux]
|
|
||||||
goarch: [amd64, arm64]
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Get version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev-$(git rev-parse --short HEAD)")
|
|
||||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Build silod
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
run: |
|
|
||||||
mkdir -p build/out
|
|
||||||
go build -ldflags="-w -s -X main.Version=${{ steps.version.outputs.version }}" \
|
|
||||||
-o build/out/silod-${{ matrix.goos }}-${{ matrix.goarch }} \
|
|
||||||
./cmd/silod
|
|
||||||
|
|
||||||
- name: Build silo CLI
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
run: |
|
|
||||||
go build -ldflags="-w -s -X main.Version=${{ steps.version.outputs.version }}" \
|
|
||||||
-o build/out/silo-${{ matrix.goos }}-${{ matrix.goarch }} \
|
|
||||||
./cmd/silo
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: binaries-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
||||||
path: build/out/
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
schema-validate:
|
|
||||||
name: Validate Schemas
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Validate YAML schemas
|
|
||||||
run: |
|
|
||||||
for f in schemas/*.yaml; do
|
|
||||||
echo "Validating $f..."
|
|
||||||
# Use Go to parse and validate schema
|
|
||||||
go run -exec "echo" ./cmd/silo 2>/dev/null || true
|
|
||||||
done
|
|
||||||
echo "Schema files are valid YAML"
|
|
||||||
|
|
||||||
docker:
|
|
||||||
name: Docker Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [lint, test]
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build Docker image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./build/package/Dockerfile
|
|
||||||
push: false
|
|
||||||
tags: silo:ci-${{ github.sha }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
name: Deploy Silo
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
- 'docs/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
environment:
|
|
||||||
description: 'Deployment environment'
|
|
||||||
required: true
|
|
||||||
default: 'production'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- production
|
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: '1.23'
|
|
||||||
BINARY_NAME: silod
|
|
||||||
DEPLOY_HOST: silo.kindred.internal
|
|
||||||
DEPLOY_USER: deploy
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # Full history for git describe
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Get version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev-$(git rev-parse --short HEAD)")
|
|
||||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
|
||||||
echo "Building version: ${VERSION}"
|
|
||||||
|
|
||||||
- name: Build binary
|
|
||||||
run: |
|
|
||||||
mkdir -p build/out
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
|
||||||
go build -ldflags="-w -s -X main.Version=${{ steps.version.outputs.version }}" \
|
|
||||||
-o build/out/${{ env.BINARY_NAME }} \
|
|
||||||
./cmd/silod
|
|
||||||
|
|
||||||
- name: Verify binary
|
|
||||||
run: |
|
|
||||||
file build/out/${{ env.BINARY_NAME }}
|
|
||||||
ls -lh build/out/${{ env.BINARY_NAME }}
|
|
||||||
|
|
||||||
- name: Upload binary artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: silod-binary
|
|
||||||
path: build/out/${{ env.BINARY_NAME }}
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
- name: Upload config artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: silo-config
|
|
||||||
path: |
|
|
||||||
deployments/config.prod.yaml
|
|
||||||
deployments/systemd/silod.service
|
|
||||||
schemas/
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
test:
|
|
||||||
name: Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: go test -v -race ./...
|
|
||||||
|
|
||||||
- name: Run go vet
|
|
||||||
run: go vet ./...
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
name: Deploy to Production
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build, test]
|
|
||||||
environment: production
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Download binary artifact
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: silod-binary
|
|
||||||
path: build/out
|
|
||||||
|
|
||||||
- name: Download config artifact
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: silo-config
|
|
||||||
path: deploy-config
|
|
||||||
|
|
||||||
- name: Setup SSH key
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key
|
|
||||||
chmod 600 ~/.ssh/deploy_key
|
|
||||||
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
|
||||||
|
|
||||||
- name: Stop service
|
|
||||||
run: |
|
|
||||||
ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new \
|
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
|
||||||
"sudo systemctl stop silod || true"
|
|
||||||
|
|
||||||
- name: Deploy binary
|
|
||||||
run: |
|
|
||||||
chmod +x build/out/${{ env.BINARY_NAME }}
|
|
||||||
scp -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new \
|
|
||||||
build/out/${{ env.BINARY_NAME }} \
|
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/${{ env.BINARY_NAME }}.new
|
|
||||||
|
|
||||||
ssh -i ~/.ssh/deploy_key ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
sudo mv /tmp/silod.new /opt/silo/bin/silod
|
|
||||||
sudo chmod 755 /opt/silo/bin/silod
|
|
||||||
sudo chown root:root /opt/silo/bin/silod
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Deploy configuration
|
|
||||||
run: |
|
|
||||||
scp -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new \
|
|
||||||
deploy-config/deployments/config.prod.yaml \
|
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/config.yaml
|
|
||||||
|
|
||||||
ssh -i ~/.ssh/deploy_key ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
sudo mv /tmp/config.yaml /etc/silo/config.yaml
|
|
||||||
sudo chmod 644 /etc/silo/config.yaml
|
|
||||||
sudo chown root:silo /etc/silo/config.yaml
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Deploy schemas
|
|
||||||
run: |
|
|
||||||
scp -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new -r \
|
|
||||||
deploy-config/schemas/* \
|
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/silo-schemas/
|
|
||||||
|
|
||||||
ssh -i ~/.ssh/deploy_key ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
sudo rm -rf /etc/silo/schemas/*
|
|
||||||
sudo mv /tmp/silo-schemas/* /etc/silo/schemas/
|
|
||||||
sudo chown -R root:silo /etc/silo/schemas
|
|
||||||
sudo chmod -R 644 /etc/silo/schemas/*
|
|
||||||
rm -rf /tmp/silo-schemas
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Deploy systemd service
|
|
||||||
run: |
|
|
||||||
scp -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new \
|
|
||||||
deploy-config/deployments/systemd/silod.service \
|
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/silod.service
|
|
||||||
|
|
||||||
ssh -i ~/.ssh/deploy_key ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
sudo mv /tmp/silod.service /etc/systemd/system/silod.service
|
|
||||||
sudo chmod 644 /etc/systemd/system/silod.service
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Start and enable service
|
|
||||||
run: |
|
|
||||||
ssh -i ~/.ssh/deploy_key ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
sudo systemctl enable silod
|
|
||||||
sudo systemctl start silod
|
|
||||||
sleep 3
|
|
||||||
sudo systemctl is-active --quiet silod && echo "Service started successfully" || exit 1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Verify deployment
|
|
||||||
run: |
|
|
||||||
ssh -i ~/.ssh/deploy_key ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
echo "Checking health endpoint..."
|
|
||||||
curl -sf http://localhost:8080/health || echo "Health check pending..."
|
|
||||||
|
|
||||||
echo "Checking readiness endpoint..."
|
|
||||||
curl -sf http://localhost:8080/ready || echo "Readiness check pending..."
|
|
||||||
|
|
||||||
echo "Service status:"
|
|
||||||
sudo systemctl status silod --no-pager -l
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Cleanup SSH key
|
|
||||||
if: always()
|
|
||||||
run: rm -f ~/.ssh/deploy_key
|
|
||||||
@@ -6,9 +6,10 @@ This guide covers deploying Silo to a dedicated VM using external PostgreSQL and
|
|||||||
|
|
||||||
- [Architecture](#architecture)
|
- [Architecture](#architecture)
|
||||||
- [External Services](#external-services)
|
- [External Services](#external-services)
|
||||||
- [Automated Deployment (CI/CD)](#automated-deployment-cicd)
|
- [Quick Start](#quick-start)
|
||||||
- [Manual Deployment](#manual-deployment)
|
- [Initial Setup](#initial-setup)
|
||||||
- [Post-Deployment Configuration](#post-deployment-configuration)
|
- [Deployment](#deployment)
|
||||||
|
- [Configuration](#configuration)
|
||||||
- [Maintenance](#maintenance)
|
- [Maintenance](#maintenance)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
@@ -33,377 +34,343 @@ This guide covers deploying Silo to a dedicated VM using external PostgreSQL and
|
|||||||
└─────────────────────────┘ └─────────────────────────────────┘
|
└─────────────────────────┘ └─────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
## Prerequisites
|
## External Services
|
||||||
|
|
||||||
### On psql.kindred.internal
|
The following external services are already configured:
|
||||||
|
|
||||||
1. Create the Silo database and user:
|
| Service | Host | Database/Bucket | User |
|
||||||
|
|---------|------|-----------------|------|
|
||||||
|
| PostgreSQL | psql.kindred.internal:5432 | silo | silo |
|
||||||
|
| MinIO | minio.kindred.internal:9000 | silo-files | silouser |
|
||||||
|
|
||||||
```sql
|
Migrations have been applied to the database.
|
||||||
-- Connect as postgres superuser
|
|
||||||
CREATE USER silo WITH PASSWORD 'your-secure-password';
|
|
||||||
CREATE DATABASE silo OWNER silo;
|
|
||||||
|
|
||||||
-- Grant necessary permissions
|
|
||||||
GRANT ALL PRIVILEGES ON DATABASE silo TO silo;
|
|
||||||
|
|
||||||
-- Connect to silo database
|
|
||||||
\c silo
|
|
||||||
|
|
||||||
-- Enable UUID extension
|
|
||||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Run migrations:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# From the silo project directory
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/001_initial.sql
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/002_sequence_by_name.sql
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/003_remove_material.sql
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/004_cad_sync_state.sql
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/005_property_schema_version.sql
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/006_project_tags.sql
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/007_revision_status.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
Or run all at once:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in migrations/*.sql; do
|
|
||||||
echo "Running $f..."
|
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f "$f"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Allow connections from Silo VM in `pg_hba.conf`:
|
|
||||||
|
|
||||||
```
|
|
||||||
# Allow silo.kindred.internal to connect
|
|
||||||
hostssl silo silo silo.kindred.internal/32 scram-sha-256
|
|
||||||
```
|
|
||||||
|
|
||||||
### On minio.kindred.internal
|
|
||||||
|
|
||||||
1. Create the Silo bucket and access credentials:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Using mc (MinIO Client)
|
|
||||||
mc alias set kindred https://minio.kindred.internal ADMIN_ACCESS_KEY ADMIN_SECRET_KEY
|
|
||||||
|
|
||||||
# Create bucket with versioning
|
|
||||||
mc mb kindred/silo-files
|
|
||||||
mc version enable kindred/silo-files
|
|
||||||
|
|
||||||
# Create service account for Silo
|
|
||||||
mc admin user add kindred silo-service 'your-minio-secret-key'
|
|
||||||
|
|
||||||
# Create policy for silo-files bucket
|
|
||||||
cat > /tmp/silo-policy.json << 'EOF'
|
|
||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"s3:GetObject",
|
|
||||||
"s3:PutObject",
|
|
||||||
"s3:DeleteObject",
|
|
||||||
"s3:ListBucket",
|
|
||||||
"s3:GetBucketVersioning",
|
|
||||||
"s3:GetObjectVersion",
|
|
||||||
"s3:DeleteObjectVersion"
|
|
||||||
],
|
|
||||||
"Resource": [
|
|
||||||
"arn:aws:s3:::silo-files",
|
|
||||||
"arn:aws:s3:::silo-files/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
mc admin policy create kindred silo-policy /tmp/silo-policy.json
|
|
||||||
mc admin policy attach kindred silo-policy --user silo-service
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Verify SSL certificate is valid (or configure Silo to use non-SSL if internal).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deployment Options
|
## Quick Start
|
||||||
|
|
||||||
### Option A: Systemd Service (Recommended for Production)
|
For a fresh VM, run these commands:
|
||||||
|
|
||||||
#### 1. Prepare the Silo VM
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create silo user
|
# 1. SSH to the target host
|
||||||
sudo useradd -r -m -d /opt/silo -s /sbin/nologin silo
|
ssh root@silo.kindred.internal
|
||||||
|
|
||||||
# Create directories
|
# 2. Download and run setup script
|
||||||
sudo mkdir -p /opt/silo/bin
|
curl -fsSL https://gitea.kindred.internal/kindred/silo-0062/raw/branch/main/scripts/setup-host.sh | bash
|
||||||
sudo mkdir -p /etc/silo/schemas
|
|
||||||
sudo mkdir -p /var/log/silo
|
|
||||||
|
|
||||||
# Set ownership
|
# 3. Configure credentials
|
||||||
sudo chown -R silo:silo /opt/silo /var/log/silo
|
nano /etc/silo/silod.env
|
||||||
sudo chown root:silo /etc/silo
|
|
||||||
sudo chmod 750 /etc/silo
|
# 4. Deploy
|
||||||
|
/opt/silo/src/scripts/deploy.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Build and Install Binary
|
---
|
||||||
|
|
||||||
|
## Initial Setup
|
||||||
|
|
||||||
|
Run the setup script once on `silo.kindred.internal` to prepare the host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# On build machine (requires Go 1.23+)
|
# Option 1: If you have the repo locally
|
||||||
cd /path/to/silo
|
scp scripts/setup-host.sh root@silo.kindred.internal:/tmp/
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o silod ./cmd/silod
|
ssh root@silo.kindred.internal 'bash /tmp/setup-host.sh'
|
||||||
|
|
||||||
# Copy to Silo VM
|
# Option 2: Direct on the host
|
||||||
scp silod silo.kindred.internal:/tmp/
|
ssh root@silo.kindred.internal
|
||||||
ssh silo.kindred.internal "sudo mv /tmp/silod /opt/silo/bin/ && sudo chmod 755 /opt/silo/bin/silod"
|
curl -fsSL https://git.kindred.internal/kindred/silo/raw/branch/main/scripts/setup-host.sh -o /tmp/setup-host.sh
|
||||||
|
bash /tmp/setup-host.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Install Configuration
|
The setup script:
|
||||||
|
- Installs dependencies (git, Go 1.23)
|
||||||
|
- Creates the `silo` system user
|
||||||
|
- Creates directory structure (`/opt/silo`, `/etc/silo`)
|
||||||
|
- Clones the repository to `/opt/silo/src`
|
||||||
|
- Creates the environment file template
|
||||||
|
|
||||||
|
### Configure Credentials
|
||||||
|
|
||||||
|
After setup, edit the environment file with your credentials:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Copy config file
|
sudo nano /etc/silo/silod.env
|
||||||
scp deployments/config.prod.yaml silo.kindred.internal:/tmp/config.yaml
|
```
|
||||||
ssh silo.kindred.internal "sudo mv /tmp/config.yaml /etc/silo/config.yaml"
|
|
||||||
|
|
||||||
# Copy schemas
|
Fill in the values:
|
||||||
scp -r schemas/* silo.kindred.internal:/tmp/schemas/
|
|
||||||
ssh silo.kindred.internal "sudo mv /tmp/schemas/* /etc/silo/schemas/"
|
|
||||||
|
|
||||||
# Create environment file with secrets
|
```bash
|
||||||
ssh silo.kindred.internal
|
# Database credentials (psql.kindred.internal)
|
||||||
sudo cat > /etc/silo/silod.env << 'EOF'
|
|
||||||
SILO_DB_PASSWORD=your-database-password
|
SILO_DB_PASSWORD=your-database-password
|
||||||
SILO_MINIO_ACCESS_KEY=silo-service
|
|
||||||
|
# MinIO credentials (minio.kindred.internal)
|
||||||
|
SILO_MINIO_ACCESS_KEY=silouser
|
||||||
SILO_MINIO_SECRET_KEY=your-minio-secret-key
|
SILO_MINIO_SECRET_KEY=your-minio-secret-key
|
||||||
EOF
|
|
||||||
sudo chmod 600 /etc/silo/silod.env
|
|
||||||
sudo chown root:silo /etc/silo/silod.env
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4. Install Systemd Service
|
### Verify External Services
|
||||||
|
|
||||||
|
Before deploying, verify connectivity to external services:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Copy service file
|
# Test PostgreSQL
|
||||||
scp deployments/systemd/silod.service silo.kindred.internal:/tmp/
|
psql -h psql.kindred.internal -U silo -d silo -c 'SELECT 1'
|
||||||
ssh silo.kindred.internal "sudo mv /tmp/silod.service /etc/systemd/system/"
|
|
||||||
|
|
||||||
# Enable and start
|
# Test MinIO
|
||||||
ssh silo.kindred.internal << 'EOF'
|
curl -I http://minio.kindred.internal:9000/minio/health/live
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable silod
|
|
||||||
sudo systemctl start silod
|
|
||||||
sudo systemctl status silod
|
|
||||||
EOF
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 5. Verify Deployment
|
---
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Deploy (or Update)
|
||||||
|
|
||||||
|
To deploy or update Silo, run the deploy script on the target host:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
ssh root@silo.kindred.internal
|
||||||
|
/opt/silo/src/scripts/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The deploy script:
|
||||||
|
1. Pulls the latest code from git
|
||||||
|
2. Builds the `silod` binary
|
||||||
|
3. Installs configuration and schemas
|
||||||
|
4. Installs/updates the systemd service
|
||||||
|
5. Restarts the service
|
||||||
|
6. Verifies health endpoints
|
||||||
|
|
||||||
|
### Deploy Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Full deployment (pull, build, deploy, restart)
|
||||||
|
sudo /opt/silo/src/scripts/deploy.sh
|
||||||
|
|
||||||
|
# Skip git pull (use current checkout)
|
||||||
|
sudo /opt/silo/src/scripts/deploy.sh --no-pull
|
||||||
|
|
||||||
|
# Skip build (use existing binary)
|
||||||
|
sudo /opt/silo/src/scripts/deploy.sh --no-build
|
||||||
|
|
||||||
|
# Just restart the service
|
||||||
|
sudo /opt/silo/src/scripts/deploy.sh --restart-only
|
||||||
|
|
||||||
# Check service status
|
# Check service status
|
||||||
sudo systemctl status silod
|
sudo /opt/silo/src/scripts/deploy.sh --status
|
||||||
|
```
|
||||||
|
|
||||||
# Check logs
|
### Environment Variables
|
||||||
sudo journalctl -u silod -f
|
|
||||||
|
|
||||||
# Test health endpoint
|
You can override the git repository URL and branch:
|
||||||
curl http://localhost:8080/health
|
|
||||||
|
|
||||||
# Test readiness (verifies DB and MinIO connectivity)
|
```bash
|
||||||
curl http://localhost:8080/ready
|
export SILO_REPO_URL=https://git.kindred.internal/kindred/silo.git
|
||||||
|
export SILO_BRANCH=main
|
||||||
|
sudo -E /opt/silo/src/scripts/deploy.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Option B: Docker Compose
|
## Configuration
|
||||||
|
|
||||||
#### 1. Install Docker on Silo VM
|
### File Locations
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `/opt/silo/bin/silod` | Server binary |
|
||||||
|
| `/opt/silo/src/` | Git repository checkout |
|
||||||
|
| `/etc/silo/config.yaml` | Server configuration |
|
||||||
|
| `/etc/silo/silod.env` | Environment variables (secrets) |
|
||||||
|
| `/etc/silo/schemas/` | Part numbering schemas |
|
||||||
|
| `/var/log/silo/` | Log directory |
|
||||||
|
|
||||||
|
### Configuration File
|
||||||
|
|
||||||
|
The configuration file `/etc/silo/config.yaml` is installed on first deployment and not overwritten on updates. To update it manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Debian/Ubuntu
|
sudo cp /opt/silo/src/deployments/config.prod.yaml /etc/silo/config.yaml
|
||||||
sudo apt-get update
|
sudo systemctl restart silod
|
||||||
sudo apt-get install -y docker.io docker-compose-plugin
|
|
||||||
sudo usermod -aG docker $USER
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Clone Repository
|
### Schemas
|
||||||
|
|
||||||
```bash
|
Schemas in `/etc/silo/schemas/` are updated on every deployment from the repository.
|
||||||
git clone https://github.com/kindred-systems/silo.git /opt/silo
|
|
||||||
cd /opt/silo
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. Configure Environment
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create .env file
|
|
||||||
cat > /opt/silo/deployments/.env << 'EOF'
|
|
||||||
SILO_DB_PASSWORD=your-database-password
|
|
||||||
SILO_MINIO_ACCESS_KEY=silo-service
|
|
||||||
SILO_MINIO_SECRET_KEY=your-minio-secret-key
|
|
||||||
SILO_BASE_URL=http://silo.kindred.internal:8080
|
|
||||||
EOF
|
|
||||||
chmod 600 /opt/silo/deployments/.env
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 4. Start Service
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/silo/deployments
|
|
||||||
docker compose -f docker-compose.prod.yaml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5. Verify
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.prod.yaml ps
|
|
||||||
docker compose -f docker-compose.prod.yaml logs -f
|
|
||||||
curl http://localhost:8080/ready
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Post-Deployment Configuration
|
|
||||||
|
|
||||||
### DNS Setup
|
|
||||||
|
|
||||||
Add DNS records for `silo.kindred.internal` pointing to the Silo VM IP address.
|
|
||||||
|
|
||||||
### Firewall Rules
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Allow incoming connections on port 8080
|
|
||||||
sudo ufw allow 8080/tcp
|
|
||||||
|
|
||||||
# Or with iptables
|
|
||||||
sudo iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reverse Proxy (Optional)
|
|
||||||
|
|
||||||
For TLS termination, configure nginx or caddy:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
# /etc/nginx/sites-available/silo
|
|
||||||
server {
|
|
||||||
listen 443 ssl http2;
|
|
||||||
server_name silo.kindred.internal;
|
|
||||||
|
|
||||||
ssl_certificate /etc/ssl/certs/silo.kindred.internal.crt;
|
|
||||||
ssl_certificate_key /etc/ssl/private/silo.kindred.internal.key;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://127.0.0.1:8080;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Maintenance
|
## Maintenance
|
||||||
|
|
||||||
|
### Service Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check status
|
||||||
|
sudo systemctl status silod
|
||||||
|
|
||||||
|
# Start/stop/restart
|
||||||
|
sudo systemctl start silod
|
||||||
|
sudo systemctl stop silod
|
||||||
|
sudo systemctl restart silod
|
||||||
|
|
||||||
|
# Enable/disable auto-start
|
||||||
|
sudo systemctl enable silod
|
||||||
|
sudo systemctl disable silod
|
||||||
|
```
|
||||||
|
|
||||||
### View Logs
|
### View Logs
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Systemd
|
# Follow logs
|
||||||
sudo journalctl -u silod -f
|
sudo journalctl -u silod -f
|
||||||
sudo journalctl -u silod --since "1 hour ago"
|
|
||||||
|
|
||||||
# Docker
|
# Recent logs
|
||||||
docker compose -f docker-compose.prod.yaml logs -f silo
|
sudo journalctl -u silod -n 100
|
||||||
|
|
||||||
|
# Logs since a time
|
||||||
|
sudo journalctl -u silod --since "1 hour ago"
|
||||||
|
sudo journalctl -u silod --since "2024-01-15 10:00:00"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restart Service
|
### Health Checks
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Systemd
|
# Basic health check
|
||||||
sudo systemctl restart silod
|
curl http://localhost:8080/health
|
||||||
|
|
||||||
# Docker
|
# Full readiness check (includes DB and MinIO)
|
||||||
docker compose -f docker-compose.prod.yaml restart silo
|
curl http://localhost:8080/ready
|
||||||
```
|
```
|
||||||
|
|
||||||
### Update Deployment
|
### Update Deployment
|
||||||
|
|
||||||
```bash
|
To update to the latest version:
|
||||||
# Systemd - rebuild and replace binary
|
|
||||||
go build -ldflags="-w -s" -o silod ./cmd/silod
|
|
||||||
sudo systemctl stop silod
|
|
||||||
sudo cp silod /opt/silo/bin/silod
|
|
||||||
sudo systemctl start silod
|
|
||||||
|
|
||||||
# Docker - rebuild and restart
|
```bash
|
||||||
docker compose -f docker-compose.prod.yaml build
|
ssh root@silo.kindred.internal
|
||||||
docker compose -f docker-compose.prod.yaml up -d
|
/opt/silo/src/scripts/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
To deploy a specific branch or tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/silo/src
|
||||||
|
git fetch --all --tags
|
||||||
|
git checkout v1.2.3 # or a branch name
|
||||||
|
sudo /opt/silo/src/scripts/deploy.sh --no-pull
|
||||||
```
|
```
|
||||||
|
|
||||||
### Database Migrations
|
### Database Migrations
|
||||||
|
|
||||||
When updating Silo, check for new migrations:
|
When new migrations are added, run them manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# List migration files
|
# Check for new migrations
|
||||||
ls -la migrations/
|
ls -la /opt/silo/src/migrations/
|
||||||
|
|
||||||
# Run new migrations
|
# Run a specific migration
|
||||||
psql -h psql.kindred.internal -U silo -d silo -f migrations/008_new_feature.sql
|
psql -h psql.kindred.internal -U silo -d silo -f /opt/silo/src/migrations/008_new_feature.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Service Won't Start
|
||||||
|
|
||||||
|
1. Check logs for errors:
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u silod -n 50
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify configuration:
|
||||||
|
```bash
|
||||||
|
cat /etc/silo/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Check environment file permissions:
|
||||||
|
```bash
|
||||||
|
ls -la /etc/silo/silod.env
|
||||||
|
# Should be: -rw------- root silo
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Verify binary exists:
|
||||||
|
```bash
|
||||||
|
ls -la /opt/silo/bin/silod
|
||||||
|
```
|
||||||
|
|
||||||
### Connection Refused to PostgreSQL
|
### Connection Refused to PostgreSQL
|
||||||
|
|
||||||
1. Verify network connectivity: `nc -zv psql.kindred.internal 5432`
|
1. Test network connectivity:
|
||||||
2. Check `pg_hba.conf` allows connections from Silo VM
|
```bash
|
||||||
3. Verify firewall rules on PostgreSQL server
|
nc -zv psql.kindred.internal 5432
|
||||||
4. Check credentials in `/etc/silo/silod.env`
|
```
|
||||||
|
|
||||||
|
2. Test credentials:
|
||||||
|
```bash
|
||||||
|
source /etc/silo/silod.env
|
||||||
|
PGPASSWORD=$SILO_DB_PASSWORD psql -h psql.kindred.internal -U silo -d silo -c 'SELECT 1'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Check `pg_hba.conf` on PostgreSQL server allows connections from this host.
|
||||||
|
|
||||||
### Connection Refused to MinIO
|
### Connection Refused to MinIO
|
||||||
|
|
||||||
1. Verify network connectivity: `nc -zv minio.kindred.internal 9000`
|
1. Test network connectivity:
|
||||||
2. Check SSL settings match (use_ssl: true/false)
|
```bash
|
||||||
3. Verify access key and secret key
|
nc -zv minio.kindred.internal 9000
|
||||||
4. Check bucket exists: `mc ls kindred/silo-files`
|
```
|
||||||
|
|
||||||
### Service Won't Start
|
2. Test with curl:
|
||||||
|
```bash
|
||||||
|
curl -I http://minio.kindred.internal:9000/minio/health/live
|
||||||
|
```
|
||||||
|
|
||||||
1. Check logs: `sudo journalctl -u silod -n 50`
|
3. Check SSL settings in config match MinIO setup:
|
||||||
2. Verify config syntax: `/opt/silo/bin/silod -config /etc/silo/config.yaml -validate`
|
```yaml
|
||||||
3. Check file permissions on config and env files
|
storage:
|
||||||
4. Verify schemas directory exists and contains YAML files
|
use_ssl: true # or false
|
||||||
|
```
|
||||||
|
|
||||||
### Health Check Fails
|
### Health Check Fails
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Test each component
|
# Check individual endpoints
|
||||||
curl http://localhost:8080/health # Basic health
|
curl -v http://localhost:8080/health
|
||||||
curl http://localhost:8080/ready # Full readiness (DB + MinIO)
|
curl -v http://localhost:8080/ready
|
||||||
|
|
||||||
# If ready fails, check individual services
|
# If ready fails but health passes, check external services
|
||||||
psql -h psql.kindred.internal -U silo -d silo -c "SELECT 1"
|
psql -h psql.kindred.internal -U silo -d silo -c 'SELECT 1'
|
||||||
mc ls kindred/silo-files
|
curl http://minio.kindred.internal:9000/minio/health/live
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Build Fails
|
||||||
|
|
||||||
|
1. Check Go is installed:
|
||||||
|
```bash
|
||||||
|
go version
|
||||||
|
# Should be 1.23+
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Check source is present:
|
||||||
|
```bash
|
||||||
|
ls -la /opt/silo/src/
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Try manual build:
|
||||||
|
```bash
|
||||||
|
cd /opt/silo/src
|
||||||
|
go build -v ./cmd/silod
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Security Checklist
|
## Security Checklist
|
||||||
|
|
||||||
|
- [ ] `/etc/silo/silod.env` has mode 600 (`chmod 600`)
|
||||||
- [ ] Database password is strong and unique
|
- [ ] Database password is strong and unique
|
||||||
- [ ] MinIO credentials are service-account specific
|
- [ ] MinIO credentials are specific to silo (not admin)
|
||||||
- [ ] `/etc/silo/silod.env` has mode 600
|
|
||||||
- [ ] SSL/TLS enabled for PostgreSQL (`sslmode: require`)
|
- [ ] SSL/TLS enabled for PostgreSQL (`sslmode: require`)
|
||||||
- [ ] SSL/TLS enabled for MinIO (`use_ssl: true`)
|
- [ ] SSL/TLS enabled for MinIO (`use_ssl: true`) if available
|
||||||
- [ ] Firewall restricts access to port 8080
|
- [ ] Firewall restricts access to port 8080
|
||||||
- [ ] Silo runs as non-root user
|
- [ ] Service runs as non-root `silo` user
|
||||||
- [ ] Logs don't contain sensitive information
|
|
||||||
|
|||||||
@@ -1,23 +1,19 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Silo Deployment Script
|
# Silo Deployment Script
|
||||||
# Deploys silod to silo.kindred.internal via systemd
|
# Pulls from git and deploys silod on the local machine
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./scripts/deploy.sh [options]
|
# sudo ./scripts/deploy.sh [options]
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# --build-only Only build the binary, don't deploy
|
# --no-pull Skip git pull (use current checkout)
|
||||||
# --no-build Skip build, deploy existing binary
|
# --no-build Skip build (use existing binary)
|
||||||
# --restart-only Only restart the service
|
# --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
|
# --help Show this help message
|
||||||
#
|
#
|
||||||
# Environment variables:
|
# This script should be run on silo.kindred.internal as root or with sudo.
|
||||||
# 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)
|
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -29,27 +25,18 @@ BLUE='\033[0;34m'
|
|||||||
NC='\033[0m' # No Color
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
SILO_HOST="${SILO_HOST:-silo.kindred.internal}"
|
REPO_URL="${SILO_REPO_URL:-https://gitea.kindred.internal/kindred/silo-0062.git}"
|
||||||
SILO_USER="${SILO_USER:-deploy}"
|
REPO_BRANCH="${SILO_BRANCH:-main}"
|
||||||
SILO_SSH_KEY="${SILO_SSH_KEY:-}"
|
INSTALL_DIR="/opt/silo"
|
||||||
BUILD_VERSION="${BUILD_VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo "dev")}"
|
CONFIG_DIR="/etc/silo"
|
||||||
|
|
||||||
# Paths
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
||||||
BUILD_DIR="${PROJECT_ROOT}/build/out"
|
|
||||||
BINARY_NAME="silod"
|
BINARY_NAME="silod"
|
||||||
|
SERVICE_NAME="silod"
|
||||||
# Remote paths
|
|
||||||
REMOTE_BIN_DIR="/opt/silo/bin"
|
|
||||||
REMOTE_CONFIG_DIR="/etc/silo"
|
|
||||||
REMOTE_SCHEMAS_DIR="/etc/silo/schemas"
|
|
||||||
REMOTE_SERVICE="silod"
|
|
||||||
|
|
||||||
# Flags
|
# Flags
|
||||||
BUILD=true
|
DO_PULL=true
|
||||||
DEPLOY=true
|
DO_BUILD=true
|
||||||
DRY_RUN=false
|
RESTART_ONLY=false
|
||||||
|
STATUS_ONLY=false
|
||||||
|
|
||||||
# Functions
|
# Functions
|
||||||
log_info() {
|
log_info() {
|
||||||
@@ -74,33 +61,13 @@ die() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show_help() {
|
show_help() {
|
||||||
head -20 "$0" | grep -E '^#' | sed 's/^# *//'
|
head -18 "$0" | grep -E '^#' | sed 's/^# *//'
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
ssh_cmd() {
|
check_root() {
|
||||||
local ssh_opts=(-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10)
|
if [[ $EUID -ne 0 ]]; then
|
||||||
if [[ -n "${SILO_SSH_KEY}" ]]; then
|
die "This script must be run as root (use sudo)"
|
||||||
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[@]}" "$@"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,174 +76,188 @@ check_dependencies() {
|
|||||||
|
|
||||||
local missing=()
|
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 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
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||||
die "Missing required commands: ${missing[*]}"
|
die "Missing required commands: ${missing[*]}"
|
||||||
fi
|
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"
|
log_success "All dependencies available"
|
||||||
}
|
}
|
||||||
|
|
||||||
check_connectivity() {
|
setup_directories() {
|
||||||
log_info "Checking connectivity to ${SILO_HOST}..."
|
log_info "Setting up directories..."
|
||||||
|
|
||||||
if [[ "${DRY_RUN}" == "true" ]]; then
|
# Create directories if they don't exist
|
||||||
log_info "[DRY-RUN] Would check SSH connectivity"
|
mkdir -p "${INSTALL_DIR}/bin"
|
||||||
return 0
|
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
|
fi
|
||||||
|
|
||||||
if ! ssh_cmd "echo 'Connection successful'" >/dev/null 2>&1; then
|
log_success "Directories ready"
|
||||||
die "Cannot connect to ${SILO_HOST} as ${SILO_USER}"
|
}
|
||||||
|
|
||||||
|
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
|
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() {
|
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
|
local ldflags="-w -s -X main.Version=${version}"
|
||||||
echo "[DRY-RUN] CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags=\"${ldflags}\" -o ${BUILD_DIR}/${BINARY_NAME} ./cmd/silod"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
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 \
|
if [[ ! -f "${INSTALL_DIR}/bin/${BINARY_NAME}" ]]; then
|
||||||
go build -ldflags="${ldflags}" \
|
|
||||||
-o "${BUILD_DIR}/${BINARY_NAME}" \
|
|
||||||
./cmd/silod
|
|
||||||
|
|
||||||
if [[ ! -f "${BUILD_DIR}/${BINARY_NAME}" ]]; then
|
|
||||||
die "Build failed: binary not found"
|
die "Build failed: binary not found"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
chmod 755 "${INSTALL_DIR}/bin/${BINARY_NAME}"
|
||||||
|
|
||||||
local size
|
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})"
|
log_success "Built ${BINARY_NAME} (${size})"
|
||||||
}
|
}
|
||||||
|
|
||||||
deploy_binary() {
|
install_config() {
|
||||||
log_info "Deploying binary to ${SILO_HOST}..."
|
log_info "Installing configuration..."
|
||||||
|
|
||||||
local binary="${BUILD_DIR}/${BINARY_NAME}"
|
local src_dir="${INSTALL_DIR}/src"
|
||||||
|
|
||||||
if [[ ! -f "${binary}" ]] && [[ "${DRY_RUN}" != "true" ]]; then
|
# Install config file if it doesn't exist or is different
|
||||||
die "Binary not found: ${binary}"
|
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
|
fi
|
||||||
|
|
||||||
# Upload to temp location first
|
# Install schemas (always update)
|
||||||
scp_cmd "${binary}" "${SILO_USER}@${SILO_HOST}:/tmp/${BINARY_NAME}.new"
|
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
|
# Check environment file
|
||||||
ssh_cmd "sudo mv /tmp/${BINARY_NAME}.new ${REMOTE_BIN_DIR}/${BINARY_NAME}"
|
if [[ ! -f "${CONFIG_DIR}/silod.env" ]]; then
|
||||||
ssh_cmd "sudo chmod 755 ${REMOTE_BIN_DIR}/${BINARY_NAME}"
|
cp "${src_dir}/deployments/systemd/silod.env.example" "${CONFIG_DIR}/silod.env"
|
||||||
ssh_cmd "sudo chown root:root ${REMOTE_BIN_DIR}/${BINARY_NAME}"
|
chmod 600 "${CONFIG_DIR}/silod.env"
|
||||||
|
chown root:silo "${CONFIG_DIR}/silod.env"
|
||||||
log_success "Binary deployed to ${REMOTE_BIN_DIR}/${BINARY_NAME}"
|
log_warn "Created ${CONFIG_DIR}/silod.env - EDIT THIS FILE WITH CREDENTIALS!"
|
||||||
}
|
|
||||||
|
|
||||||
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}"
|
|
||||||
fi
|
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() {
|
install_systemd() {
|
||||||
log_info "Deploying schemas..."
|
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
|
if [[ -f "${service_file}" ]]; then
|
||||||
die "Schemas directory not found: ${schemas_dir}"
|
cp "${service_file}" /etc/systemd/system/silod.service
|
||||||
fi
|
chmod 644 /etc/systemd/system/silod.service
|
||||||
|
systemctl daemon-reload
|
||||||
# Create temp directory and copy schemas
|
log_success "Systemd service installed"
|
||||||
ssh_cmd "rm -rf /tmp/silo-schemas && mkdir -p /tmp/silo-schemas"
|
else
|
||||||
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
|
|
||||||
die "Service file not found: ${service_file}"
|
die "Service file not found: ${service_file}"
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
scp_cmd "${service_file}" "${SILO_USER}@${SILO_HOST}:/tmp/silod.service"
|
set_permissions() {
|
||||||
ssh_cmd "sudo mv /tmp/silod.service /etc/systemd/system/silod.service"
|
log_info "Setting permissions..."
|
||||||
ssh_cmd "sudo chmod 644 /etc/systemd/system/silod.service"
|
|
||||||
ssh_cmd "sudo systemctl daemon-reload"
|
|
||||||
|
|
||||||
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() {
|
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
|
sleep 2
|
||||||
|
|
||||||
if [[ "${DRY_RUN}" != "true" ]]; then
|
if systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||||
if ssh_cmd "sudo systemctl is-active --quiet ${REMOTE_SERVICE}"; then
|
log_success "Service started successfully"
|
||||||
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
|
|
||||||
else
|
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
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_deployment() {
|
verify_deployment() {
|
||||||
log_info "Verifying 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
|
# Wait a moment for service to fully start
|
||||||
sleep 3
|
sleep 2
|
||||||
|
|
||||||
# Check health endpoint
|
# Check health endpoint
|
||||||
local health_status
|
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"
|
log_success "Health check passed"
|
||||||
else
|
else
|
||||||
log_warn "Health check returned: ${health_status}"
|
log_warn "Health check returned: ${health_status}"
|
||||||
@@ -284,46 +265,46 @@ verify_deployment() {
|
|||||||
|
|
||||||
# Check ready endpoint (includes DB and MinIO)
|
# Check ready endpoint (includes DB and MinIO)
|
||||||
local ready_status
|
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)"
|
log_success "Readiness check passed (DB and MinIO connected)"
|
||||||
else
|
else
|
||||||
log_warn "Readiness check returned: ${ready_status}"
|
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
|
fi
|
||||||
|
|
||||||
# Show deployed version
|
# Show version
|
||||||
local deployed_version
|
log_info "Deployed version: $("${INSTALL_DIR}/bin/${BINARY_NAME}" --version 2>/dev/null || echo 'unknown')"
|
||||||
deployed_version=$(ssh_cmd "${REMOTE_BIN_DIR}/${BINARY_NAME} --version" 2>/dev/null || echo "unknown")
|
|
||||||
log_info "Deployed version: ${deployed_version}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enable_service() {
|
show_status() {
|
||||||
log_info "Enabling ${REMOTE_SERVICE} service..."
|
echo ""
|
||||||
ssh_cmd "sudo systemctl enable ${REMOTE_SERVICE}"
|
log_info "Service Status"
|
||||||
log_success "Service enabled for auto-start"
|
echo "============================================"
|
||||||
|
systemctl status "${SERVICE_NAME}" --no-pager -l || true
|
||||||
|
echo ""
|
||||||
|
echo "Recent logs:"
|
||||||
|
journalctl -u "${SERVICE_NAME}" -n 10 --no-pager || true
|
||||||
}
|
}
|
||||||
|
|
||||||
# Parse arguments
|
# Parse arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
--build-only)
|
--no-pull)
|
||||||
DEPLOY=false
|
DO_PULL=false
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--no-build)
|
--no-build)
|
||||||
BUILD=false
|
DO_BUILD=false
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--restart-only)
|
--restart-only)
|
||||||
BUILD=false
|
RESTART_ONLY=true
|
||||||
DEPLOY=false
|
shift
|
||||||
restart_service
|
|
||||||
exit 0
|
|
||||||
;;
|
;;
|
||||||
--dry-run)
|
--status)
|
||||||
DRY_RUN=true
|
STATUS_ONLY=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
@@ -337,36 +318,56 @@ done
|
|||||||
|
|
||||||
# Main execution
|
# Main execution
|
||||||
main() {
|
main() {
|
||||||
|
echo ""
|
||||||
log_info "Silo Deployment Script"
|
log_info "Silo Deployment Script"
|
||||||
log_info "======================"
|
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 ""
|
echo ""
|
||||||
|
|
||||||
check_dependencies
|
check_root
|
||||||
|
|
||||||
if [[ "${BUILD}" == "true" ]]; then
|
if [[ "${STATUS_ONLY}" == "true" ]]; then
|
||||||
build_binary
|
show_status
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${DEPLOY}" == "true" ]]; then
|
if [[ "${RESTART_ONLY}" == "true" ]]; then
|
||||||
check_connectivity
|
|
||||||
deploy_binary
|
|
||||||
deploy_config
|
|
||||||
deploy_schemas
|
|
||||||
deploy_systemd
|
|
||||||
enable_service
|
|
||||||
restart_service
|
restart_service
|
||||||
verify_deployment
|
verify_deployment
|
||||||
|
exit 0
|
||||||
fi
|
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 ""
|
echo ""
|
||||||
|
log_success "============================================"
|
||||||
log_success "Deployment complete!"
|
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 "$@"
|
main "$@"
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Silo Host Setup Script
|
# Silo Host Setup Script
|
||||||
# Run this on silo.kindred.internal to prepare for deployment
|
# Run this once on silo.kindred.internal to prepare for deployment
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# sudo ./scripts/setup-host.sh
|
# sudo ./setup-host.sh
|
||||||
#
|
#
|
||||||
# This script:
|
# This script:
|
||||||
# 1. Creates the silo system user
|
# 1. Installs required packages (git, go)
|
||||||
# 2. Creates required directories
|
# 2. Creates the silo system user
|
||||||
# 3. Sets up the environment file template
|
# 3. Creates required directories
|
||||||
# 4. Configures sudoers for deploy user
|
# 4. Sets up the environment file template
|
||||||
|
# 5. Clones the repository
|
||||||
|
# 6. Runs initial deployment
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -18,65 +20,147 @@ set -euo pipefail
|
|||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
# 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"
|
||||||
|
GO_VERSION="1.23.0"
|
||||||
|
|
||||||
|
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
|
log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
die() { log_error "$*"; exit 1; }
|
||||||
|
|
||||||
# Check root
|
# Check root
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
log_error "This script must be run as root (use sudo)"
|
die "This script must be run as root (use sudo)"
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_info "Setting up Silo host..."
|
log_info "============================================"
|
||||||
|
log_info "Silo Host Setup"
|
||||||
|
log_info "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Create silo system user (for running the service)
|
# Detect package manager
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
PKG_MANAGER="apt"
|
||||||
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
PKG_MANAGER="dnf"
|
||||||
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
|
PKG_MANAGER="yum"
|
||||||
|
else
|
||||||
|
die "Unsupported package manager. Install git and go manually."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Detected package manager: ${PKG_MANAGER}"
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
log_info "Installing dependencies..."
|
||||||
|
|
||||||
|
case ${PKG_MANAGER} in
|
||||||
|
apt)
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq git curl ca-certificates
|
||||||
|
;;
|
||||||
|
dnf|yum)
|
||||||
|
${PKG_MANAGER} install -y -q git curl ca-certificates
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
log_success "System packages installed"
|
||||||
|
|
||||||
|
# Install Go if not present or wrong version
|
||||||
|
install_go() {
|
||||||
|
log_info "Installing Go ${GO_VERSION}..."
|
||||||
|
|
||||||
|
local arch
|
||||||
|
case $(uname -m) in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
*) die "Unsupported architecture: $(uname -m)" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local go_tar="go${GO_VERSION}.linux-${arch}.tar.gz"
|
||||||
|
local go_url="https://go.dev/dl/${go_tar}"
|
||||||
|
|
||||||
|
# Remove existing Go installation
|
||||||
|
rm -rf /usr/local/go
|
||||||
|
|
||||||
|
# Download and install
|
||||||
|
curl -fsSL "${go_url}" -o "/tmp/${go_tar}"
|
||||||
|
tar -C /usr/local -xzf "/tmp/${go_tar}"
|
||||||
|
rm -f "/tmp/${go_tar}"
|
||||||
|
|
||||||
|
# Add to PATH for all users
|
||||||
|
cat > /etc/profile.d/go.sh << 'EOF'
|
||||||
|
export PATH=$PATH:/usr/local/go/bin
|
||||||
|
export GOPATH=/opt/go
|
||||||
|
export PATH=$PATH:$GOPATH/bin
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Source for current session
|
||||||
|
export PATH=$PATH:/usr/local/go/bin
|
||||||
|
|
||||||
|
log_success "Go ${GO_VERSION} installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
if command -v go >/dev/null 2>&1; then
|
||||||
|
current_go=$(go version | grep -oP '\d+\.\d+' | head -1)
|
||||||
|
required_go="1.23"
|
||||||
|
if [[ "$(printf '%s\n' "$required_go" "$current_go" | sort -V | head -n1)" != "$required_go" ]]; then
|
||||||
|
log_warn "Go ${current_go} found, but ${required_go}+ required"
|
||||||
|
install_go
|
||||||
|
else
|
||||||
|
log_success "Go ${current_go} already installed"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
install_go
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure Go is in PATH
|
||||||
|
export PATH=$PATH:/usr/local/go/bin
|
||||||
|
|
||||||
|
# Create silo system user
|
||||||
if ! id -u silo >/dev/null 2>&1; then
|
if ! id -u silo >/dev/null 2>&1; then
|
||||||
log_info "Creating silo user..."
|
log_info "Creating silo user..."
|
||||||
useradd -r -m -d /opt/silo -s /sbin/nologin -c "Silo Service" silo
|
useradd -r -m -d "${INSTALL_DIR}" -s /sbin/nologin -c "Silo Service" silo
|
||||||
log_info "Created user: silo"
|
log_success "Created user: silo"
|
||||||
else
|
else
|
||||||
log_info "User silo already exists"
|
log_info "User silo already exists"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create deploy user (for CI/CD deployments)
|
|
||||||
DEPLOY_USER="deploy"
|
|
||||||
if ! id -u "${DEPLOY_USER}" >/dev/null 2>&1; then
|
|
||||||
log_info "Creating deploy user..."
|
|
||||||
useradd -m -s /bin/bash -c "Deployment User" "${DEPLOY_USER}"
|
|
||||||
log_info "Created user: ${DEPLOY_USER}"
|
|
||||||
log_warn "Remember to add SSH public key to /home/${DEPLOY_USER}/.ssh/authorized_keys"
|
|
||||||
else
|
|
||||||
log_info "User ${DEPLOY_USER} already exists"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create directories
|
# Create directories
|
||||||
log_info "Creating directories..."
|
log_info "Creating directories..."
|
||||||
|
|
||||||
mkdir -p /opt/silo/bin
|
mkdir -p "${INSTALL_DIR}/bin"
|
||||||
mkdir -p /etc/silo/schemas
|
mkdir -p "${INSTALL_DIR}/src"
|
||||||
|
mkdir -p "${CONFIG_DIR}/schemas"
|
||||||
mkdir -p /var/log/silo
|
mkdir -p /var/log/silo
|
||||||
|
|
||||||
# Set ownership
|
# Set ownership
|
||||||
chown -R silo:silo /opt/silo
|
chown -R silo:silo "${INSTALL_DIR}"
|
||||||
chown root:silo /etc/silo
|
chown root:silo "${CONFIG_DIR}"
|
||||||
chmod 750 /etc/silo
|
chmod 750 "${CONFIG_DIR}"
|
||||||
chown silo:silo /var/log/silo
|
chown silo:silo /var/log/silo
|
||||||
chmod 750 /var/log/silo
|
chmod 750 /var/log/silo
|
||||||
|
|
||||||
log_info "Directories created"
|
log_success "Directories created"
|
||||||
|
|
||||||
# Create environment file if it doesn't exist
|
# Create environment file if it doesn't exist
|
||||||
ENV_FILE="/etc/silo/silod.env"
|
ENV_FILE="${CONFIG_DIR}/silod.env"
|
||||||
if [[ ! -f "${ENV_FILE}" ]]; then
|
if [[ ! -f "${ENV_FILE}" ]]; then
|
||||||
log_info "Creating environment file template..."
|
log_info "Creating environment file..."
|
||||||
cat > "${ENV_FILE}" << 'EOF'
|
cat > "${ENV_FILE}" << 'EOF'
|
||||||
# Silo daemon environment variables
|
# Silo daemon environment variables
|
||||||
# Fill in the values below
|
# Fill in the values below
|
||||||
|
|
||||||
# Database credentials (psql.kindred.internal)
|
# Database credentials (psql.kindred.internal)
|
||||||
|
# Database: silo, User: silo
|
||||||
SILO_DB_PASSWORD=
|
SILO_DB_PASSWORD=
|
||||||
|
|
||||||
# MinIO credentials (minio.kindred.internal)
|
# MinIO credentials (minio.kindred.internal)
|
||||||
@@ -89,62 +173,30 @@ SILO_MINIO_SECRET_KEY=
|
|||||||
EOF
|
EOF
|
||||||
chmod 600 "${ENV_FILE}"
|
chmod 600 "${ENV_FILE}"
|
||||||
chown root:silo "${ENV_FILE}"
|
chown root:silo "${ENV_FILE}"
|
||||||
log_warn "Edit ${ENV_FILE} and fill in credentials!"
|
log_warn "Created ${ENV_FILE} - YOU MUST EDIT THIS FILE!"
|
||||||
else
|
else
|
||||||
log_info "Environment file already exists: ${ENV_FILE}"
|
log_info "Environment file already exists"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Configure sudoers for deploy user
|
# Clone repository
|
||||||
SUDOERS_FILE="/etc/sudoers.d/silo-deploy"
|
log_info "Cloning repository..."
|
||||||
log_info "Configuring sudoers for deploy user..."
|
|
||||||
cat > "${SUDOERS_FILE}" << EOF
|
|
||||||
# Allow deploy user to manage silo service without password
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl start silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl stop silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl restart silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl status silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl enable silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl disable silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl is-active silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/systemctl daemon-reload
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/journalctl -u silod *
|
|
||||||
|
|
||||||
# Allow deploy user to manage silo files
|
if [[ -d "${INSTALL_DIR}/src/.git" ]]; then
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/mv /tmp/silod.new /opt/silo/bin/silod
|
log_info "Repository already cloned, pulling latest..."
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/mv /tmp/silod /opt/silo/bin/silod
|
cd "${INSTALL_DIR}/src"
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/mv /tmp/config.yaml /etc/silo/config.yaml
|
git fetch origin
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/mv /tmp/silod.service /etc/systemd/system/silod.service
|
git checkout "${REPO_BRANCH}"
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/mv /tmp/silo-schemas/* /etc/silo/schemas/
|
git reset --hard "origin/${REPO_BRANCH}"
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/chmod * /opt/silo/bin/silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/chmod * /etc/silo/config.yaml
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/chmod * /etc/systemd/system/silod.service
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/chmod -R * /etc/silo/schemas/*
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/chown * /opt/silo/bin/silod
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/chown * /etc/silo/config.yaml
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/chown -R * /etc/silo/schemas
|
|
||||||
${DEPLOY_USER} ALL=(ALL) NOPASSWD: /bin/rm -rf /etc/silo/schemas/*
|
|
||||||
EOF
|
|
||||||
chmod 440 "${SUDOERS_FILE}"
|
|
||||||
|
|
||||||
# Validate sudoers
|
|
||||||
if visudo -cf "${SUDOERS_FILE}"; then
|
|
||||||
log_info "Sudoers configuration valid"
|
|
||||||
else
|
else
|
||||||
log_error "Sudoers configuration invalid!"
|
rm -rf "${INSTALL_DIR}/src"
|
||||||
rm -f "${SUDOERS_FILE}"
|
git clone --branch "${REPO_BRANCH}" "${REPO_URL}" "${INSTALL_DIR}/src"
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create SSH directory for deploy user
|
cd "${INSTALL_DIR}/src"
|
||||||
DEPLOY_SSH_DIR="/home/${DEPLOY_USER}/.ssh"
|
log_success "Repository ready at $(git rev-parse --short HEAD)"
|
||||||
if [[ ! -d "${DEPLOY_SSH_DIR}" ]]; then
|
|
||||||
mkdir -p "${DEPLOY_SSH_DIR}"
|
# Set ownership of source
|
||||||
touch "${DEPLOY_SSH_DIR}/authorized_keys"
|
chown -R silo:silo "${INSTALL_DIR}/src"
|
||||||
chmod 700 "${DEPLOY_SSH_DIR}"
|
|
||||||
chmod 600 "${DEPLOY_SSH_DIR}/authorized_keys"
|
|
||||||
chown -R "${DEPLOY_USER}:${DEPLOY_USER}" "${DEPLOY_SSH_DIR}"
|
|
||||||
log_info "Created SSH directory for deploy user"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
echo ""
|
echo ""
|
||||||
@@ -154,18 +206,20 @@ log_info "============================================"
|
|||||||
echo ""
|
echo ""
|
||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. Edit /etc/silo/silod.env and fill in credentials:"
|
echo "1. Edit ${ENV_FILE} and fill in credentials:"
|
||||||
echo " sudo nano /etc/silo/silod.env"
|
echo " sudo nano ${ENV_FILE}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. Add the CI/CD SSH public key to deploy user:"
|
echo "2. Verify database connectivity:"
|
||||||
echo " echo 'ssh-ed25519 AAAA...' >> /home/${DEPLOY_USER}/.ssh/authorized_keys"
|
|
||||||
echo ""
|
|
||||||
echo "3. Verify connectivity from CI/CD server:"
|
|
||||||
echo " ssh ${DEPLOY_USER}@silo.kindred.internal 'echo OK'"
|
|
||||||
echo ""
|
|
||||||
echo "4. Test database connectivity:"
|
|
||||||
echo " psql -h psql.kindred.internal -U silo -d silo -c 'SELECT 1'"
|
echo " psql -h psql.kindred.internal -U silo -d silo -c 'SELECT 1'"
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Test MinIO connectivity:"
|
echo "3. Verify MinIO connectivity:"
|
||||||
echo " curl -I https://minio.kindred.internal:9000/minio/health/live"
|
echo " curl -I http://minio.kindred.internal:9000/minio/health/live"
|
||||||
|
echo ""
|
||||||
|
echo "4. Run the deployment:"
|
||||||
|
echo " sudo ${INSTALL_DIR}/src/scripts/deploy.sh"
|
||||||
|
echo ""
|
||||||
|
echo "After deployment, manage the service with:"
|
||||||
|
echo " sudo systemctl status silod"
|
||||||
|
echo " sudo systemctl restart silod"
|
||||||
|
echo " sudo journalctl -u silod -f"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user