Merge branch 'codex/fix/accounts-nil-registry'
This commit is contained in:
commit
c1df654cbe
19
README.md
19
README.md
@ -21,9 +21,25 @@ Cloud Neutral Toolkit 的账号与身份服务 (Account Service).
|
||||
|
||||
```bash
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus
|
||||
| bash -s -- accounts.svc.plus --mode process --deploy
|
||||
```
|
||||
|
||||
Docker 部署模式:
|
||||
|
||||
```bash
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode docker --deploy
|
||||
```
|
||||
|
||||
Cloud Run 部署模式:
|
||||
|
||||
```bash
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode cloudrun
|
||||
```
|
||||
|
||||
单机 `process` / `docker` 模式默认会写入 Caddy 站点配置到 `/etc/caddy/conf.d/accounts.svc.plus.conf`,并反向代理到本机 `127.0.0.1:8080`。
|
||||
|
||||
### 本地运行 (Local Dev)
|
||||
|
||||
```bash
|
||||
@ -54,4 +70,3 @@ make dev
|
||||
- API 参考:`docs/api/overview.md`
|
||||
- 运维:`docs/operations/monitoring.md`, `docs/operations/troubleshooting.md`
|
||||
- Runbooks:`docs/Runbook/README.md`
|
||||
|
||||
|
||||
17
api/api.go
17
api/api.go
@ -13,6 +13,7 @@ import (
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -220,10 +221,26 @@ func WithOAuthFrontendURL(url string) Option {
|
||||
// WithAgentRegistry configures the handler with the provided agent registry.
|
||||
func WithAgentRegistry(registry agentRegistry) Option {
|
||||
return func(h *handler) {
|
||||
if isNilAgentRegistry(registry) {
|
||||
return
|
||||
}
|
||||
h.agentRegistry = registry
|
||||
}
|
||||
}
|
||||
|
||||
func isNilAgentRegistry(registry agentRegistry) bool {
|
||||
if registry == nil {
|
||||
return true
|
||||
}
|
||||
value := reflect.ValueOf(registry)
|
||||
switch value.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
||||
return value.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// WithGormDB configures the handler with the provided GORM database for admin settings.
|
||||
func WithGormDB(db *gorm.DB) Option {
|
||||
return func(h *handler) {
|
||||
|
||||
@ -113,6 +113,17 @@ func extractVerificationCodeFromMessage(t *testing.T, msg capturedEmail) string
|
||||
return ""
|
||||
}
|
||||
|
||||
func TestWithAgentRegistry_IgnoresTypedNil(t *testing.T) {
|
||||
var registry *agentserver.Registry
|
||||
h := &handler{}
|
||||
|
||||
WithAgentRegistry(registry)(h)
|
||||
|
||||
if h.agentRegistry != nil {
|
||||
t.Fatalf("expected nil agent registry, got %T", h.agentRegistry)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeResponse(t *testing.T, rr *httptest.ResponseRecorder) apiResponse {
|
||||
t.Helper()
|
||||
var resp apiResponse
|
||||
|
||||
@ -841,10 +841,12 @@ func runServer(ctx context.Context, cfg *config.Config, logger *slog.Logger) err
|
||||
options = append(options, api.WithGormDB(gormDB))
|
||||
|
||||
// Pre-load sandbox bindings from database into the registry
|
||||
var sandboxBindings []model.SandboxBinding
|
||||
if err := gormDB.Find(&sandboxBindings).Error; err == nil {
|
||||
for _, b := range sandboxBindings {
|
||||
agentRegistry.SetSandboxAgent(b.AgentID, true)
|
||||
if agentRegistry != nil {
|
||||
var sandboxBindings []model.SandboxBinding
|
||||
if err := gormDB.Find(&sandboxBindings).Error; err == nil {
|
||||
for _, b := range sandboxBindings {
|
||||
agentRegistry.SetSandboxAgent(b.AgentID, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,22 @@
|
||||
|
||||
本项目支持本地编译运行、Docker 容器运行,以及 Cloud Run 部署。以下步骤基于仓库现有脚本与配置。
|
||||
|
||||
一键初始化脚本支持按部署模式选择:
|
||||
|
||||
```bash
|
||||
# 进程部署模式
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode process
|
||||
|
||||
# Docker 部署模式
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode docker
|
||||
|
||||
# Cloud Run 部署模式
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode cloudrun
|
||||
```
|
||||
|
||||
## 本地安装(Go)
|
||||
|
||||
前置条件:
|
||||
@ -41,7 +57,8 @@ docker run --rm -p 8080:8080 \
|
||||
## Cloud Run
|
||||
|
||||
参考文件:
|
||||
- `deploy/gcp/cloud-run/service.yaml`
|
||||
- `deploy/gcp/cloud-run/prod-service.yaml`
|
||||
- `deploy/gcp/cloud-run/preview-service.yaml`
|
||||
- `deploy/gcp/cloud-run/stunnel.conf`
|
||||
- `config/account.cloudrun.yaml`
|
||||
|
||||
|
||||
@ -5,23 +5,42 @@
|
||||
推荐通过 Makefile 与脚本执行:
|
||||
|
||||
```bash
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode process --deploy
|
||||
|
||||
make init-db
|
||||
make build
|
||||
make start
|
||||
```
|
||||
|
||||
默认启动脚本 `scripts/start.sh` 使用 `config/account.yaml`。
|
||||
单机部署脚本 `scripts/install-process.sh` 默认写入 Caddy 配置到 `/etc/caddy/conf.d/accounts.svc.plus.conf`。
|
||||
|
||||
## Docker
|
||||
|
||||
详见 `getting-started/installation.md`。
|
||||
|
||||
初始化命令:
|
||||
|
||||
```bash
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode docker --deploy
|
||||
```
|
||||
|
||||
## Cloud Run
|
||||
|
||||
仓库内的 Cloud Run 配置:
|
||||
- `deploy/gcp/cloud-run/service.yaml`
|
||||
- `deploy/gcp/cloud-run/prod-service.yaml`
|
||||
- `deploy/gcp/cloud-run/preview-service.yaml`
|
||||
- `config/account.cloudrun.yaml`
|
||||
|
||||
初始化命令:
|
||||
|
||||
```bash
|
||||
curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/accounts.svc.plus/main/scripts/setup.sh?$(date +%s)" \
|
||||
| bash -s -- accounts.svc.plus --mode cloudrun
|
||||
```
|
||||
|
||||
特点:
|
||||
- 通过 `entrypoint.sh` + `CONFIG_TEMPLATE` 注入配置
|
||||
- 附带 stunnel sidecar,用于安全连接数据库
|
||||
|
||||
@ -3,6 +3,17 @@ set -euo pipefail
|
||||
|
||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"
|
||||
|
||||
apt_install() {
|
||||
if command -v sudo >/dev/null 2>&1 && [[ "${EUID}" -ne 0 ]]; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y golang
|
||||
return
|
||||
fi
|
||||
|
||||
apt-get update
|
||||
apt-get install -y golang
|
||||
}
|
||||
|
||||
if [ ! -f go.mod ]; then
|
||||
echo ">>> go.mod not found, initializing module"
|
||||
go mod init account
|
||||
@ -17,8 +28,7 @@ if ! command -v go >/dev/null; then
|
||||
brew install go@1.24
|
||||
brew link --overwrite --force go@1.24
|
||||
else
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y golang
|
||||
apt_install
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
155
scripts/install-docker.sh
Normal file
155
scripts/install-docker.sh
Normal file
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"
|
||||
|
||||
if [[ "$(uname -s)" != "Linux" ]]; then
|
||||
echo "docker deployment is only supported on Linux" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "docker deployment must run as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
need_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "missing required command: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
need_cmd docker
|
||||
need_cmd systemctl
|
||||
need_cmd install
|
||||
need_cmd curl
|
||||
|
||||
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus-docker}"
|
||||
CONTAINER_NAME="${CONTAINER_NAME:-accounts-svc-plus}"
|
||||
IMAGE_TAG="${IMAGE_TAG:-accounts-svc-plus:local}"
|
||||
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
|
||||
LISTEN_ADDR="${ACCOUNT_LISTEN_ADDR:-127.0.0.1:8080}"
|
||||
PUBLIC_URL="${ACCOUNT_PUBLIC_URL:-https://${DOMAIN}}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
|
||||
CONFIG_PATH="${CONFIG_PATH:-${CONFIG_DIR}/account.standalone.yaml}"
|
||||
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
|
||||
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
|
||||
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
|
||||
DOCKER_BIN="$(command -v docker)"
|
||||
|
||||
install -d -m 0755 "${CONFIG_DIR}" "${CADDY_CONF_DIR}"
|
||||
|
||||
cat > "${CONFIG_PATH}" <<EOF
|
||||
mode: "server-agent"
|
||||
|
||||
log:
|
||||
level: info
|
||||
|
||||
auth:
|
||||
enable: true
|
||||
token:
|
||||
publicToken: "standalone-public-token"
|
||||
refreshSecret: "standalone-refresh-secret"
|
||||
accessSecret: "standalone-access-secret"
|
||||
accessExpiry: "1h"
|
||||
refreshExpiry: "168h"
|
||||
|
||||
server:
|
||||
addr: ":8080"
|
||||
readTimeout: 15s
|
||||
writeTimeout: 15s
|
||||
publicUrl: "${PUBLIC_URL}"
|
||||
allowedOrigins:
|
||||
- "${PUBLIC_URL}"
|
||||
- "http://localhost:8080"
|
||||
- "http://127.0.0.1:8080"
|
||||
|
||||
store:
|
||||
driver: "memory"
|
||||
dsn: ""
|
||||
|
||||
session:
|
||||
ttl: 24h
|
||||
|
||||
smtp:
|
||||
host: ""
|
||||
port: 587
|
||||
username: ""
|
||||
password: ""
|
||||
from: ""
|
||||
replyTo: ""
|
||||
timeout: 10s
|
||||
tls:
|
||||
mode: "auto"
|
||||
insecureSkipVerify: false
|
||||
|
||||
xray:
|
||||
sync:
|
||||
enabled: false
|
||||
interval: 5m
|
||||
outputPath: ""
|
||||
templatePath: ""
|
||||
validateCommand: []
|
||||
restartCommand: []
|
||||
|
||||
agent:
|
||||
id: "account-primary"
|
||||
controllerUrl: "http://127.0.0.1:8080"
|
||||
apiToken: "standalone-agent-token"
|
||||
httpTimeout: 15s
|
||||
statusInterval: 1m
|
||||
syncInterval: 5m
|
||||
tls:
|
||||
insecureSkipVerify: false
|
||||
|
||||
agents:
|
||||
credentials:
|
||||
- id: "account-primary"
|
||||
name: "Account Server (docker)"
|
||||
token: "standalone-agent-token"
|
||||
groups:
|
||||
- "default"
|
||||
EOF
|
||||
|
||||
cat > "${SYSTEMD_UNIT_PATH}" <<EOF
|
||||
[Unit]
|
||||
Description=Accounts Service Plus (docker mode)
|
||||
After=docker.service network-online.target
|
||||
Requires=docker.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
ExecStartPre=-${DOCKER_BIN} rm -f ${CONTAINER_NAME}
|
||||
ExecStart=${DOCKER_BIN} run --rm --name ${CONTAINER_NAME} -p ${LISTEN_ADDR}:8080 -v ${CONFIG_PATH}:/etc/xcontrol/account.yaml:ro ${IMAGE_TAG}
|
||||
ExecStop=${DOCKER_BIN} stop ${CONTAINER_NAME}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat > "${CADDY_CONF_PATH}" <<EOF
|
||||
${DOMAIN} {
|
||||
encode zstd gzip
|
||||
reverse_proxy ${LISTEN_ADDR}
|
||||
}
|
||||
EOF
|
||||
|
||||
docker build -t "${IMAGE_TAG}" .
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now "${SERVICE_NAME}.service"
|
||||
|
||||
if systemctl is-active --quiet caddy; then
|
||||
systemctl reload caddy || systemctl restart caddy
|
||||
fi
|
||||
|
||||
curl -fsS "http://${LISTEN_ADDR}/healthz" >/dev/null
|
||||
|
||||
echo "docker deployment complete"
|
||||
echo "service: ${SERVICE_NAME}.service"
|
||||
echo "image: ${IMAGE_TAG}"
|
||||
echo "caddy: ${CADDY_CONF_PATH}"
|
||||
163
scripts/install-process.sh
Normal file
163
scripts/install-process.sh
Normal file
@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"
|
||||
|
||||
if [[ "$(uname -s)" != "Linux" ]]; then
|
||||
echo "process deployment is only supported on Linux" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "process deployment must run as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
need_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "missing required command: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
need_cmd systemctl
|
||||
need_cmd install
|
||||
need_cmd curl
|
||||
|
||||
if ! command -v go >/dev/null 2>&1; then
|
||||
bash scripts/init-go.sh
|
||||
fi
|
||||
need_cmd go
|
||||
|
||||
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus}"
|
||||
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
|
||||
LISTEN_ADDR="${ACCOUNT_LISTEN_ADDR:-127.0.0.1:8080}"
|
||||
PUBLIC_URL="${ACCOUNT_PUBLIC_URL:-https://${DOMAIN}}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
|
||||
CONFIG_PATH="${CONFIG_PATH:-${CONFIG_DIR}/account.standalone.yaml}"
|
||||
BINARY_PATH="${BINARY_PATH:-/usr/local/bin/accounts-svc-plus}"
|
||||
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
|
||||
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
|
||||
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
|
||||
|
||||
tmp_binary="$(mktemp)"
|
||||
trap 'rm -f "${tmp_binary}"' EXIT
|
||||
|
||||
go build -o "${tmp_binary}" ./cmd/accountsvc/main.go
|
||||
|
||||
install -d -m 0755 "$(dirname "${BINARY_PATH}")" "${CONFIG_DIR}" "${CADDY_CONF_DIR}"
|
||||
install -m 0755 "${tmp_binary}" "${BINARY_PATH}"
|
||||
|
||||
cat > "${CONFIG_PATH}" <<EOF
|
||||
mode: "server-agent"
|
||||
|
||||
log:
|
||||
level: info
|
||||
|
||||
auth:
|
||||
enable: true
|
||||
token:
|
||||
publicToken: "standalone-public-token"
|
||||
refreshSecret: "standalone-refresh-secret"
|
||||
accessSecret: "standalone-access-secret"
|
||||
accessExpiry: "1h"
|
||||
refreshExpiry: "168h"
|
||||
|
||||
server:
|
||||
addr: "${LISTEN_ADDR}"
|
||||
readTimeout: 15s
|
||||
writeTimeout: 15s
|
||||
publicUrl: "${PUBLIC_URL}"
|
||||
allowedOrigins:
|
||||
- "${PUBLIC_URL}"
|
||||
- "http://localhost:8080"
|
||||
- "http://127.0.0.1:8080"
|
||||
|
||||
store:
|
||||
driver: "memory"
|
||||
dsn: ""
|
||||
|
||||
session:
|
||||
ttl: 24h
|
||||
|
||||
smtp:
|
||||
host: ""
|
||||
port: 587
|
||||
username: ""
|
||||
password: ""
|
||||
from: ""
|
||||
replyTo: ""
|
||||
timeout: 10s
|
||||
tls:
|
||||
mode: "auto"
|
||||
insecureSkipVerify: false
|
||||
|
||||
xray:
|
||||
sync:
|
||||
enabled: false
|
||||
interval: 5m
|
||||
outputPath: ""
|
||||
templatePath: ""
|
||||
validateCommand: []
|
||||
restartCommand: []
|
||||
|
||||
agent:
|
||||
id: "account-primary"
|
||||
controllerUrl: "http://${LISTEN_ADDR}"
|
||||
apiToken: "standalone-agent-token"
|
||||
httpTimeout: 15s
|
||||
statusInterval: 1m
|
||||
syncInterval: 5m
|
||||
tls:
|
||||
insecureSkipVerify: false
|
||||
|
||||
agents:
|
||||
credentials:
|
||||
- id: "account-primary"
|
||||
name: "Account Server (standalone)"
|
||||
token: "standalone-agent-token"
|
||||
groups:
|
||||
- "default"
|
||||
EOF
|
||||
|
||||
cat > "${SYSTEMD_UNIT_PATH}" <<EOF
|
||||
[Unit]
|
||||
Description=Accounts Service Plus (process mode)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
DynamicUser=yes
|
||||
StateDirectory=${SERVICE_NAME}
|
||||
WorkingDirectory=/var/lib/${SERVICE_NAME}
|
||||
Environment=HOME=/var/lib/${SERVICE_NAME}
|
||||
ExecStart=${BINARY_PATH} --config ${CONFIG_PATH}
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
NoNewPrivileges=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat > "${CADDY_CONF_PATH}" <<EOF
|
||||
${DOMAIN} {
|
||||
encode zstd gzip
|
||||
reverse_proxy ${LISTEN_ADDR}
|
||||
}
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now "${SERVICE_NAME}.service"
|
||||
|
||||
if systemctl is-active --quiet caddy; then
|
||||
systemctl reload caddy || systemctl restart caddy
|
||||
fi
|
||||
|
||||
curl -fsS "http://${LISTEN_ADDR}/healthz" >/dev/null
|
||||
|
||||
echo "process deployment complete"
|
||||
echo "service: ${SERVICE_NAME}.service"
|
||||
echo "config: ${CONFIG_PATH}"
|
||||
echo "caddy: ${CADDY_CONF_PATH}"
|
||||
153
scripts/setup.sh
153
scripts/setup.sh
@ -4,14 +4,23 @@ set -euo pipefail
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
setup.sh <repo_name_or_dir> [--repo <git_url>] [--ref <git_ref>] [--dir <path>]
|
||||
setup.sh <repo_name_or_dir> [--mode <process|docker|cloudrun>] [--deploy|--uninstall] [--repo <git_url>] [--ref <git_ref>] [--dir <path>]
|
||||
|
||||
Examples:
|
||||
# Remote install:
|
||||
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo>
|
||||
# Remote install (process mode):
|
||||
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo> --mode process --deploy
|
||||
#
|
||||
# Remote install (docker mode):
|
||||
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo> --mode docker --deploy
|
||||
#
|
||||
# Remote install (Cloud Run mode):
|
||||
# curl -fsSL "https://raw.githubusercontent.com/cloud-neutral-toolkit/<repo>/main/scripts/setup.sh?$(date +%s)" | bash -s -- <repo> --mode cloudrun
|
||||
#
|
||||
# Local:
|
||||
# bash scripts/setup.sh <repo>
|
||||
# bash scripts/setup.sh <repo> --mode process
|
||||
#
|
||||
# Uninstall:
|
||||
# bash scripts/setup.sh <repo> --mode process --uninstall
|
||||
|
||||
Notes:
|
||||
- Safe: no secrets written; no destructive actions.
|
||||
@ -21,6 +30,19 @@ EOF
|
||||
|
||||
log() { printf '[setup] %s\n' "$*"; }
|
||||
|
||||
normalize_mode() {
|
||||
case "${1:-process}" in
|
||||
process|proc|local|binary) printf 'process\n' ;;
|
||||
docker|container) printf 'docker\n' ;;
|
||||
cloudrun|cloud-run|gcp) printf 'cloudrun\n' ;;
|
||||
*)
|
||||
log "unsupported mode: ${1:-}"
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
need_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
log "missing required command: $1"
|
||||
@ -39,9 +61,14 @@ shift
|
||||
REPO_URL=""
|
||||
REF="main"
|
||||
DIR="$NAME"
|
||||
MODE="process"
|
||||
ACTION="setup"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode|--deploy-mode) MODE="${2:-}"; shift 2 ;;
|
||||
--deploy) ACTION="deploy"; shift ;;
|
||||
--uninstall) ACTION="uninstall"; shift ;;
|
||||
--repo) REPO_URL="${2:-}"; shift 2 ;;
|
||||
--ref) REF="${2:-}"; shift 2 ;;
|
||||
--dir) DIR="${2:-}"; shift 2 ;;
|
||||
@ -49,6 +76,8 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
MODE="$(normalize_mode "$MODE")"
|
||||
|
||||
if [[ -z "${REPO_URL}" ]]; then
|
||||
REPO_URL="https://github.com/cloud-neutral-toolkit/${NAME}.git"
|
||||
fi
|
||||
@ -74,29 +103,61 @@ fi
|
||||
|
||||
cd "${DIR}"
|
||||
|
||||
if [[ "${ACTION}" == "uninstall" ]]; then
|
||||
case "$MODE" in
|
||||
process) bash scripts/uninstall-process.sh ;;
|
||||
docker) bash scripts/uninstall-docker.sh ;;
|
||||
cloudrun)
|
||||
log "cloudrun uninstall is not supported by setup.sh; use gcloud directly"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
|
||||
did_any=false
|
||||
|
||||
if [[ -f "package.json" ]]; then
|
||||
need_cmd node
|
||||
if command -v corepack >/dev/null 2>&1; then
|
||||
corepack enable >/dev/null 2>&1 || true
|
||||
fi
|
||||
if command -v yarn >/dev/null 2>&1; then
|
||||
log "installing JS dependencies (yarn install)"
|
||||
yarn install
|
||||
did_any=true
|
||||
else
|
||||
log "yarn not found; install yarn (or enable corepack) then re-run"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
case "$MODE" in
|
||||
process)
|
||||
if [[ -f "package.json" ]]; then
|
||||
need_cmd node
|
||||
if command -v corepack >/dev/null 2>&1; then
|
||||
corepack enable >/dev/null 2>&1 || true
|
||||
fi
|
||||
if command -v yarn >/dev/null 2>&1; then
|
||||
log "installing JS dependencies (yarn install)"
|
||||
yarn install
|
||||
did_any=true
|
||||
else
|
||||
log "yarn not found; install yarn (or enable corepack) then re-run"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "go.mod" ]]; then
|
||||
need_cmd go
|
||||
log "downloading Go dependencies (go mod download)"
|
||||
go mod download
|
||||
did_any=true
|
||||
fi
|
||||
if [[ -f "go.mod" ]]; then
|
||||
if command -v go >/dev/null 2>&1; then
|
||||
log "downloading Go dependencies (go mod download)"
|
||||
go mod download
|
||||
elif [[ "${ACTION}" == "deploy" ]]; then
|
||||
log "go not found; process deploy will install it during scripts/install-process.sh"
|
||||
else
|
||||
need_cmd go
|
||||
fi
|
||||
did_any=true
|
||||
fi
|
||||
;;
|
||||
docker)
|
||||
need_cmd docker
|
||||
log "docker mode selected; skipping host-level dependency installation"
|
||||
did_any=true
|
||||
;;
|
||||
cloudrun)
|
||||
need_cmd docker
|
||||
need_cmd gcloud
|
||||
log "cloudrun mode selected; skipping host-level dependency installation"
|
||||
did_any=true
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "${did_any}" == "false" ]]; then
|
||||
log "no supported project type detected (package.json/go.mod)."
|
||||
@ -113,10 +174,44 @@ if [[ -f "scripts/post-setup.sh" ]]; then
|
||||
bash scripts/post-setup.sh
|
||||
fi
|
||||
|
||||
log "setup complete"
|
||||
log "next steps:"
|
||||
if [[ -f "go.mod" ]]; then
|
||||
log " cp .env.example .env"
|
||||
log " make dev"
|
||||
if [[ "${ACTION}" == "deploy" ]]; then
|
||||
case "$MODE" in
|
||||
process) bash scripts/install-process.sh ;;
|
||||
docker) bash scripts/install-docker.sh ;;
|
||||
cloudrun)
|
||||
if [[ -z "${GCP_PROJECT:-}" ]]; then
|
||||
log "cloudrun deploy requires GCP_PROJECT in the environment"
|
||||
exit 2
|
||||
fi
|
||||
make cloudrun-build
|
||||
make cloudrun-deploy
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
log "setup complete (mode=${MODE})"
|
||||
log "next steps:"
|
||||
case "$MODE" in
|
||||
process)
|
||||
if [[ -f ".env.example" ]]; then
|
||||
log " cp .env.example .env"
|
||||
fi
|
||||
if [[ -f "Makefile" ]]; then
|
||||
log " make init-db"
|
||||
log " make start"
|
||||
log " sudo bash scripts/install-process.sh"
|
||||
elif [[ -f "go.mod" ]]; then
|
||||
log " go run ./cmd/accountsvc/main.go --config config/account.yaml"
|
||||
fi
|
||||
;;
|
||||
docker)
|
||||
log " docker build -t accounts-svc-plus ."
|
||||
log " docker run --rm -p 8080:8080 -e CONFIG_TEMPLATE=/app/config/account.yaml -e CONFIG_PATH=/etc/xcontrol/account.yaml accounts-svc-plus"
|
||||
log " sudo bash scripts/install-docker.sh"
|
||||
;;
|
||||
cloudrun)
|
||||
log " export GCP_PROJECT=<your-project>"
|
||||
log " GCP_PROJECT=<your-project> make cloudrun-build"
|
||||
log " GCP_PROJECT=<your-project> make cloudrun-deploy CLOUD_RUN_SERVICE_YAML=deploy/gcp/cloud-run/prod-service.yaml"
|
||||
;;
|
||||
esac
|
||||
|
||||
42
scripts/uninstall-docker.sh
Normal file
42
scripts/uninstall-docker.sh
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "$(uname -s)" != "Linux" ]]; then
|
||||
echo "docker uninstall is only supported on Linux" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "docker uninstall must run as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus-docker}"
|
||||
CONTAINER_NAME="${CONTAINER_NAME:-accounts-svc-plus}"
|
||||
IMAGE_TAG="${IMAGE_TAG:-accounts-svc-plus:local}"
|
||||
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
|
||||
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
|
||||
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
|
||||
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl disable --now "${SERVICE_NAME}.service" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
|
||||
docker image rm "${IMAGE_TAG}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
rm -f "${SYSTEMD_UNIT_PATH}" "${CADDY_CONF_PATH}"
|
||||
rm -rf "${CONFIG_DIR}"
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload
|
||||
if systemctl is-active --quiet caddy; then
|
||||
systemctl reload caddy || systemctl restart caddy
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "docker deployment removed"
|
||||
36
scripts/uninstall-process.sh
Normal file
36
scripts/uninstall-process.sh
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "$(uname -s)" != "Linux" ]]; then
|
||||
echo "process uninstall is only supported on Linux" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "process uninstall must run as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVICE_NAME="${SERVICE_NAME:-accounts-svc-plus}"
|
||||
DOMAIN="${ACCOUNT_DOMAIN:-accounts.svc.plus}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-/etc/accounts.svc.plus}"
|
||||
BINARY_PATH="${BINARY_PATH:-/usr/local/bin/accounts-svc-plus}"
|
||||
SYSTEMD_UNIT_PATH="${SYSTEMD_UNIT_PATH:-/etc/systemd/system/${SERVICE_NAME}.service}"
|
||||
CADDY_CONF_DIR="${CADDY_CONF_DIR:-/etc/caddy/conf.d}"
|
||||
CADDY_CONF_PATH="${CADDY_CONF_PATH:-${CADDY_CONF_DIR}/${DOMAIN}.conf}"
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl disable --now "${SERVICE_NAME}.service" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
rm -f "${SYSTEMD_UNIT_PATH}" "${BINARY_PATH}" "${CADDY_CONF_PATH}"
|
||||
rm -rf "${CONFIG_DIR}"
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload
|
||||
if systemctl is-active --quiet caddy; then
|
||||
systemctl reload caddy || systemctl restart caddy
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "process deployment removed"
|
||||
Loading…
Reference in New Issue
Block a user