From 39425af43e969ffaaeb0efc07eb5218aead0db23 Mon Sep 17 00:00:00 2001 From: AlexBelyan Date: Tue, 19 May 2026 23:27:37 +0300 Subject: [PATCH] Added possibility deploy with k3s --- .gitea/workflows/deploy.yaml | 20 ++++- .gitignore | 4 +- .../000001-create-pg-cron-extension.down.sql | 1 + .../000001-create-pg-cron-extension.up.sql | 0 ....down.sql => 000002_create_users.down.sql} | 0 ...sers.up.sql => 000002_create_users.up.sql} | 0 ...wn.sql => 000003_create_families.down.sql} | 0 ...s.up.sql => 000003_create_families.up.sql} | 0 ... => 000004_create_family_members.down.sql} | 0 ...ql => 000004_create_family_members.up.sql} | 0 ...tp.down.sql => 000005_create_otp.down.sql} | 0 ...te_otp.up.sql => 000005_create_otp.up.sql} | 0 ...wn.sql => 000006_create_receipts.down.sql} | 0 ...s.up.sql => 000006_create_receipts.up.sql} | 0 ...n.sql => 000007_create_positions.down.sql} | 0 ....up.sql => 000007_create_positions.up.sql} | 0 ...l => 000008_create_activity_logs.down.sql} | 0 ...sql => 000008_create_activity_logs.up.sql} | 0 backend/src/config/config.go | 33 ++++++- infra/{docker => }/application/Dockerfile | 21 ++--- infra/docker-compose.yml | 63 ------------- infra/docker/docker-compose.yml | 55 ++++++++++++ infra/docker/postgres-pg-cron/Dockerfile | 5 -- infra/k8s/app.yaml | 62 +++++++++++++ infra/k8s/configmap.yaml | 15 ++++ infra/k8s/ingress.yaml | 19 ++++ infra/k8s/namespace.yaml | 4 + infra/k8s/postgres.yaml | 62 +++++++++++++ infra/postgres/Dockerfile | 8 ++ infra/webhook/Dockerfile | 12 --- infra/webhook/go.mod | 5 -- infra/webhook/go.sum | 1 - infra/webhook/main.go | 88 ------------------- 33 files changed, 283 insertions(+), 195 deletions(-) create mode 100644 backend/migrations/000001-create-pg-cron-extension.down.sql rename infra/docker/postgres-pg-cron/init/001-create-pg-cron-extension.sql => backend/migrations/000001-create-pg-cron-extension.up.sql (100%) rename backend/migrations/{000001_create_users.down.sql => 000002_create_users.down.sql} (100%) rename backend/migrations/{000001_create_users.up.sql => 000002_create_users.up.sql} (100%) rename backend/migrations/{000002_create_families.down.sql => 000003_create_families.down.sql} (100%) rename backend/migrations/{000002_create_families.up.sql => 000003_create_families.up.sql} (100%) rename backend/migrations/{000003_create_family_members.down.sql => 000004_create_family_members.down.sql} (100%) rename backend/migrations/{000003_create_family_members.up.sql => 000004_create_family_members.up.sql} (100%) rename backend/migrations/{000004_create_otp.down.sql => 000005_create_otp.down.sql} (100%) rename backend/migrations/{000004_create_otp.up.sql => 000005_create_otp.up.sql} (100%) rename backend/migrations/{000005_create_receipts.down.sql => 000006_create_receipts.down.sql} (100%) rename backend/migrations/{000005_create_receipts.up.sql => 000006_create_receipts.up.sql} (100%) rename backend/migrations/{000006_create_positions.down.sql => 000007_create_positions.down.sql} (100%) rename backend/migrations/{000006_create_positions.up.sql => 000007_create_positions.up.sql} (100%) rename backend/migrations/{000007_create_activity_logs.down.sql => 000008_create_activity_logs.down.sql} (100%) rename backend/migrations/{000007_create_activity_logs.up.sql => 000008_create_activity_logs.up.sql} (100%) rename infra/{docker => }/application/Dockerfile (67%) delete mode 100644 infra/docker-compose.yml create mode 100644 infra/docker/docker-compose.yml delete mode 100644 infra/docker/postgres-pg-cron/Dockerfile create mode 100644 infra/k8s/app.yaml create mode 100644 infra/k8s/configmap.yaml create mode 100644 infra/k8s/ingress.yaml create mode 100644 infra/k8s/namespace.yaml create mode 100644 infra/k8s/postgres.yaml create mode 100644 infra/postgres/Dockerfile delete mode 100644 infra/webhook/Dockerfile delete mode 100644 infra/webhook/go.mod delete mode 100644 infra/webhook/go.sum delete mode 100644 infra/webhook/main.go diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index 202a898..57282fd 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -49,8 +49,20 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - - name: Trigger deploy + - name: Install kubectl run: | - curl -s -X POST \ - -H "X-Webhook-Secret: ${{ secrets.WEBHOOK_SECRET }}" \ - "http://10.0.0.2:9001/deploy?container=familyhub" \ No newline at end of file + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + chmod +x kubectl + sudo mv kubectl /usr/local/bin/ + + - name: Deploy to k3s + env: + KUBECONFIG_DATA: ${{ secrets.KUBECONFIG }} + run: | + mkdir -p ~/.kube + echo "$KUBECONFIG_DATA" > ~/.kube/config + chmod 600 ~/.kube/config + kubectl rollout restart deployment/application -n family-hub + kubectl rollout restart deployment/postgres -n family-hub + kubectl rollout status deployment/application -n family-hub --timeout=120s + kubectl rollout status deployment/postgres -n family-hub --timeout=120s diff --git a/.gitignore b/.gitignore index f4f75a7..2904e9f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ data archive volumes *.dtmp -*.gocache \ No newline at end of file +*.gocache +infra/k8s/secrets.yaml +infra/k8s/google-creds.yaml \ No newline at end of file diff --git a/backend/migrations/000001-create-pg-cron-extension.down.sql b/backend/migrations/000001-create-pg-cron-extension.down.sql new file mode 100644 index 0000000..8395816 --- /dev/null +++ b/backend/migrations/000001-create-pg-cron-extension.down.sql @@ -0,0 +1 @@ +DROP EXTENSION pg_cron; diff --git a/infra/docker/postgres-pg-cron/init/001-create-pg-cron-extension.sql b/backend/migrations/000001-create-pg-cron-extension.up.sql similarity index 100% rename from infra/docker/postgres-pg-cron/init/001-create-pg-cron-extension.sql rename to backend/migrations/000001-create-pg-cron-extension.up.sql diff --git a/backend/migrations/000001_create_users.down.sql b/backend/migrations/000002_create_users.down.sql similarity index 100% rename from backend/migrations/000001_create_users.down.sql rename to backend/migrations/000002_create_users.down.sql diff --git a/backend/migrations/000001_create_users.up.sql b/backend/migrations/000002_create_users.up.sql similarity index 100% rename from backend/migrations/000001_create_users.up.sql rename to backend/migrations/000002_create_users.up.sql diff --git a/backend/migrations/000002_create_families.down.sql b/backend/migrations/000003_create_families.down.sql similarity index 100% rename from backend/migrations/000002_create_families.down.sql rename to backend/migrations/000003_create_families.down.sql diff --git a/backend/migrations/000002_create_families.up.sql b/backend/migrations/000003_create_families.up.sql similarity index 100% rename from backend/migrations/000002_create_families.up.sql rename to backend/migrations/000003_create_families.up.sql diff --git a/backend/migrations/000003_create_family_members.down.sql b/backend/migrations/000004_create_family_members.down.sql similarity index 100% rename from backend/migrations/000003_create_family_members.down.sql rename to backend/migrations/000004_create_family_members.down.sql diff --git a/backend/migrations/000003_create_family_members.up.sql b/backend/migrations/000004_create_family_members.up.sql similarity index 100% rename from backend/migrations/000003_create_family_members.up.sql rename to backend/migrations/000004_create_family_members.up.sql diff --git a/backend/migrations/000004_create_otp.down.sql b/backend/migrations/000005_create_otp.down.sql similarity index 100% rename from backend/migrations/000004_create_otp.down.sql rename to backend/migrations/000005_create_otp.down.sql diff --git a/backend/migrations/000004_create_otp.up.sql b/backend/migrations/000005_create_otp.up.sql similarity index 100% rename from backend/migrations/000004_create_otp.up.sql rename to backend/migrations/000005_create_otp.up.sql diff --git a/backend/migrations/000005_create_receipts.down.sql b/backend/migrations/000006_create_receipts.down.sql similarity index 100% rename from backend/migrations/000005_create_receipts.down.sql rename to backend/migrations/000006_create_receipts.down.sql diff --git a/backend/migrations/000005_create_receipts.up.sql b/backend/migrations/000006_create_receipts.up.sql similarity index 100% rename from backend/migrations/000005_create_receipts.up.sql rename to backend/migrations/000006_create_receipts.up.sql diff --git a/backend/migrations/000006_create_positions.down.sql b/backend/migrations/000007_create_positions.down.sql similarity index 100% rename from backend/migrations/000006_create_positions.down.sql rename to backend/migrations/000007_create_positions.down.sql diff --git a/backend/migrations/000006_create_positions.up.sql b/backend/migrations/000007_create_positions.up.sql similarity index 100% rename from backend/migrations/000006_create_positions.up.sql rename to backend/migrations/000007_create_positions.up.sql diff --git a/backend/migrations/000007_create_activity_logs.down.sql b/backend/migrations/000008_create_activity_logs.down.sql similarity index 100% rename from backend/migrations/000007_create_activity_logs.down.sql rename to backend/migrations/000008_create_activity_logs.down.sql diff --git a/backend/migrations/000007_create_activity_logs.up.sql b/backend/migrations/000008_create_activity_logs.up.sql similarity index 100% rename from backend/migrations/000007_create_activity_logs.up.sql rename to backend/migrations/000008_create_activity_logs.up.sql diff --git a/backend/src/config/config.go b/backend/src/config/config.go index 6379e9a..2d60ad3 100644 --- a/backend/src/config/config.go +++ b/backend/src/config/config.go @@ -2,6 +2,7 @@ package config import ( "errors" + "fmt" "os" "strings" @@ -33,7 +34,6 @@ func Load() (Config, error) { mode := os.Getenv("RUN_MODE") debugMode := os.Getenv("DEBUG_MODE") == "true" botToken := os.Getenv("BOT_TOKEN") - dbConnectionString := os.Getenv("DB_PATH") ocrTokenPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") apiPort := os.Getenv("API_PORT") apiHost := os.Getenv("API_HOST") @@ -42,6 +42,7 @@ func Load() (Config, error) { openAPIEndpoint := os.Getenv("OPEN_API_ENDPOINT") runMode, err := ParseRunMode(mode) + dbConnectionString := buildConnectionString() if err != nil { warnings = append(warnings, err.Error()) } @@ -61,9 +62,6 @@ func Load() (Config, error) { if apiSecret == "" { warnings = append(warnings, "Missing required environment variable: API_SECRET") } - if dbConnectionString == "" { - dbConnectionString = "sqlite://data/app.db" - } if apiHost == "" { apiHost = "localhost" } @@ -92,3 +90,30 @@ func Load() (Config, error) { TelegramApi: "https://api.telegram.org", }, nil } + +func buildConnectionString() string { + // если задана готовая строка — используем её (удобно для локальной разработки через .env) + if dsn := os.Getenv("DB_PATH"); dsn != "" { + return dsn + } + + // собираем из отдельных переменных (для Kubernetes) + host := os.Getenv("DB_HOST") + port := os.Getenv("DB_PORT") + user := os.Getenv("DB_USER") + password := os.Getenv("DB_PASSWORD") + dbName := os.Getenv("DB_NAME") + + if host == "" || user == "" || password == "" || dbName == "" { + return "" + } + + if port == "" { + port = "5432" + } + + return fmt.Sprintf( + "postgres://%s:%s@%s:%s/%s?sslmode=disable", + user, password, host, port, dbName, + ) +} diff --git a/infra/docker/application/Dockerfile b/infra/application/Dockerfile similarity index 67% rename from infra/docker/application/Dockerfile rename to infra/application/Dockerfile index 0f62880..be8a7df 100644 --- a/infra/docker/application/Dockerfile +++ b/infra/application/Dockerfile @@ -14,7 +14,7 @@ RUN npm run build # ================================ # Stage 2: сборка Go # ================================ -FROM golang:1.25-alpine AS backend +FROM golang:1.26-bookworm AS backend WORKDIR /app @@ -27,22 +27,19 @@ COPY backend/ ./ # встраиваем собранную статику Vue COPY --from=frontend /app/dist ./src/api/dist - +# Миграции кладём туда, откуда Go их ищет +COPY backend/migrations ./migrations # сборка бинарника RUN CGO_ENABLED=0 GOOS=linux go build -o server ./src/ # ================================ # Stage 3: финальный образ # ================================ -FROM alpine:3.19 +FROM scratch -# нужен для корректной работы TLS и временных зон -RUN apk add --no-cache ca-certificates tzdata +COPY --from=backend /app/server /server +COPY --from=backend /app/migrations /migrations +COPY --from=backend /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=backend /usr/share/zoneinfo /usr/share/zoneinfo -WORKDIR /app - -COPY --from=backend /app/server ./server - -EXPOSE 8080 - -ENTRYPOINT ["./server"] \ No newline at end of file +ENTRYPOINT ["/server"] \ No newline at end of file diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml deleted file mode 100644 index 4ec4f7d..0000000 --- a/infra/docker-compose.yml +++ /dev/null @@ -1,63 +0,0 @@ -version: '3.9' - -services: - app: - image: git.myhomecloud.tech/admin/familyhub:latest - container_name: familyhub - restart: unless-stopped - ports: - - "10.0.0.2:8000:8000" # только через WireGuard - environment: - - DB_HOST=db - - DB_PORT=5432 - - DB_USER=familyUser - - DB_PASSWORD=familyPass - - DB_NAME=familyHubDB - depends_on: - - db - networks: - - family-hub-net - - db: - image: git.myhomecloud.tech/admin/familyhub-postgres:latest - container_name: postgres - restart: always - pull_policy: always - command: - - postgres - - -c - - shared_preload_libraries=pg_cron - - -c - - cron.database_name=familyHubDB - environment: - POSTGRES_USER: familyUser - POSTGRES_PASSWORD: familyPass - POSTGRES_DB: familyHubDB - volumes: - - postgres-data:/var/lib/postgresql/data - - ./init:/docker-entrypoint-initdb.d - networks: - - family-hub-net - - webhook: - image: git.myhomecloud.tech/admin/familyhub-webhook:latest - container_name: webhook - restart: unless-stopped - pull_policy: always - ports: - - "10.0.0.2:9001:9001" - environment: - - WEBHOOK_SECRET=${WEBHOOK_SECRET} - - COMPOSE_FILE=/compose/docker-compose.yml - - COMPOSE_PROJECT=familyhub - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./docker-compose.yml:/compose/docker-compose.yml:ro - networks: - - family-hub-net - -networks: - family-hub-net: - -volumes: - postgres-data: diff --git a/infra/docker/docker-compose.yml b/infra/docker/docker-compose.yml new file mode 100644 index 0000000..8a564eb --- /dev/null +++ b/infra/docker/docker-compose.yml @@ -0,0 +1,55 @@ +version: '3.9' + +services: + app: + image: git.myhomecloud.tech/admin/familyhub:latest + container_name: application + restart: unless-stopped + ports: + - "8000:8000" + environment: + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_USER=${DB_USER} + - DB_PASSWORD=${DB_PASSWORD} + - DB_NAME=${DB_NAME} + - BOT_TOKEN=${BOT_TOKEN} + - GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS} + - RUN_MODE=${RUN_MODE} + - API_SECRET=${API_SECRET} + - DB_PATH=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable + - OPEN_API_ENABLED=${OPEN_API_ENABLED} + - DEBUG_MODE=${DEBUG_MODE} + depends_on: + - db + networks: + - family-hub-net + + db: + image: git.myhomecloud.tech/admin/familyhub-postgres:latest + container_name: postgres + restart: always + pull_policy: always + ports: + - "5432:5432" + command: + - postgres + - -c + - shared_preload_libraries=pg_cron + - -c + - cron.database_name=familyHubDB + environment: + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: ${DB_NAME} + volumes: + - postgres-data:/var/lib/postgresql/data + - ./init:/docker-entrypoint-initdb.d + networks: + - family-hub-net + +networks: + family-hub-net: + +volumes: + postgres-data: diff --git a/infra/docker/postgres-pg-cron/Dockerfile b/infra/docker/postgres-pg-cron/Dockerfile deleted file mode 100644 index 1ebcdc4..0000000 --- a/infra/docker/postgres-pg-cron/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM postgres:16 - -RUN apt-get update \ - && apt-get install -y --no-install-recommends postgresql-16-cron \ - && rm -rf /var/lib/apt/lists/* diff --git a/infra/k8s/app.yaml b/infra/k8s/app.yaml new file mode 100644 index 0000000..1389f8d --- /dev/null +++ b/infra/k8s/app.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: application + namespace: family-hub +spec: + replicas: 1 + selector: + matchLabels: + app: application + template: + metadata: + labels: + app: application + spec: + containers: + - name: application + image: git.myhomecloud.tech/admin/familyhub:latest + ports: + - containerPort: 8000 + envFrom: + - configMapRef: + name: family-hub-config + - secretRef: + name: family-hub-secrets + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /secrets/credentials.json + volumeMounts: + - name: google-credentials + mountPath: /secrets + readOnly: true +# livenessProbe: +# httpGet: +# path: /api/v1/health +# port: 8000 +# initialDelaySeconds: 10 +# periodSeconds: 30 +# readinessProbe: +# httpGet: +# path: /api/v1/health +# port: 8000 +# initialDelaySeconds: 5 +# periodSeconds: 10 + volumes: + - name: google-credentials + secret: + secretName: google-credentials + imagePullSecrets: + - name: gitea-registry +--- +apiVersion: v1 +kind: Service +metadata: + name: application + namespace: family-hub +spec: + selector: + app: application + ports: + - port: 9876 + targetPort: 8000 \ No newline at end of file diff --git a/infra/k8s/configmap.yaml b/infra/k8s/configmap.yaml new file mode 100644 index 0000000..76c08c8 --- /dev/null +++ b/infra/k8s/configmap.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: family-hub-config + namespace: family-hub +data: + DB_HOST: postgres + DB_PORT: "5432" + DB_NAME: familyHubDB + DB_USER: familyUser + API_PORT: "8000" + API_HOST: 0.0.0.0 + RUN_MODE: standalone + OPEN_API_ENABLED: "true" + DEBUG_MODE: "false" \ No newline at end of file diff --git a/infra/k8s/ingress.yaml b/infra/k8s/ingress.yaml new file mode 100644 index 0000000..beb546f --- /dev/null +++ b/infra/k8s/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: application + namespace: family-hub + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web +spec: + rules: + - host: application.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: application + port: + number: 9876 \ No newline at end of file diff --git a/infra/k8s/namespace.yaml b/infra/k8s/namespace.yaml new file mode 100644 index 0000000..fc85d73 --- /dev/null +++ b/infra/k8s/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: family-hub \ No newline at end of file diff --git a/infra/k8s/postgres.yaml b/infra/k8s/postgres.yaml new file mode 100644 index 0000000..b6e9313 --- /dev/null +++ b/infra/k8s/postgres.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + namespace: family-hub +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: git.myhomecloud.tech/admin/familyhub-postgres:latest + env: + - name: POSTGRES_USER + value: familyUser + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: family-hub-secrets + key: DB_PASSWORD + - name: POSTGRES_DB + value: familyHubDB + ports: + - containerPort: 5432 + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + imagePullSecrets: + - name: gitea-registry + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: family-hub +spec: + selector: + app: postgres + ports: + - port: 5432 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + namespace: family-hub +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi \ No newline at end of file diff --git a/infra/postgres/Dockerfile b/infra/postgres/Dockerfile new file mode 100644 index 0000000..d802512 --- /dev/null +++ b/infra/postgres/Dockerfile @@ -0,0 +1,8 @@ +FROM postgres:16 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends postgresql-16-cron \ + && rm -rf /var/lib/apt/lists/* \ + +RUN echo "shared_preload_libraries = 'pg_cron'" >> /usr/share/postgresql/postgresql.conf.sample \ + && echo "cron.database_name = 'familyHubDB'" >> /usr/share/postgresql/postgresql.conf.sample \ No newline at end of file diff --git a/infra/webhook/Dockerfile b/infra/webhook/Dockerfile deleted file mode 100644 index fea7be5..0000000 --- a/infra/webhook/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:1.25-alpine AS builder - -WORKDIR /app -COPY infra/webhook/ . -RUN go mod download && \ - CGO_ENABLED=0 GOOS=linux go build -o webhook . - -FROM alpine:3.19 -RUN apk add --no-cache docker-cli ca-certificates -COPY --from=builder /app/webhook /webhook -EXPOSE 9001 -ENTRYPOINT ["/webhook"] \ No newline at end of file diff --git a/infra/webhook/go.mod b/infra/webhook/go.mod deleted file mode 100644 index a3261a0..0000000 --- a/infra/webhook/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module webhook - -go 1.25 - -require github.com/docker/docker v26.1.0+incompatible \ No newline at end of file diff --git a/infra/webhook/go.sum b/infra/webhook/go.sum deleted file mode 100644 index e358dc9..0000000 --- a/infra/webhook/go.sum +++ /dev/null @@ -1 +0,0 @@ -github.com/docker/docker v26.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= diff --git a/infra/webhook/main.go b/infra/webhook/main.go deleted file mode 100644 index 50c05a0..0000000 --- a/infra/webhook/main.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "log" - "net/http" - "os" - "os/exec" - - "github.com/docker/docker/client" -) - -func main() { - secret := os.Getenv("WEBHOOK_SECRET") - - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - log.Fatal("failed to create docker client:", err) - } - defer cli.Close() - - http.HandleFunc("/deploy", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) - return - } - - if r.Header.Get("X-Webhook-Secret") != secret { - http.Error(w, "unauthorized", http.StatusUnauthorized) - return - } - - // имя контейнера берём из query параметра - containerName := r.URL.Query().Get("container") - if containerName == "" { - http.Error(w, "container parameter is required", http.StatusBadRequest) - return - } - - ctx := context.Background() - - // получаем инфо о контейнере чтобы узнать имя образа - inspect, err := cli.ContainerInspect(ctx, containerName) - if err != nil { - log.Printf("failed to inspect container %s: %v", containerName, err) - http.Error(w, "container not found", http.StatusNotFound) - return - } - - imageName := inspect.Config.Image - log.Printf("Container: %s, Image: %s", containerName, imageName) - - // тянем новый образ - log.Println("Pulling new image...") - pull := exec.Command("docker", "pull", imageName) - pull.Stdout = os.Stdout - pull.Stderr = os.Stderr - if err := pull.Run(); err != nil { - log.Println("pull failed:", err) - http.Error(w, "pull failed", http.StatusInternalServerError) - return - } - - // перезапускаем контейнер - log.Printf("Restarting container %s...", containerName) - restart := exec.Command("docker", "restart", containerName) - restart.Stdout = os.Stdout - restart.Stderr = os.Stderr - if err := restart.Run(); err != nil { - log.Println("restart failed:", err) - http.Error(w, "restart failed", http.StatusInternalServerError) - return - } - - log.Printf("Deploy of %s completed", containerName) - w.WriteHeader(http.StatusOK) - - json.NewEncoder(w).Encode(map[string]string{ - "status": "ok", - "container": containerName, - "image": imageName, - }) - }) - - log.Println("Webhook server listening on :9001") - log.Fatal(http.ListenAndServe(":9001", nil)) -}