Add isolated development environment
This commit is contained in:
1 parent
421fa98292
commit
f5af75f305
7 files changed
+240
No files matched your search
@@ -0,0 +1,20 @@
|
|||||||
|
# Copying this file is optional. scripts/dev-env-init.sh generates strong secrets.
|
||||||
|
APP_ENV=development
|
||||||
|
APP_TIMEZONE=America/Winnipeg
|
||||||
|
POSTGRES_DB=minerops_dev
|
||||||
|
POSTGRES_USER=minerops_dev
|
||||||
|
POSTGRES_PASSWORD=replace-with-a-random-local-only-secret
|
||||||
|
POSTGRES_HOST=postgres
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
DATABASE_URL=postgresql://minerops_dev:replace-with-a-random-local-only-secret@postgres:5432/minerops_dev
|
||||||
|
REDIS_URL=redis://redis:6379/0
|
||||||
|
JWT_SECRET_KEY=replace-with-a-random-local-only-secret
|
||||||
|
MINEROPS_AGENT_SHARED_SECRET=replace-with-a-random-local-only-secret
|
||||||
|
MINEROPS_DEV_PORT=18000
|
||||||
|
|
||||||
|
# Safety interlocks: keep these values disabled for development.
|
||||||
|
SCHEDULED_SCAN_ENABLED=false
|
||||||
|
MINEROPS_HUB_MODE=true
|
||||||
|
MINEROPS_DISABLE_LOCAL_SCANNER=true
|
||||||
|
MINEROPS_ENABLE_REAL_REBOOT=false
|
||||||
|
MINEROPS_ENABLE_REAL_POWER_CONTROL=false
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
*.env
|
*.env
|
||||||
|
!.env.example
|
||||||
|
!.env.dev.example
|
||||||
|
|
||||||
# Backups / dumps / generated data
|
# Backups / dumps / generated data
|
||||||
backups/
|
backups/
|
||||||
|
|||||||
@@ -74,9 +74,39 @@ Git status:
|
|||||||
|
|
||||||
pct exec 306 -- bash -lc "cd /opt/minerops && git status --short"
|
pct exec 306 -- bash -lc "cd /opt/minerops && git status --short"
|
||||||
|
|
||||||
|
## Safe local development
|
||||||
|
|
||||||
|
Development uses a separate Compose project, database volume, Redis volume, and
|
||||||
|
localhost-only backend port. It does not reuse the production `.env` file.
|
||||||
|
Scheduled scanning and real miner power/reboot controls are disabled.
|
||||||
|
|
||||||
|
First-time setup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/dev-env-init.sh
|
||||||
|
docker compose --env-file .env.dev -f compose.dev.yaml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify the environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --env-file .env.dev -f compose.dev.yaml ps
|
||||||
|
curl --fail --silent http://127.0.0.1:18000/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop development services without deleting their data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --env-file .env.dev -f compose.dev.yaml down
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated `.env.dev` file is mode `0600` and ignored by Git. To use a
|
||||||
|
different local port, change `MINEROPS_DEV_PORT` in `.env.dev`.
|
||||||
|
|
||||||
## Safety notes
|
## Safety notes
|
||||||
|
|
||||||
- Do not commit .env.
|
- Do not commit .env.
|
||||||
- Do not commit backups, logs, database dumps, imports, or runtime data.
|
- Do not commit backups, logs, database dumps, imports, or runtime data.
|
||||||
|
- Do not enable scanning or real miner controls in the development environment.
|
||||||
- Check /system-health after major changes.
|
- Check /system-health after major changes.
|
||||||
- Commit and push after each stable working change.
|
- Commit and push after each stable working change.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.bak
|
||||||
|
*.bak.*
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
name: minerops-dev
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- postgres-dev:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- dev
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
command: ["redis-server", "--appendonly", "yes"]
|
||||||
|
volumes:
|
||||||
|
- redis-dev:/data
|
||||||
|
networks:
|
||||||
|
- dev
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ./backend
|
||||||
|
env_file:
|
||||||
|
- .env.dev
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -ec
|
||||||
|
- alembic upgrade head && exec uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- dev
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /tmp
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- python
|
||||||
|
- -c
|
||||||
|
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=3)"
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 20s
|
||||||
|
|
||||||
|
gateway:
|
||||||
|
image: nginx:1.29-alpine
|
||||||
|
depends_on:
|
||||||
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:${MINEROPS_DEV_PORT:-18000}:8080"
|
||||||
|
networks:
|
||||||
|
- dev
|
||||||
|
- edge
|
||||||
|
volumes:
|
||||||
|
- ./dev/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /tmp
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
user: "101:101"
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- wget
|
||||||
|
- --quiet
|
||||||
|
- --spider
|
||||||
|
- http://127.0.0.1:8080/api/health
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
|
||||||
|
networks:
|
||||||
|
dev:
|
||||||
|
internal: true
|
||||||
|
edge:
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-dev:
|
||||||
|
redis-dev:
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
worker_processes 1;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
access_log /dev/stdout;
|
||||||
|
error_log /dev/stderr notice;
|
||||||
|
|
||||||
|
client_body_temp_path /tmp/client_body;
|
||||||
|
proxy_temp_path /tmp/proxy;
|
||||||
|
fastcgi_temp_path /tmp/fastcgi;
|
||||||
|
uwsgi_temp_path /tmp/uwsgi;
|
||||||
|
scgi_temp_path /tmp/scgi;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://backend:8000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
env_file="${project_dir}/.env.dev"
|
||||||
|
|
||||||
|
if [[ -e "${env_file}" ]]; then
|
||||||
|
echo "Keeping existing ${env_file}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
generate_secret() {
|
||||||
|
if command -v openssl >/dev/null 2>&1; then
|
||||||
|
openssl rand -hex 32
|
||||||
|
else
|
||||||
|
python3 -c 'import secrets; print(secrets.token_hex(32))'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
postgres_password="$(generate_secret)"
|
||||||
|
jwt_secret="$(generate_secret)"
|
||||||
|
agent_secret="$(generate_secret)"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "APP_ENV=development"
|
||||||
|
echo "APP_TIMEZONE=America/Winnipeg"
|
||||||
|
echo "POSTGRES_DB=minerops_dev"
|
||||||
|
echo "POSTGRES_USER=minerops_dev"
|
||||||
|
echo "POSTGRES_PASSWORD=${postgres_password}"
|
||||||
|
echo "POSTGRES_HOST=postgres"
|
||||||
|
echo "POSTGRES_PORT=5432"
|
||||||
|
echo "DATABASE_URL=postgresql://minerops_dev:${postgres_password}@postgres:5432/minerops_dev"
|
||||||
|
echo "REDIS_URL=redis://redis:6379/0"
|
||||||
|
echo "JWT_SECRET_KEY=${jwt_secret}"
|
||||||
|
echo "MINEROPS_AGENT_SHARED_SECRET=${agent_secret}"
|
||||||
|
echo "MINEROPS_DEV_PORT=18000"
|
||||||
|
echo "SCHEDULED_SCAN_ENABLED=false"
|
||||||
|
echo "MINEROPS_HUB_MODE=true"
|
||||||
|
echo "MINEROPS_DISABLE_LOCAL_SCANNER=true"
|
||||||
|
echo "MINEROPS_ENABLE_REAL_REBOOT=false"
|
||||||
|
echo "MINEROPS_ENABLE_REAL_POWER_CONTROL=false"
|
||||||
|
} >"${env_file}"
|
||||||
|
|
||||||
|
chmod 0600 "${env_file}"
|
||||||
|
echo "Created ${env_file} with development-only secrets and safety interlocks."
|
||||||
Reference in new issue
Block a user