diff --git a/.woodpecker/detect-duplicates.py b/.woodpecker/detect-duplicates.py index 58fc160..0485833 100644 --- a/.woodpecker/detect-duplicates.py +++ b/.woodpecker/detect-duplicates.py @@ -294,13 +294,6 @@ def main() -> int: "9f6ae8e7811575b964279d8820494eb0": "Verification helper: for loop done pattern", # Standard lib source block shared across formula-driven agent run scripts "330e5809a00b95ade1a5fce2d749b94b": "Standard lib source block (env.sh, formula-session.sh, worktree.sh, guard.sh, agent-sdk.sh)", - # Common vault-seed script patterns: logging helpers + flag parsing - # Used in tools/vault-seed-woodpecker.sh + lib/init/nomad/wp-oauth-register.sh - "843a1cbf987952697d4e05e96ed2b2d5": "Logging helpers + DRY_RUN init (vault-seed-woodpecker + wp-oauth-register)", - "ee51df9642f2ef37af73b0c15f4d8406": "Logging helpers + DRY_RUN loop start (vault-seed-woodpecker + wp-oauth-register)", - "9a57368f3c1dfd29ec328596b86962a0": "Flag parsing loop + case start (vault-seed-woodpecker + wp-oauth-register)", - "9d72d40ff303cbed0b7e628fc15381c3": "Case loop + dry-run handler (vault-seed-woodpecker + wp-oauth-register)", - "5b52ddbbf47948e3cbc1b383f0909588": "Help + invalid arg handler end (vault-seed-woodpecker + wp-oauth-register)", } if not sh_files: diff --git a/nomad/jobs/woodpecker-agent.hcl b/nomad/jobs/woodpecker-agent.hcl deleted file mode 100644 index de81459..0000000 --- a/nomad/jobs/woodpecker-agent.hcl +++ /dev/null @@ -1,138 +0,0 @@ -# ============================================================================= -# nomad/jobs/woodpecker-agent.hcl — Woodpecker CI agent (Nomad service job) -# -# Part of the Nomad+Vault migration (S3.2, issue #935). -# Drop-in for the current docker-compose setup with host networking + -# docker.sock mount, enabling the agent to spawn containers via the -# mounted socket. -# -# Host networking: -# Uses network_mode = "host" to match the compose setup. The Woodpecker -# server gRPC endpoint is addressed as "localhost:9000" since both -# server and agent run on the same host. -# -# Vault integration: -# - vault { role = "service-woodpecker-agent" } at the group scope — the -# task's workload-identity JWT is exchanged for a Vault token carrying -# the policy named on that role. Role + policy are defined in -# vault/roles.yaml + vault/policies/service-woodpecker.hcl. -# - template stanza pulls WOODPECKER_AGENT_SECRET from Vault KV v2 -# at kv/disinto/shared/woodpecker and writes it to secrets/agent.env. -# Seeded on fresh boxes by tools/vault-seed-woodpecker.sh. -# ============================================================================= - -job "woodpecker-agent" { - type = "service" - datacenters = ["dc1"] - - group "woodpecker-agent" { - count = 1 - - # ── Vault workload identity ───────────────────────────────────────── - # `role = "service-woodpecker-agent"` is defined in vault/roles.yaml and - # applied by tools/vault-apply-roles.sh. The role's bound - # claim pins nomad_job_id = "woodpecker-agent" — renaming this - # jobspec's `job "woodpecker-agent"` without updating vault/roles.yaml - # will make token exchange fail at placement with a "claim mismatch" - # error. - vault { - role = "service-woodpecker-agent" - } - - # Health check port: static 3333 for Nomad service discovery. The agent - # exposes :3333/healthz for Nomad to probe. - network { - port "healthz" { - static = 3333 - } - } - - # Native Nomad service discovery for the health check endpoint. - service { - name = "woodpecker-agent" - port = "healthz" - provider = "nomad" - - check { - type = "http" - path = "/healthz" - interval = "15s" - timeout = "3s" - } - } - - # Conservative restart policy — fail fast to the scheduler instead of - # spinning on a broken image/config. 3 attempts over 5m, then back off. - restart { - attempts = 3 - interval = "5m" - delay = "15s" - mode = "delay" - } - - task "woodpecker-agent" { - driver = "docker" - - config { - image = "woodpeckerci/woodpecker-agent:v3" - network_mode = "host" - privileged = true - volumes = ["/var/run/docker.sock:/var/run/docker.sock"] - } - - # Non-secret env — server address, gRPC security, concurrency limit, - # and health check endpoint. Nothing sensitive here. - env { - WOODPECKER_SERVER = "localhost:9000" - WOODPECKER_GRPC_SECURE = "false" - WOODPECKER_MAX_WORKFLOWS = "1" - WOODPECKER_HEALTHCHECK_ADDR = ":3333" - } - - # ── Vault-templated agent secret ────────────────────────────────── - # Renders /secrets/agent.env (per-alloc secrets dir, - # never on disk on the host root filesystem, never in `nomad job - # inspect` output). `env = true` merges WOODPECKER_AGENT_SECRET - # from the file into the task environment. - # - # Vault path: `kv/data/disinto/shared/woodpecker`. The literal - # `/data/` segment is required by consul-template for KV v2 mounts. - # - # Empty-Vault fallback (`with ... else ...`): on a fresh LXC where - # the KV path is absent, consul-template's `with` short-circuits to - # the `else` branch. Emitting a visible placeholder means the - # container still boots, but with an obviously-bad secret that an - # operator will spot — better than the agent failing silently with - # auth errors. Seed the path with tools/vault-seed-woodpecker.sh - # to replace the placeholder. - # - # Placeholder values are kept short on purpose: the repo-wide - # secret-scan (.woodpecker/secret-scan.yml → lib/secret-scan.sh) - # flags `TOKEN=<16+ non-space chars>` as a plaintext secret, so a - # descriptive long placeholder would fail CI on every PR that touched - # this file. "seed-me" is < 16 chars and still distinctive enough - # to surface in a `grep WOODPECKER` audit. - template { - destination = "secrets/agent.env" - env = true - change_mode = "restart" - error_on_missing_key = false - data = <.hcl — land in later steps) ─────── # job_id placeholders match the policy name 1:1 until each bot's jobspec # lands. When a bot's jobspec is added under nomad/jobs/, update the