Class: Pvectl::Config::Service

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

Overview

Facade for configuration management operations.

Service coordinates Provider, Store, and Wizard to provide a unified interface for loading, modifying, and saving configuration. It handles the complete lifecycle of configuration management.

Examples:

Loading configuration

service = Service.new
service.load(config: "~/.pvectl/config", context: "production")
puts service.current_config.server

Switching contexts

service.load(config: path)
service.use_context("development")

Listing contexts

service.contexts.each do |ctx|
  puts ctx.name
end

Constant Summary collapse

DEFAULT_CONFIG_PATH =

Default path for configuration file

File.expand_path("~/.pvectl/config").freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(provider: nil, store: nil, wizard: nil) ⇒ Service

Creates a new Service instance.

Parameters:

  • provider (Provider, nil) (defaults to: nil)

    configuration provider (default: new Provider)

  • store (Store, nil) (defaults to: nil)

    configuration store (default: new Store)

  • wizard (Wizard, nil) (defaults to: nil)

    configuration wizard (default: nil, created on demand)



43
44
45
46
47
48
# File 'lib/pvectl/config/service.rb', line 43

def initialize(provider: nil, store: nil, wizard: nil)
  @provider = provider || Provider.new
  @store = store || Store.new
  @wizard = wizard
  @loaded = false
end

Instance Attribute Details

#config_pathString (readonly)

Returns path to loaded configuration file.

Returns:

  • (String)

    path to loaded configuration file



30
31
32
# File 'lib/pvectl/config/service.rb', line 30

def config_path
  @config_path
end

#current_context_nameString (readonly)

Returns name of the current context.

Returns:

  • (String)

    name of the current context



36
37
38
# File 'lib/pvectl/config/service.rb', line 36

def current_context_name
  @current_context_name
end

#raw_configHash (readonly)

Returns raw configuration hash from file.

Returns:

  • (Hash)

    raw configuration hash from file



33
34
35
# File 'lib/pvectl/config/service.rb', line 33

def raw_config
  @raw_config
end

Instance Method Details

#cluster(name) ⇒ Models::Cluster?

Returns a specific cluster by name.

Parameters:

  • name (String)

    cluster name

Returns:



164
165
166
# File 'lib/pvectl/config/service.rb', line 164

def cluster(name)
  clusters.find { |c| c.name == name }
end

#clustersArray<Models::Cluster>

Returns all clusters from the configuration.

Returns:



154
155
156
157
158
# File 'lib/pvectl/config/service.rb', line 154

def clusters
  (@raw_config["clusters"] || []).map do |cluster_hash|
    Models::Cluster.from_hash(cluster_hash)
  end
end

#context(name) ⇒ Models::Context?

Returns a specific context by name.

Parameters:

  • name (String)

    context name

Returns:



105
106
107
# File 'lib/pvectl/config/service.rb', line 105

def context(name)
  contexts.find { |ctx| ctx.name == name }
end

#contextsArray<Models::Context>

Returns all contexts from the configuration.

Returns:



95
96
97
98
99
# File 'lib/pvectl/config/service.rb', line 95

def contexts
  (@raw_config["contexts"] || []).map do |ctx_hash|
    Models::Context.from_hash(ctx_hash)
  end
end

#current_configModels::ResolvedConfig

Returns the current resolved configuration.

Returns:

Raises:



83
84
85
86
87
88
89
90
# File 'lib/pvectl/config/service.rb', line 83

def current_config
  raise ConfigError, "Configuration not loaded. Call #load first." unless @loaded

  @resolved_config ||= @provider.resolve(
    config_path: @config_path,
    cli_options: { context: @current_context_name }
  )
end

#load(cli_options = {}) ⇒ Service

Loads configuration from file and CLI options.

Determines the configuration path from CLI options, environment, or default location. Warns if file permissions are insecure.

Parameters:

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

    CLI options (:config, :context)

Returns:

Raises:



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/pvectl/config/service.rb', line 59

def load(cli_options = {})
  @config_path = resolve_config_path(cli_options)

  unless @provider.file_exists?(@config_path)
    raise ConfigNotFoundError, "Configuration file not found: #{@config_path}"
  end

  warn_insecure_permissions if @provider.insecure_permissions?(@config_path)

  @raw_config = @provider.load_file(@config_path)
  @current_context_name = @provider.resolve_context_name(
    cli_options: cli_options,
    file_config: @raw_config
  )
  @resolved_config = nil # Reset cached resolved config
  @loaded = true

  self
