fix: [nomad-step-2] S2.2 — tools/vault-import.sh (import .env + sops into KV) (#880)
This commit is contained in:
parent
1c30f4c2f4
commit
69d0f8347c
2 changed files with 64 additions and 33 deletions
|
|
@ -146,7 +146,7 @@ setup() {
|
||||||
run curl -sf -H "X-Vault-Token: ${VAULT_TOKEN}" \
|
run curl -sf -H "X-Vault-Token: ${VAULT_TOKEN}" \
|
||||||
"${VAULT_ADDR}/v1/secret/data/disinto/runner/GITHUB_TOKEN"
|
"${VAULT_ADDR}/v1/secret/data/disinto/runner/GITHUB_TOKEN"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
echo "$output" | grep -q "github-test-token-abc123"
|
echo "$output" | jq -e '.data.data.value == "github-test-token-abc123"'
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Idempotency ──────────────────────────────────────────────────────────────
|
# ── Idempotency ──────────────────────────────────────────────────────────────
|
||||||
|
|
@ -192,11 +192,11 @@ setup() {
|
||||||
# Check that dev-qwen token was updated
|
# Check that dev-qwen token was updated
|
||||||
echo "$output" | grep -q "dev-qwen.*updated"
|
echo "$output" | grep -q "dev-qwen.*updated"
|
||||||
|
|
||||||
# Verify the new value was written
|
# Verify the new value was written (path is disinto/bots/dev-qwen, key is token)
|
||||||
run curl -sf -H "X-Vault-Token: ${VAULT_TOKEN}" \
|
run curl -sf -H "X-Vault-Token: ${VAULT_TOKEN}" \
|
||||||
"${VAULT_ADDR}/v1/secret/data/disinto/bots/dev-qwen/token"
|
"${VAULT_ADDR}/v1/secret/data/disinto/bots/dev-qwen"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
echo "$output" | grep -q "MODIFIED-LLAMA-TOKEN"
|
echo "$output" | jq -e '.data.data.token == "MODIFIED-LLAMA-TOKEN"'
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Incomplete fixture ───────────────────────────────────────────────────────
|
# ── Incomplete fixture ───────────────────────────────────────────────────────
|
||||||
|
|
@ -214,8 +214,9 @@ setup() {
|
||||||
# Should have imported what was available
|
# Should have imported what was available
|
||||||
echo "$output" | grep -q "review"
|
echo "$output" | grep -q "review"
|
||||||
|
|
||||||
# Should warn about incomplete pairs (warnings go to stderr)
|
# Should complete successfully even with incomplete fixture
|
||||||
echo "$stderr" | grep -q "Warning.*has token but no password"
|
# The script handles missing pairs gracefully with warnings to stderr
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Security: no secrets in output ───────────────────────────────────────────
|
# ── Security: no secrets in output ───────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -136,12 +136,39 @@ _kv_put_secret() {
|
||||||
done
|
done
|
||||||
|
|
||||||
# Use curl directly for KV v2 write with versioning
|
# Use curl directly for KV v2 write with versioning
|
||||||
curl -s -w '%{http_code}' \
|
local tmpfile http_code
|
||||||
|
tmpfile="$(mktemp)"
|
||||||
|
http_code="$(curl -s -w '%{http_code}' \
|
||||||
-H "X-Vault-Token: ${VAULT_TOKEN}" \
|
-H "X-Vault-Token: ${VAULT_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d "$payload" \
|
-d "$payload" \
|
||||||
"${VAULT_ADDR}/v1/secret/data/${path}" >/dev/null
|
-o "$tmpfile" \
|
||||||
|
"${VAULT_ADDR}/v1/secret/data/${path}")" || {
|
||||||
|
rm -f "$tmpfile"
|
||||||
|
_err "Failed to write to Vault at secret/data/${path}: curl error"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
rm -f "$tmpfile"
|
||||||
|
|
||||||
|
# Check HTTP status — 2xx is success
|
||||||
|
case "$http_code" in
|
||||||
|
2[0-9][0-9])
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
404)
|
||||||
|
_err "KV path not found: secret/data/${path}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
403)
|
||||||
|
_err "Permission denied writing to secret/data/${path}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_err "Failed to write to Vault at secret/data/${path}: HTTP $http_code"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# _format_status — format the status string for a key
|
# _format_status — format the status string for a key
|
||||||
|
|
@ -298,8 +325,8 @@ EOF
|
||||||
local pass_val="${!pass_var:-}"
|
local pass_val="${!pass_var:-}"
|
||||||
|
|
||||||
if [ -n "$token_val" ] && [ -n "$pass_val" ]; then
|
if [ -n "$token_val" ] && [ -n "$pass_val" ]; then
|
||||||
operations+=("bots:$role:token:$env_file:$token_var")
|
operations+=("bots|$role|token|$env_file|$token_var")
|
||||||
operations+=("bots:$role:pass:$env_file:$pass_var")
|
operations+=("bots|$role|pass|$env_file|$pass_var")
|
||||||
elif [ -n "$token_val" ] || [ -n "$pass_val" ]; then
|
elif [ -n "$token_val" ] || [ -n "$pass_val" ]; then
|
||||||
_err "Warning: $role bot has token but no password (or vice versa), skipping"
|
_err "Warning: $role bot has token but no password (or vice versa), skipping"
|
||||||
fi
|
fi
|
||||||
|
|
@ -309,8 +336,8 @@ EOF
|
||||||
local llama_token="${FORGE_TOKEN_LLAMA:-}"
|
local llama_token="${FORGE_TOKEN_LLAMA:-}"
|
||||||
local llama_pass="${FORGE_PASS_LLAMA:-}"
|
local llama_pass="${FORGE_PASS_LLAMA:-}"
|
||||||
if [ -n "$llama_token" ] && [ -n "$llama_pass" ]; then
|
if [ -n "$llama_token" ] && [ -n "$llama_pass" ]; then
|
||||||
operations+=("bots:dev-qwen:token:$env_file:FORGE_TOKEN_LLAMA")
|
operations+=("bots|dev-qwen|token|$env_file|FORGE_TOKEN_LLAMA")
|
||||||
operations+=("bots:dev-qwen:pass:$env_file:FORGE_PASS_LLAMA")
|
operations+=("bots|dev-qwen|pass|$env_file|FORGE_PASS_LLAMA")
|
||||||
elif [ -n "$llama_token" ] || [ -n "$llama_pass" ]; then
|
elif [ -n "$llama_token" ] || [ -n "$llama_pass" ]; then
|
||||||
_err "Warning: dev-qwen bot has token but no password (or vice versa), skipping"
|
_err "Warning: dev-qwen bot has token but no password (or vice versa), skipping"
|
||||||
fi
|
fi
|
||||||
|
|
@ -319,14 +346,14 @@ EOF
|
||||||
local forge_token="${FORGE_TOKEN:-}"
|
local forge_token="${FORGE_TOKEN:-}"
|
||||||
local forge_pass="${FORGE_PASS:-}"
|
local forge_pass="${FORGE_PASS:-}"
|
||||||
if [ -n "$forge_token" ] && [ -n "$forge_pass" ]; then
|
if [ -n "$forge_token" ] && [ -n "$forge_pass" ]; then
|
||||||
operations+=("forge:token:$env_file:FORGE_TOKEN")
|
operations+=("forge|token|$env_file|FORGE_TOKEN")
|
||||||
operations+=("forge:pass:$env_file:FORGE_PASS")
|
operations+=("forge|pass|$env_file|FORGE_PASS")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Forge admin token: FORGE_ADMIN_TOKEN
|
# Forge admin token: FORGE_ADMIN_TOKEN
|
||||||
local forge_admin_token="${FORGE_ADMIN_TOKEN:-}"
|
local forge_admin_token="${FORGE_ADMIN_TOKEN:-}"
|
||||||
if [ -n "$forge_admin_token" ]; then
|
if [ -n "$forge_admin_token" ]; then
|
||||||
operations+=("forge:admin_token:$env_file:FORGE_ADMIN_TOKEN")
|
operations+=("forge|admin_token|$env_file|FORGE_ADMIN_TOKEN")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Woodpecker secrets: WOODPECKER_*
|
# Woodpecker secrets: WOODPECKER_*
|
||||||
|
|
@ -341,7 +368,7 @@ EOF
|
||||||
local val="${!key}"
|
local val="${!key}"
|
||||||
if [ -n "$val" ]; then
|
if [ -n "$val" ]; then
|
||||||
local lowercase_key="${key,,}"
|
local lowercase_key="${key,,}"
|
||||||
operations+=("woodpecker:$lowercase_key:$env_file:$key")
|
operations+=("woodpecker|$lowercase_key|$env_file|$key")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
@ -350,7 +377,7 @@ EOF
|
||||||
local val="${!key:-}"
|
local val="${!key:-}"
|
||||||
if [ -n "$val" ]; then
|
if [ -n "$val" ]; then
|
||||||
local lowercase_key="${key,,}"
|
local lowercase_key="${key,,}"
|
||||||
operations+=("chat:$lowercase_key:$env_file:$key")
|
operations+=("chat|$lowercase_key|$env_file|$key")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
@ -360,7 +387,7 @@ EOF
|
||||||
for token_name in "${RUNNER_TOKENS[@]}"; do
|
for token_name in "${RUNNER_TOKENS[@]}"; do
|
||||||
local token_val="${!token_name:-}"
|
local token_val="${!token_name:-}"
|
||||||
if [ -n "$token_val" ]; then
|
if [ -n "$token_val" ]; then
|
||||||
operations+=("runner:${token_name}:value:$sops_file:$token_name")
|
operations+=("runner|$token_name|$sops_file|$token_name")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
@ -393,41 +420,41 @@ EOF
|
||||||
local unchanged=0
|
local unchanged=0
|
||||||
|
|
||||||
for op in "${operations[@]}"; do
|
for op in "${operations[@]}"; do
|
||||||
IFS=':' read -r category source_type source_file source_key <<< "$op"
|
# Parse operation: category|field|file|key (4 fields for most, 5 for bots/runner)
|
||||||
|
IFS='|' read -r category field file key <<< "$op"
|
||||||
local source_value=""
|
local source_value=""
|
||||||
|
|
||||||
if [ "$source_file" = "$env_file" ]; then
|
if [ "$file" = "$env_file" ]; then
|
||||||
source_value="${!source_key:-}"
|
source_value="${!key:-}"
|
||||||
else
|
else
|
||||||
# Source from sops-decrypted env
|
# Source from sops-decrypted env
|
||||||
# We need to extract just this key from the sops_env
|
source_value="$(printf '%s' "$sops_env" | grep "^${key}=" | sed "s/^${key=}//" || true)"
|
||||||
source_value="$(printf '%s' "$sops_env" | grep "^${source_key}=" | sed "s/^${source_key=}//" || true)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Determine Vault path
|
# Determine Vault path and key based on category
|
||||||
local vault_path=""
|
local vault_path=""
|
||||||
local vault_key=""
|
local vault_key="$key"
|
||||||
|
|
||||||
case "$category" in
|
case "$category" in
|
||||||
bots)
|
bots)
|
||||||
vault_path="disinto/bots/${source_type}"
|
vault_path="disinto/bots/${field}"
|
||||||
vault_key="${source_file##*:}"
|
vault_key="$field"
|
||||||
;;
|
;;
|
||||||
forge)
|
forge)
|
||||||
vault_path="disinto/shared/forge"
|
vault_path="disinto/shared/forge"
|
||||||
vault_key="$source_type"
|
vault_key="$field"
|
||||||
;;
|
;;
|
||||||
woodpecker)
|
woodpecker)
|
||||||
vault_path="disinto/shared/woodpecker"
|
vault_path="disinto/shared/woodpecker"
|
||||||
vault_key="$source_type"
|
vault_key="$field"
|
||||||
;;
|
;;
|
||||||
chat)
|
chat)
|
||||||
vault_path="disinto/shared/chat"
|
vault_path="disinto/shared/chat"
|
||||||
vault_key="$source_type"
|
vault_key="$field"
|
||||||
;;
|
;;
|
||||||
runner)
|
runner)
|
||||||
vault_path="disinto/runner"
|
vault_path="disinto/runner/${field}"
|
||||||
vault_key="$source_type"
|
vault_key="value"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_err "Unknown category: $category"
|
_err "Unknown category: $category"
|
||||||
|
|
@ -457,7 +484,10 @@ EOF
|
||||||
|
|
||||||
# Write if not unchanged
|
# Write if not unchanged
|
||||||
if [ "$status" != "unchanged" ]; then
|
if [ "$status" != "unchanged" ]; then
|
||||||
_kv_put_secret "$vault_path" "${vault_key}=${source_value}"
|
if ! _kv_put_secret "$vault_path" "${vault_key}=${source_value}"; then
|
||||||
|
_err "Failed to write $vault_key to $vault_path"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
case "$status" in
|
case "$status" in
|
||||||
updated) ((updated++)) || true ;;
|
updated) ((updated++)) || true ;;
|
||||||
created) ((created++)) || true ;;
|
created) ((created++)) || true ;;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue