From 1a637fdc27733af64256a1fda02366e7c6517820 Mon Sep 17 00:00:00 2001 From: dev-qwen2 Date: Fri, 17 Apr 2026 14:43:06 +0000 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20[nomad-step-4]=20S4-fix-1=20?= =?UTF-8?q?=E2=80=94=20vault-seed-agents.sh=20must=20seed=20kv/disinto/bot?= =?UTF-8?q?s/dev=20(missing=20from=20.env=20import)=20(#963)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/vault-seed-agents.sh | 55 +++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/tools/vault-seed-agents.sh b/tools/vault-seed-agents.sh index 366bfde..fbed325 100755 --- a/tools/vault-seed-agents.sh +++ b/tools/vault-seed-agents.sh @@ -84,6 +84,18 @@ hvault_ensure_kv_v2 "$KV_MOUNT" "${LOG_TAG}" \ # ── Step 2: seed each bot role ─────────────────────────────────────────────── total_generated=0 +# Check if shared forge credentials exist for dev role fallback +shared_forge_exists=0 +shared_forge_raw="$(hvault_get_or_empty "${KV_MOUNT}/data/disinto/shared/forge")" \ + || true +if [ -n "$shared_forge_raw" ]; then + shared_forge_token="$(printf '%s' "$shared_forge_raw" | jq -r '.data.data.token // ""')" + shared_forge_pass="$(printf '%s' "$shared_forge_raw" | jq -r '.data.data.pass // ""')" + if [ -n "$shared_forge_token" ] && [ -n "$shared_forge_pass" ]; then + shared_forge_exists=1 + fi +fi + for role in "${BOT_ROLES[@]}"; do kv_logical="disinto/bots/${role}" kv_api="${KV_MOUNT}/data/${kv_logical}" @@ -103,12 +115,35 @@ for role in "${BOT_ROLES[@]}"; do fi generated=() + desired_token="$existing_token" + desired_pass="$existing_pass" - if [ -z "$existing_token" ]; then - generated+=("token") - fi - if [ -z "$existing_pass" ]; then - generated+=("pass") + # Special case: dev role uses shared forge credentials if available + if [ "$role" = "dev" ] && [ "$shared_forge_exists" -eq 1 ]; then + # Use shared FORGE_TOKEN + FORGE_PASS for dev role + if [ -z "$existing_token" ]; then + desired_token="$shared_forge_token" + generated+=("token") + fi + if [ -z "$existing_pass" ]; then + desired_pass="$shared_forge_pass" + generated+=("pass") + fi + else + # Generate random values for missing keys + if [ -z "$existing_token" ]; then + generated+=("token") + fi + if [ -z "$existing_pass" ]; then + generated+=("pass") + fi + + for key in "${generated[@]}"; do + case "$key" in + token) desired_token="$(openssl rand -hex "$TOKEN_BYTES")" ;; + pass) desired_pass="$(openssl rand -hex "$PASS_BYTES")" ;; + esac + done fi if [ "${#generated[@]}" -eq 0 ]; then @@ -122,16 +157,6 @@ for role in "${BOT_ROLES[@]}"; do continue fi - desired_token="$existing_token" - desired_pass="$existing_pass" - - for key in "${generated[@]}"; do - case "$key" in - token) desired_token="$(openssl rand -hex "$TOKEN_BYTES")" ;; - pass) desired_pass="$(openssl rand -hex "$PASS_BYTES")" ;; - esac - done - # Merge new keys into existing data to preserve any keys we don't own. payload="$(printf '%s' "$existing_data" \ | jq --arg t "$desired_token" --arg p "$desired_pass" \ From fa6485b1dc561d15d4138756b85f314c8f6d618e Mon Sep 17 00:00:00 2001 From: Agent Date: Fri, 17 Apr 2026 14:43:49 +0000 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20[nomad-step-3]=20S3-fix-6=20?= =?UTF-8?q?=E2=80=94=20woodpecker-agent=20can't=20reach=20server=20gRPC=20?= =?UTF-8?q?at=20localhost:9000=20(port=20bound=20to=20LXC=20IP)=20(#964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nomad/jobs/woodpecker-agent.hcl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nomad/jobs/woodpecker-agent.hcl b/nomad/jobs/woodpecker-agent.hcl index de81459..f753818 100644 --- a/nomad/jobs/woodpecker-agent.hcl +++ b/nomad/jobs/woodpecker-agent.hcl @@ -8,8 +8,9 @@ # # 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. +# server gRPC endpoint is addressed via Nomad service discovery using +# the host's IP address (10.10.10.x:9000), since the server's port +# binding in Nomad binds to the allocation's IP, not localhost. # # Vault integration: # - vault { role = "service-woodpecker-agent" } at the group scope — the @@ -82,8 +83,13 @@ job "woodpecker-agent" { # Non-secret env — server address, gRPC security, concurrency limit, # and health check endpoint. Nothing sensitive here. + # + # WOODPECKER_SERVER uses Nomad's attribute template to get the host's + # IP address (10.10.10.x). The server's gRPC port 9000 is bound via + # Nomad's port stanza to the allocation's IP (not localhost), so the + # agent must use the LXC's eth0 IP, not 127.0.0.1. env { - WOODPECKER_SERVER = "localhost:9000" + WOODPECKER_SERVER = "{{ env \"attr.unique.network.ip-address\" }}:9000" WOODPECKER_GRPC_SECURE = "false" WOODPECKER_MAX_WORKFLOWS = "1" WOODPECKER_HEALTHCHECK_ADDR = ":3333" From 3d62b52e36e081e5beabb9b0dc4be9aa17877f96 Mon Sep 17 00:00:00 2001 From: Agent Date: Fri, 17 Apr 2026 14:43:49 +0000 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20[nomad-step-3]=20S3-fix-6=20?= =?UTF-8?q?=E2=80=94=20woodpecker-agent=20can't=20reach=20server=20gRPC=20?= =?UTF-8?q?at=20localhost:9000=20(port=20bound=20to=20LXC=20IP)=20(#964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nomad/jobs/woodpecker-agent.hcl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nomad/jobs/woodpecker-agent.hcl b/nomad/jobs/woodpecker-agent.hcl index de81459..f753818 100644 --- a/nomad/jobs/woodpecker-agent.hcl +++ b/nomad/jobs/woodpecker-agent.hcl @@ -8,8 +8,9 @@ # # 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. +# server gRPC endpoint is addressed via Nomad service discovery using +# the host's IP address (10.10.10.x:9000), since the server's port +# binding in Nomad binds to the allocation's IP, not localhost. # # Vault integration: # - vault { role = "service-woodpecker-agent" } at the group scope — the @@ -82,8 +83,13 @@ job "woodpecker-agent" { # Non-secret env — server address, gRPC security, concurrency limit, # and health check endpoint. Nothing sensitive here. + # + # WOODPECKER_SERVER uses Nomad's attribute template to get the host's + # IP address (10.10.10.x). The server's gRPC port 9000 is bound via + # Nomad's port stanza to the allocation's IP (not localhost), so the + # agent must use the LXC's eth0 IP, not 127.0.0.1. env { - WOODPECKER_SERVER = "localhost:9000" + WOODPECKER_SERVER = "{{ env \"attr.unique.network.ip-address\" }}:9000" WOODPECKER_GRPC_SECURE = "false" WOODPECKER_MAX_WORKFLOWS = "1" WOODPECKER_HEALTHCHECK_ADDR = ":3333" From ab0a6be41fb86eb9b20064fea19716575df53f53 Mon Sep 17 00:00:00 2001 From: Agent Date: Fri, 17 Apr 2026 14:58:10 +0000 Subject: [PATCH 4/4] fix: use Nomad interpolation syntax for WOODPECKER_SERVER --- nomad/jobs/woodpecker-agent.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nomad/jobs/woodpecker-agent.hcl b/nomad/jobs/woodpecker-agent.hcl index f753818..c7779a2 100644 --- a/nomad/jobs/woodpecker-agent.hcl +++ b/nomad/jobs/woodpecker-agent.hcl @@ -89,7 +89,7 @@ job "woodpecker-agent" { # Nomad's port stanza to the allocation's IP (not localhost), so the # agent must use the LXC's eth0 IP, not 127.0.0.1. env { - WOODPECKER_SERVER = "{{ env \"attr.unique.network.ip-address\" }}:9000" + WOODPECKER_SERVER = "${attr.unique.network.ip-address}:9000" WOODPECKER_GRPC_SECURE = "false" WOODPECKER_MAX_WORKFLOWS = "1" WOODPECKER_HEALTHCHECK_ADDR = ":3333"