end

#masked_configHash

Returns configuration with secrets masked for display.

Returns:

  • (Hash)

    configuration with masked secrets



236
237
238
239
240
241
242
243
244
245
246
# File 'lib/pvectl/config/service.rb', line 236

def masked_config
  config = deep_copy(@raw_config)

  (config["users"] || []).each do |user|
    user_data = user["user"] || {}
    user_data["token-secret"] = "********" if user_data["token-secret"]
    user_data["password"] = "********" if user_data["password"]
  end

  config
end

#savevoid

This method returns an undefined value.

Saves the current configuration to file.



251
252
253
# File 'lib/pvectl/config/service.rb', line 251

def save
  @store.save(@config_path, @raw_config)
end

#set_cluster(name:, server:, verify_ssl: true, certificate_authority: nil) ⇒ Models::Cluster

Creates or updates a cluster.

Parameters:

  • name (String)

    cluster name

  • server (String)

    Proxmox server URL

  • verify_ssl (Boolean) (defaults to: true)

    whether to verify SSL (default: true)

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

    path to CA certificate

Returns:



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/pvectl/config/service.rb', line 175

def set_cluster(name:, server:, verify_ssl: true, certificate_authority: nil)
  new_cluster = Models::Cluster.new(
    name: name,
    server: server,
    verify_ssl: verify_ssl,
    certificate_authority: certificate_authority
  )

  @store.upsert_cluster(@config_path, new_cluster)

  # Update in-memory config
  refresh_raw_config

  new_cluster
end

#set_context(name:, cluster:, user:, default_node: nil) ⇒ Models::Context

Creates or updates a context.

Parameters:

  • name (String)

    context name

  • cluster (String)

    cluster reference

  • user (String)

    user reference

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

    optional default node

Returns:



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/pvectl/config/service.rb', line 135

def set_context(name:, cluster:, user:, default_node: nil)
  new_context = Models::Context.new(
    name: name,
    cluster_ref: cluster,
    user_ref: user,
    default_node: default_node
  )

  @store.upsert_context(@config_path, new_context)

  # Update in-memory config
  refresh_raw_config

  new_context
end

#set_credentials(name:, token_id: nil, token_secret: nil, username: nil, password: nil) ⇒ Models::User

Creates or updates user credentials.

Parameters:

  • name (String)

    user name

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

    API token ID

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

    API token secret

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

    username for password auth

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

    password for password auth

Returns:



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/pvectl/config/service.rb', line 216

def set_credentials(name:, token_id: nil, token_secret: nil, username: nil, password: nil)
  new_user = Models::User.new(
    name: name,
    token_id: token_id,
    token_secret: token_secret,
    username: username,
    password: password
  )

  @store.upsert_user(@config_path, new_user)

  # Update in-memory config
  refresh_raw_config

  new_user
end

#use_context(context_name) ⇒ void

This method returns an undefined value.

Switches to a different context.

Updates both the in-memory state and the configuration file.

Parameters:

  • context_name (String)

    name of context to switch to

Raises:



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/pvectl/config/service.rb', line 116

def use_context(context_name)
  unless context(context_name)
    available = contexts.map(&:name).join(", ")
    raise ContextNotFoundError, "Context '#{context_name}' not found. Available: #{available}"
  end

  @store.update_current_context(@config_path, context_name)
  @current_context_name = context_name
  @resolved_config = nil # Reset cached config
  @raw_config["current-context"] = context_name
end

#user(name) ⇒ Models::User?

Returns a specific user by name.

Parameters:

  • name (String)

    user name

Returns:



204
205
206
# File 'lib/pvectl/config/service.rb', line 204

def user(name)
  users.find { |u| u.name == name }
end

#usersArray<Models::User>

Returns all users from the configuration.

Returns:



194
195
196
197
198
# File 'lib/pvectl/config/service.rb', line 194

def users
  (@raw_config["users"] || []).map do |user_hash|
    Models::User.from_hash(user_hash)
  end
end