diff --git a/shared/key-request.sh b/shared/key-request.sh index a4c2bcc6..2fc05cb3 100644 --- a/shared/key-request.sh +++ b/shared/key-request.sh @@ -6,6 +6,9 @@ # # Functions: # load_cloud_keys_from_config — Load keys from ~/.config/spawn/{cloud}.json into env +# _parse_cloud_auths — Extract cloud auth specs from manifest.json +# _try_load_env_var — Load a single env var from config file +# _load_cloud_credentials — Load all env vars for one cloud provider # request_missing_cloud_keys — POST to key server for missing providers (fire-and-forget) # invalidate_cloud_key — Delete a cloud's config file # get_cloud_env_vars — Get env var names for a cloud from manifest @@ -33,6 +36,76 @@ for var in re.split(r'\s*\+\s*', auth): " "${REPO_ROOT}/manifest.json" "${cloud}" 2>/dev/null } +# Parse manifest.json to extract cloud_key|auth_string lines for API-token clouds. +# Skips CLI-based auth (sprite login, aws configure, etc.) and empty auth fields. +# Outputs one "cloud_key|auth_string" per line to stdout. +_parse_cloud_auths() { + local manifest_path="${1}" + python3 -c " +import json, re, sys +manifest = json.load(open(sys.argv[1])) +for key, cloud in manifest.get('clouds', {}).items(): + auth = cloud.get('auth', '') + if re.search(r'\b(login|configure|setup)\b', auth, re.I): + continue + if not auth.strip(): + continue + print(key + '|' + auth) +" "${manifest_path}" 2>/dev/null +} + +# Try to load a single env var from config file if not already set in environment. +# Returns 0 if the var is available (already set or loaded from config), 1 if missing. +_try_load_env_var() { + local var_name="${1}" + local config_file="${2}" + + # Already set in environment? + local current_val="${!var_name:-}" + if [[ -n "${current_val}" ]]; then + return 0 + fi + + # Try loading from config file + if [[ -f "${config_file}" ]]; then + local val + val=$(python3 -c " +import json, sys +data = json.load(open(sys.argv[1])) +v = data.get(sys.argv[2], '') or data.get('api_key', '') or data.get('token', '') +print(v) +" "${config_file}" "${var_name}" 2>/dev/null) + if [[ -n "${val}" ]]; then + export "${var_name}=${val}" + return 0 + fi + fi + + return 1 +} + +# Load all env vars for a single cloud provider. +# Returns 0 if all vars are available, 1 if any are missing. +_load_cloud_credentials() { + local cloud_key="${1}" + local auth_string="${2}" + + local env_vars + env_vars=$(printf '%s' "${auth_string}" | tr '+' '\n' | sed 's/^ *//;s/ *$//') + + local config_file="${HOME}/.config/spawn/${cloud_key}.json" + local all_loaded=true + + while IFS= read -r var_name; do + [[ -z "${var_name}" ]] && continue + if ! _try_load_env_var "${var_name}" "${config_file}"; then + all_loaded=false + fi + done <<< "${env_vars}" + + [[ "${all_loaded}" == "true" ]] +} + # Load cloud API keys from ~/.config/spawn/{cloud}.json into environment # Reads manifest.json to determine which clouds need API-token auth # Skips CLI-based auth (sprite login, aws configure, etc.) @@ -49,64 +122,18 @@ load_cloud_keys_from_config() { return 1 fi + local cloud_auths + cloud_auths=$(_parse_cloud_auths "${manifest_path}") || return 1 + local total=0 local loaded=0 local missing_providers="" - # Parse manifest.json → cloud_key|auth_string for each API-token cloud - local cloud_auths - cloud_auths=$(python3 -c " -import json, re, sys -manifest = json.load(open(sys.argv[1])) -for key, cloud in manifest.get('clouds', {}).items(): - auth = cloud.get('auth', '') - if re.search(r'\b(login|configure|setup)\b', auth, re.I): - continue - if not auth.strip(): - continue - print(key + '|' + auth) -" "${manifest_path}" 2>/dev/null) || return 1 - while IFS='|' read -r cloud_key auth_string; do [[ -z "${cloud_key}" ]] && continue total=$((total + 1)) - # Parse env var names from auth string (split on " + ") - local env_vars - env_vars=$(printf '%s' "${auth_string}" | tr '+' '\n' | sed 's/^ *//;s/ *$//') - - local cloud_complete=true - local config_file="${HOME}/.config/spawn/${cloud_key}.json" - - while IFS= read -r var_name; do - [[ -z "${var_name}" ]] && continue - - # Already set in environment? Skip - local current_val="${!var_name:-}" - if [[ -n "${current_val}" ]]; then - continue - fi - - # Try loading from config file - if [[ -f "${config_file}" ]]; then - local val - val=$(python3 -c " -import json, sys -data = json.load(open(sys.argv[1])) -v = data.get(sys.argv[2], '') or data.get('api_key', '') or data.get('token', '') -print(v) -" "${config_file}" "${var_name}" 2>/dev/null) - if [[ -n "${val}" ]]; then - export "${var_name}=${val}" - continue - fi - fi - - # This env var is missing - cloud_complete=false - done <<< "${env_vars}" - - if [[ "${cloud_complete}" == "true" ]]; then + if _load_cloud_credentials "${cloud_key}" "${auth_string}"; then loaded=$((loaded + 1)) else missing_providers="${missing_providers} ${cloud_key}"