mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-22 11:24:18 +00:00
refactor: extract ensure_multi_credentials to reduce duplication across 5 providers (#468)
Add a generic ensure_multi_credentials() helper to shared/common.sh that handles the env-var/config-file/prompt/test/save flow for providers needing multiple credentials. This eliminates ~270 lines of duplicated logic across contabo, netcup, ramnode, ionos, and upcloud, replacing it with single function calls. Each provider's ensure_*_credentials() function is now 3-8 lines instead of 30-65 lines. Agent: complexity-hunter Co-authored-by: A <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e38a75ea17
commit
79e3b887c9
6 changed files with 144 additions and 273 deletions
|
|
@ -92,71 +92,14 @@ test_contabo_credentials() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# Prompt for a single Contabo credential if not already set
|
||||
# Usage: _prompt_contabo_cred VAR_NAME "prompt text"
|
||||
_prompt_contabo_cred() {
|
||||
local var_name="$1"
|
||||
local prompt_text="$2"
|
||||
|
||||
if [[ -n "${!var_name:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local value
|
||||
value=$(safe_read "$prompt_text") || return 1
|
||||
export "${var_name}=${value}"
|
||||
}
|
||||
|
||||
# Ensure Contabo credentials are available
|
||||
ensure_contabo_credentials() {
|
||||
check_python_available || return 1
|
||||
|
||||
local config_file="$HOME/.config/spawn/contabo.json"
|
||||
|
||||
# Try environment variables first (all 4 must be set)
|
||||
if [[ -n "${CONTABO_CLIENT_ID:-}" ]] && [[ -n "${CONTABO_CLIENT_SECRET:-}" ]] && \
|
||||
[[ -n "${CONTABO_API_USER:-}" ]] && [[ -n "${CONTABO_API_PASSWORD:-}" ]]; then
|
||||
log_info "Using Contabo credentials from environment"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try config file
|
||||
local creds
|
||||
if creds=$(_load_json_config_fields "$config_file" client_id client_secret api_user api_password); then
|
||||
local saved_client_id saved_secret saved_user saved_password
|
||||
{ read -r saved_client_id; read -r saved_secret; read -r saved_user; read -r saved_password; } <<< "${creds}"
|
||||
if [[ -n "$saved_client_id" ]] && [[ -n "$saved_secret" ]] && [[ -n "$saved_user" ]] && [[ -n "$saved_password" ]]; then
|
||||
export CONTABO_CLIENT_ID="$saved_client_id"
|
||||
export CONTABO_CLIENT_SECRET="$saved_secret"
|
||||
export CONTABO_API_USER="$saved_user"
|
||||
export CONTABO_API_PASSWORD="$saved_password"
|
||||
log_info "Using Contabo credentials from $config_file"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Prompt for missing credentials
|
||||
echo ""
|
||||
log_warn "Contabo API Credentials Required"
|
||||
log_warn "Get your credentials from: https://my.contabo.com/api/details"
|
||||
echo ""
|
||||
|
||||
_prompt_contabo_cred "CONTABO_CLIENT_ID" "Enter Contabo Client ID: " || return 1
|
||||
_prompt_contabo_cred "CONTABO_CLIENT_SECRET" "Enter Contabo Client Secret: " || return 1
|
||||
_prompt_contabo_cred "CONTABO_API_USER" "Enter Contabo API User (username/email): " || return 1
|
||||
_prompt_contabo_cred "CONTABO_API_PASSWORD" "Enter Contabo API Password: " || return 1
|
||||
|
||||
# Test credentials
|
||||
log_info "Testing Contabo credentials..."
|
||||
if ! test_contabo_credentials; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_save_json_config "$config_file" \
|
||||
client_id "$CONTABO_CLIENT_ID" \
|
||||
client_secret "$CONTABO_CLIENT_SECRET" \
|
||||
api_user "$CONTABO_API_USER" \
|
||||
api_password "$CONTABO_API_PASSWORD"
|
||||
ensure_multi_credentials "Contabo" "$HOME/.config/spawn/contabo.json" \
|
||||
"https://my.contabo.com/api/details" test_contabo_credentials \
|
||||
"CONTABO_CLIENT_ID:client_id:Client ID" \
|
||||
"CONTABO_CLIENT_SECRET:client_secret:Client Secret" \
|
||||
"CONTABO_API_USER:api_user:API User (email)" \
|
||||
"CONTABO_API_PASSWORD:api_password:API Password"
|
||||
}
|
||||
|
||||
# Check if SSH key is registered with Contabo
|
||||
|
|
|
|||
|
|
@ -72,60 +72,12 @@ test_ionos_credentials() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# Try loading IONOS credentials from config file
|
||||
# Returns 0 if loaded, 1 otherwise
|
||||
_ionos_load_config_credentials() {
|
||||
local config_file="$1"
|
||||
local creds
|
||||
creds=$(_load_json_config_fields "$config_file" username password) || return 1
|
||||
|
||||
local saved_user saved_pass
|
||||
{ read -r saved_user; read -r saved_pass; } <<< "${creds}"
|
||||
if [[ -z "$saved_user" ]] || [[ -z "$saved_pass" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Loading IONOS credentials from $config_file"
|
||||
export IONOS_USERNAME="$saved_user" IONOS_PASSWORD="$saved_pass"
|
||||
}
|
||||
|
||||
# Prompt user for IONOS credentials interactively
|
||||
# Returns 0 on success (exports credentials), 1 on failure
|
||||
_ionos_prompt_credentials() {
|
||||
log_warn "IONOS Cloud credentials not found"
|
||||
echo ""
|
||||
log_info "Get credentials at: https://dcd.ionos.com/ → Management → Users & Keys"
|
||||
echo ""
|
||||
|
||||
IONOS_USERNAME=$(safe_read "Enter IONOS username (email): ") || return 1
|
||||
IONOS_PASSWORD=$(safe_read "Enter IONOS password/API key: ") || return 1
|
||||
export IONOS_USERNAME IONOS_PASSWORD
|
||||
}
|
||||
|
||||
# Ensure IONOS credentials are available (env vars → config file → prompt+save)
|
||||
# Ensure IONOS credentials are available (env vars -> config file -> prompt+save)
|
||||
ensure_ionos_credentials() {
|
||||
local config_file="$HOME/.config/spawn/ionos.json"
|
||||
|
||||
# Try env vars, then config file, then prompt
|
||||
if [[ -z "${IONOS_USERNAME:-}" ]] || [[ -z "${IONOS_PASSWORD:-}" ]]; then
|
||||
_ionos_load_config_credentials "$config_file" || true
|
||||
fi
|
||||
|
||||
local need_save=false
|
||||
if [[ -z "${IONOS_USERNAME:-}" ]] || [[ -z "${IONOS_PASSWORD:-}" ]]; then
|
||||
_ionos_prompt_credentials || return 1
|
||||
need_save=true
|
||||
fi
|
||||
|
||||
log_info "Testing IONOS credentials..."
|
||||
if ! test_ionos_credentials; then
|
||||
log_error "Invalid IONOS credentials. Please check IONOS_USERNAME and IONOS_PASSWORD."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$need_save" == "true" ]]; then
|
||||
_save_json_config "$config_file" username "$IONOS_USERNAME" password "$IONOS_PASSWORD"
|
||||
fi
|
||||
ensure_multi_credentials "IONOS" "$HOME/.config/spawn/ionos.json" \
|
||||
"https://dcd.ionos.com/ -> Management -> Users & Keys" test_ionos_credentials \
|
||||
"IONOS_USERNAME:username:Username (email)" \
|
||||
"IONOS_PASSWORD:password:Password/API Key"
|
||||
}
|
||||
|
||||
# Check if SSH key is registered with IONOS
|
||||
|
|
|
|||
|
|
@ -124,51 +124,11 @@ test_netcup_credentials() {
|
|||
|
||||
# Ensure Netcup credentials are available
|
||||
ensure_netcup_credentials() {
|
||||
local config_file="$HOME/.config/spawn/netcup.json"
|
||||
|
||||
# Try loading from env vars first
|
||||
if [[ -n "${NETCUP_CUSTOMER_NUMBER:-}" && -n "${NETCUP_API_KEY:-}" && -n "${NETCUP_API_PASSWORD:-}" ]]; then
|
||||
if test_netcup_credentials; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try loading from config file (single python3 call instead of 3)
|
||||
local creds
|
||||
if creds=$(_load_json_config_fields "$config_file" customer_number api_key api_password); then
|
||||
local saved_num saved_key saved_pass
|
||||
{ read -r saved_num; read -r saved_key; read -r saved_pass; } <<< "${creds}"
|
||||
if [[ -n "$saved_num" ]] && [[ -n "$saved_key" ]] && [[ -n "$saved_pass" ]]; then
|
||||
log_info "Loading Netcup credentials from $config_file"
|
||||
export NETCUP_CUSTOMER_NUMBER="$saved_num" NETCUP_API_KEY="$saved_key" NETCUP_API_PASSWORD="$saved_pass"
|
||||
if test_netcup_credentials; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Prompt for credentials
|
||||
log_info "Netcup credentials not found"
|
||||
log_info "Get your API credentials at: https://ccp.netcup.net/ → Settings → API"
|
||||
log_info ""
|
||||
|
||||
NETCUP_CUSTOMER_NUMBER=$(safe_read "Enter Netcup customer number: ") || return 1
|
||||
NETCUP_API_KEY=$(safe_read "Enter Netcup API key: ") || return 1
|
||||
NETCUP_API_PASSWORD=$(safe_read "Enter Netcup API password: ") || return 1
|
||||
export NETCUP_CUSTOMER_NUMBER NETCUP_API_KEY NETCUP_API_PASSWORD
|
||||
|
||||
# Test credentials
|
||||
if ! test_netcup_credentials; then
|
||||
log_error "Invalid Netcup credentials"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_save_json_config "$config_file" \
|
||||
customer_number "$NETCUP_CUSTOMER_NUMBER" \
|
||||
api_key "$NETCUP_API_KEY" \
|
||||
api_password "$NETCUP_API_PASSWORD"
|
||||
|
||||
return 0
|
||||
ensure_multi_credentials "Netcup" "$HOME/.config/spawn/netcup.json" \
|
||||
"https://ccp.netcup.net/ -> Settings -> API" test_netcup_credentials \
|
||||
"NETCUP_CUSTOMER_NUMBER:customer_number:Customer Number" \
|
||||
"NETCUP_API_KEY:api_key:API Key" \
|
||||
"NETCUP_API_PASSWORD:api_password:API Password"
|
||||
}
|
||||
|
||||
# Check if SSH key is registered with Netcup
|
||||
|
|
|
|||
|
|
@ -121,51 +121,11 @@ test_ramnode_credentials() {
|
|||
|
||||
# Ensure RamNode credentials are available
|
||||
ensure_ramnode_credentials() {
|
||||
# Check for required environment variables
|
||||
if [[ -n "${RAMNODE_USERNAME:-}" && -n "${RAMNODE_PASSWORD:-}" && -n "${RAMNODE_PROJECT_ID:-}" ]]; then
|
||||
if test_ramnode_credentials; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try to load from config file (single python3 call instead of 3)
|
||||
local config_file="$HOME/.config/spawn/ramnode.json"
|
||||
local creds
|
||||
if creds=$(_load_json_config_fields "$config_file" username password project_id); then
|
||||
local saved_user saved_pass saved_pid
|
||||
{ read -r saved_user; read -r saved_pass; read -r saved_pid; } <<< "${creds}"
|
||||
if [[ -n "$saved_user" ]] && [[ -n "$saved_pass" ]] && [[ -n "$saved_pid" ]]; then
|
||||
log_info "Loading RamNode credentials from $config_file..."
|
||||
export RAMNODE_USERNAME="$saved_user" RAMNODE_PASSWORD="$saved_pass" RAMNODE_PROJECT_ID="$saved_pid"
|
||||
if test_ramnode_credentials; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Prompt for credentials
|
||||
log_warn "RamNode OpenStack credentials not found"
|
||||
log_info ""
|
||||
log_info "Get credentials from: https://manage.ramnode.com/ → Cloud → API Users"
|
||||
log_info ""
|
||||
|
||||
RAMNODE_USERNAME=$(safe_read "Enter RamNode username: ") || return 1
|
||||
RAMNODE_PASSWORD=$(safe_read "Enter RamNode password: ") || return 1
|
||||
RAMNODE_PROJECT_ID=$(safe_read "Enter RamNode project ID: ") || return 1
|
||||
|
||||
export RAMNODE_USERNAME RAMNODE_PASSWORD RAMNODE_PROJECT_ID
|
||||
|
||||
if ! test_ramnode_credentials; then
|
||||
log_error "Invalid credentials"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_save_json_config "$config_file" \
|
||||
username "$RAMNODE_USERNAME" \
|
||||
password "$RAMNODE_PASSWORD" \
|
||||
project_id "$RAMNODE_PROJECT_ID"
|
||||
|
||||
return 0
|
||||
ensure_multi_credentials "RamNode" "$HOME/.config/spawn/ramnode.json" \
|
||||
"https://manage.ramnode.com/ -> Cloud -> API Users" test_ramnode_credentials \
|
||||
"RAMNODE_USERNAME:username:Username" \
|
||||
"RAMNODE_PASSWORD:password:Password" \
|
||||
"RAMNODE_PROJECT_ID:project_id:Project ID"
|
||||
}
|
||||
|
||||
# Check if SSH key is registered with RamNode
|
||||
|
|
|
|||
119
shared/common.sh
119
shared/common.sh
|
|
@ -1694,6 +1694,125 @@ _save_json_config() {
|
|||
log_info "Credentials saved to ${config_file}"
|
||||
}
|
||||
|
||||
# Generic multi-credential ensure function
|
||||
# Eliminates duplicated env-var/config/prompt/test/save logic across providers
|
||||
# that need more than one credential (username+password, client_id+secret, etc.)
|
||||
#
|
||||
# Usage: ensure_multi_credentials PROVIDER_NAME CONFIG_FILE HELP_URL TEST_FUNC \
|
||||
# "ENV_VAR:config_key:Prompt Label" ...
|
||||
#
|
||||
# Arguments:
|
||||
# PROVIDER_NAME - Display name for logging (e.g., "Contabo")
|
||||
# CONFIG_FILE - Path to JSON config file (e.g., "$HOME/.config/spawn/contabo.json")
|
||||
# HELP_URL - URL where users can find their credentials
|
||||
# TEST_FUNC - Function to validate credentials (returns 0=ok, 1=fail); empty to skip
|
||||
# ... - One or more credential specs as "ENV_VAR:config_key:Prompt Label"
|
||||
#
|
||||
# Each credential spec is a colon-delimited triple:
|
||||
# ENV_VAR - Environment variable name (e.g., CONTABO_CLIENT_ID)
|
||||
# config_key - JSON key in the config file (e.g., client_id)
|
||||
# Prompt Label - Human-readable label for prompting (e.g., "Client ID")
|
||||
#
|
||||
# Example:
|
||||
# ensure_multi_credentials "Contabo" "$HOME/.config/spawn/contabo.json" \
|
||||
# "https://my.contabo.com/api/details" test_contabo_credentials \
|
||||
# "CONTABO_CLIENT_ID:client_id:Client ID" \
|
||||
# "CONTABO_CLIENT_SECRET:client_secret:Client Secret" \
|
||||
# "CONTABO_API_USER:api_user:API User (email)" \
|
||||
# "CONTABO_API_PASSWORD:api_password:API Password"
|
||||
ensure_multi_credentials() {
|
||||
local provider_name="${1}"
|
||||
local config_file="${2}"
|
||||
local help_url="${3}"
|
||||
local test_func="${4:-}"
|
||||
shift 4
|
||||
|
||||
check_python_available || return 1
|
||||
|
||||
# Collect credential specs
|
||||
local specs=("$@")
|
||||
local env_vars=() config_keys=() labels=()
|
||||
local spec
|
||||
for spec in "${specs[@]}"; do
|
||||
env_vars+=("${spec%%:*}")
|
||||
local rest="${spec#*:}"
|
||||
config_keys+=("${rest%%:*}")
|
||||
labels+=("${rest#*:}")
|
||||
done
|
||||
|
||||
# 1. Check if ALL env vars are already set
|
||||
local all_set=true
|
||||
local var
|
||||
for var in "${env_vars[@]}"; do
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
all_set=false
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "${all_set}" == "true" ]]; then
|
||||
log_info "Using ${provider_name} credentials from environment"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 2. Try loading from config file
|
||||
local creds
|
||||
if creds=$(_load_json_config_fields "${config_file}" "${config_keys[@]}"); then
|
||||
local all_loaded=true
|
||||
local i=0
|
||||
while IFS= read -r value; do
|
||||
if [[ -z "${value}" ]]; then
|
||||
all_loaded=false
|
||||
break
|
||||
fi
|
||||
export "${env_vars[$i]}=${value}"
|
||||
i=$((i + 1))
|
||||
done <<< "${creds}"
|
||||
|
||||
if [[ "${all_loaded}" == "true" && "${i}" -eq "${#env_vars[@]}" ]]; then
|
||||
log_info "Using ${provider_name} credentials from ${config_file}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. Prompt for each credential
|
||||
echo ""
|
||||
log_step "${provider_name} API Credentials Required"
|
||||
log_step "Get your credentials from: ${help_url}"
|
||||
echo ""
|
||||
|
||||
local idx=0
|
||||
for idx in $(seq 0 $((${#env_vars[@]} - 1))); do
|
||||
local val
|
||||
val=$(safe_read "Enter ${provider_name} ${labels[$idx]}: ") || return 1
|
||||
if [[ -z "${val}" ]]; then
|
||||
log_error "${labels[$idx]} is required"
|
||||
return 1
|
||||
fi
|
||||
export "${env_vars[$idx]}=${val}"
|
||||
done
|
||||
|
||||
# 4. Validate credentials
|
||||
if [[ -n "${test_func}" ]]; then
|
||||
log_info "Testing ${provider_name} credentials..."
|
||||
if ! "${test_func}"; then
|
||||
log_error "Invalid ${provider_name} credentials"
|
||||
local v
|
||||
for v in "${env_vars[@]}"; do
|
||||
unset "${v}"
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 5. Save to config file
|
||||
local save_args=()
|
||||
for idx in $(seq 0 $((${#env_vars[@]} - 1))); do
|
||||
save_args+=("${config_keys[$idx]}" "${!env_vars[$idx]}")
|
||||
done
|
||||
_save_json_config "${config_file}" "${save_args[@]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Configuration file helpers
|
||||
# ============================================================
|
||||
|
|
|
|||
|
|
@ -53,75 +53,12 @@ test_upcloud_credentials() {
|
|||
|
||||
# Try loading UpCloud credentials from config file
|
||||
# Returns 0 if loaded, 1 otherwise
|
||||
_upcloud_load_config_credentials() {
|
||||
local config_file="$1"
|
||||
local creds
|
||||
creds=$(_load_json_config_fields "$config_file" username password) || return 1
|
||||
|
||||
local saved_username saved_password
|
||||
{ read -r saved_username; read -r saved_password; } <<< "${creds}"
|
||||
if [[ -z "${saved_username}" ]] || [[ -z "${saved_password}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
export UPCLOUD_USERNAME="${saved_username}"
|
||||
export UPCLOUD_PASSWORD="${saved_password}"
|
||||
log_info "Using UpCloud credentials from ${config_file}"
|
||||
}
|
||||
|
||||
# Prompt user for UpCloud credentials interactively
|
||||
# Returns 0 on success (exports credentials), 1 on failure
|
||||
_upcloud_prompt_credentials() {
|
||||
echo ""
|
||||
log_warn "UpCloud API Credentials Required"
|
||||
log_warn "Create API credentials at: https://hub.upcloud.com/people/account"
|
||||
echo ""
|
||||
|
||||
local username
|
||||
username=$(safe_read "Enter your UpCloud API username: ") || return 1
|
||||
if [[ -z "${username}" ]]; then
|
||||
log_error "Username is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local password
|
||||
password=$(safe_read "Enter your UpCloud API password: ") || return 1
|
||||
if [[ -z "${password}" ]]; then
|
||||
log_error "Password is required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
export UPCLOUD_USERNAME="${username}"
|
||||
export UPCLOUD_PASSWORD="${password}"
|
||||
}
|
||||
|
||||
# Ensure UpCloud credentials are available (env var -> config file -> prompt+save)
|
||||
ensure_upcloud_credentials() {
|
||||
check_python_available || return 1
|
||||
|
||||
local config_file="$HOME/.config/spawn/upcloud.json"
|
||||
|
||||
# 1. Check environment variables
|
||||
if [[ -n "${UPCLOUD_USERNAME:-}" ]] && [[ -n "${UPCLOUD_PASSWORD:-}" ]]; then
|
||||
log_info "Using UpCloud credentials from environment"
|
||||
test_upcloud_credentials
|
||||
return $?
|
||||
fi
|
||||
|
||||
# 2. Check config file
|
||||
if _upcloud_load_config_credentials "$config_file"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 3. Prompt and save
|
||||
_upcloud_prompt_credentials || return 1
|
||||
|
||||
if ! test_upcloud_credentials; then
|
||||
unset UPCLOUD_USERNAME UPCLOUD_PASSWORD
|
||||
return 1
|
||||
fi
|
||||
|
||||
_save_json_config "$config_file" username "$UPCLOUD_USERNAME" password "$UPCLOUD_PASSWORD"
|
||||
ensure_multi_credentials "UpCloud" "$HOME/.config/spawn/upcloud.json" \
|
||||
"https://hub.upcloud.com/people/account" test_upcloud_credentials \
|
||||
"UPCLOUD_USERNAME:username:API Username" \
|
||||
"UPCLOUD_PASSWORD:password:API Password"
|
||||
}
|
||||
|
||||
# Get server name from env var or prompt
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue