Class: Pvectl::Config::Provider

Inherits:
Object
  • Object
show all
Defined in:
lib/pvectl/config/provider.rb

Overview

Loads and resolves configuration from multiple sources.

Provider handles loading configuration from YAML files and environment variables, then merging them with proper priority to produce a final ResolvedConfig. Priority order (highest to lowest):

  1. CLI options

  2. Environment variables

  3. Configuration file

Examples:

Loading configuration

provider = Provider.new
config = provider.resolve(
  config_path: "~/.pvectl/config",
  cli_options: { context: "production" }
)

Constant Summary collapse

ENV_VARS =

Mapping of environment variables to configuration keys

{
  "PROXMOX_HOST" => :server,
  "PROXMOX_TOKEN_ID" => :token_id,
  "PROXMOX_TOKEN_SECRET" => :token_secret,
  "PROXMOX_USER" => :username,
  "PROXMOX_PASSWORD" => :password,
  "PROXMOX_VERIFY_SSL" => :verify_ssl,
  "PVECTL_CONTEXT" => :context,
  "PVECTL_CONFIG" => :config_path,
  # Retry/timeout settings
  "PROXMOX_TIMEOUT" => :timeout,
  "PROXMOX_RETRY_COUNT" => :retry_count,
  "PROXMOX_RETRY_DELAY" => :retry_delay,
  "PROXMOX_MAX_RETRY_DELAY" => :max_retry_delay,
  "PROXMOX_RETRY_WRITES" => :retry_writes
}.freeze
INTEGER_VARS =

Keys that should be parsed as integers

%i[timeout retry_count retry_delay max_retry_delay].freeze
BOOLEAN_VARS =

Keys that should be parsed as booleans

%i[verify_ssl retry_writes].freeze

Instance Method Summary collapse

Instance Method Details

#file_exists?(path) ⇒ Boolean

Checks if a configuration file exists at the given path.

Parameters:

  • path (String)

    path to configuration file

Returns:

  • (Boolean)

    true if file exists



52
53
54
# File 'lib/pvectl/config/provider.rb', line 52

def file_exists?(path)
  File.exist?(path)
end

#insecure_permissions?(path) ⇒ Boolean

Checks if a file has insecure permissions (readable by group/others).

Parameters:

  • path (String)

    path to file

Returns:

  • (Boolean)

    true if permissions are insecure



60
61
62
63
64
65
# File 'lib/pvectl/config/provider.rb', line 60

def insecure_permissions?(path)
  return false unless File.exist?(path)

  mode = File.stat(path).mode & 0o777
  (mode & 0o077) != 0
end

#load_envHash

Loads configuration from environment variables.

Returns:

  • (Hash)

    configuration values from environment

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/pvectl/config/provider.rb', line 86

def load_env
  result = {}

  ENV_VARS.each do |env_var, config_key|
    value = ENV[env_var]
    next if value.nil? || value.empty?

    result[config_key] = parse_env_value(config_key, value)
  end

  result
end

#load_file(path) ⇒ Hash

Loads configuration from a YAML file.

Parameters:

  • path (String)

    path to configuration file

Returns:

  • (Hash)

    parsed configuration hash

Raises:



73
74
75
76
77
78
79
80
# File 'lib/pvectl/config/provider.rb', line 73

def load_file(path)
  raise ConfigNotFoundError, "Configuration file not found: #{path}" unless File.exist?(path)

  content = File.read(path)
  YAML.safe_load(content, permitted_classes: [Symbol])
rescue Psych::SyntaxError => e
  raise InvalidConfigError, "Invalid YAML in #{path}: #{e.message}"
end

#resolve(config_path:, cli_options:, cluster_override: nil) ⇒ Models::ResolvedConfig

Resolves full configuration by merging all sources.

Parameters:

  • config_path (String)

    path to configuration file

  • cli_options (Hash)

    CLI options

  • cluster_override (String, nil) (defaults to: nil)

    for testing invalid cluster references

Returns:

Raises:



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/pvectl/config/provider.rb', line 120

def resolve(config_path:, cli_options:, cluster_override: nil)
  file_config = load_file(config_path)
  env_config = load_env

  context_name = resolve_context_name(cli_options: cli_options, file_config: file_config)
  context = find_context(file_config, context_name)

  cluster_name = cluster_override || context.cluster_ref
  cluster = find_cluster(file_config, cluster_name)
  user = find_user(file_config, context.user_ref)

  build_resolved_config(
    context: context,
    cluster: cluster,
    user: user,
    env_config: env_config,
    cli_options: cli_options
  )
end

#resolve_context_name(cli_options:, file_config: {}) ⇒ String?

Resolves the context name from various sources.

Priority: CLI > ENV > file

Parameters:

  • cli_options (Hash)

    CLI options (may contain :context)

  • file_config (Hash) (defaults to: {})

    configuration from file (may contain “current-context”)

Returns:

  • (String, nil)

    resolved context name



106
107
108
# File 'lib/pvectl/config/provider.rb', line 106

def resolve_context_name(cli_options:, file_config: {})
  cli_options[:context] || ENV["PVECTL_CONTEXT"] || file_config["current-context"]
end