Module: Pvectl::Commands::IrreversibleCommand

Included in:
DeleteCommand, TemplateCommand
Defined in:
lib/pvectl/commands/irreversible_command.rb,
sig/pvectl/commands/irreversible_command.rbs

Overview

Shared functionality for irreversible batch commands (delete, template).

Provides the generic flow: resolve resources → confirm → execute → format results. Submodules override hooks to customize behavior per operation.

Examples:

Including in an operation-specific module

module TemplateCommand
  include IrreversibleCommand

  def self.included(base)
    base.extend(IrreversibleCommand::ClassMethods)
  end
end

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

Hook called when module is included.

Parameters:

  • base (Module)

    the module or class including this module



37
38
39
# File 'lib/pvectl/commands/irreversible_command.rb', line 37

def self.included(base)
  base.extend(ClassMethods)
end

Instance Method Details

#apply_selectors(resources) ⇒ Array

Applies selectors to resource collection.

Parameters:

  • resources (Array)

    Resources to filter

Returns:

  • (Array)

    Filtered resources



160
161
162
163
164
165
166
# File 'lib/pvectl/commands/irreversible_command.rb', line 160

def apply_selectors(resources)
  return resources if selector_strings.empty?

  selector_class = resource_type_symbol == :vm ? Selectors::Vm : Selectors::Container
  selector = selector_class.parse_all(selector_strings)
  selector.apply(resources)
end

#build_presenterPresenters::Base

Builds the presenter for results output. Must be implemented by the including module.

Returns:

Raises:

  • (NotImplementedError)


191
192
193
# File 'lib/pvectl/commands/irreversible_command.rb', line 191

def build_presenter
  raise NotImplementedError, "#{self.class} must implement #build_presenter"
end

#build_repository(connection) ⇒ Repositories::Base

Builds the appropriate repository for the resource type.

Parameters:

Returns:



141
142
143
144
145
146
147
# File 'lib/pvectl/commands/irreversible_command.rb', line 141

def build_repository(connection)
  if resource_type_symbol == :vm
    Pvectl::Repositories::Vm.new(connection)
  else
    Pvectl::Repositories::Container.new(connection)
  end
end

#confirm_operation(resources) ⇒ Boolean

Confirms the operation with the user. Override in submodule for custom confirmation logic.

Parameters:

  • resources (Array)

    Resources to operate on

Returns:

  • (Boolean)

    true if operation should proceed

Raises:

  • (NotImplementedError)


173
174
175
# File 'lib/pvectl/commands/irreversible_command.rb', line 173

def confirm_operation(resources)
  raise NotImplementedError, "#{self.class} must implement #confirm_operation"
end

#determine_exit_code(results) ⇒ Integer

Determines exit code based on results.

Parameters:

Returns:

  • (Integer)

    exit code



234
235
236
237
238
239
# File 'lib/pvectl/commands/irreversible_command.rb', line 234

def determine_exit_code(results)
  return ExitCodes::SUCCESS if results.all?(&:successful?)
  return ExitCodes::SUCCESS if results.all?(&:pending?)

  ExitCodes::GENERAL_ERROR
end

#executeInteger

Executes the command.

Returns:

  • (Integer)

    exit code



57
58
59
60
61
62
63
64
65
66
# File 'lib/pvectl/commands/irreversible_command.rb', line 57

def execute
  return usage_error("Resource type required (#{supported_types.join(', ')})") unless @resource_type
  return usage_error("Unsupported resource: #{@resource_type}") unless supported_resource?

  if @resource_ids.empty? && !@options[:all] && selector_strings.empty?
    return usage_error("VMID, --all, or -l selector required")
  end

  perform_operation
end

#initialize(resource_type, resource_ids, options, global_options) ⇒ IrreversibleCommand

Initializes an irreversible command.

Parameters:

  • resource_type (String, nil)

    resource type (vm, container)

  • resource_ids (Array<String>, String, nil)

    resource identifiers

  • options (Hash)

    command options

  • global_options (Hash)

    global CLI options

Returns:



47
48
49
50
51
52
# File 'lib/pvectl/commands/irreversible_command.rb', line 47

def initialize(resource_type, resource_ids, options, global_options)
  @resource_type = resource_type
  @resource_ids = Array(resource_ids).compact
  @options = options
  @global_options = global_options
end

#load_configvoid

This method returns an undefined value.

Loads configuration from file or environment.



198
199
200
201
202
# File 'lib/pvectl/commands/irreversible_command.rb', line 198

def load_config
  service = Pvectl::Config::Service.new
  service.load(config: @global_options[:config])
  @config = service.current_config
end

#no_resources_foundInteger

Outputs no resources found error and returns exit code.

Returns:

  • (Integer)

    exit code



253
254
255
256
257
258
259
260
261
262
# File 'lib/pvectl/commands/irreversible_command.rb', line 253

def no_resources_found
  type_plural = resource_type_symbol == :vm ? "VMs" : "containers"
  msg = if @options[:all] || selector_strings.any?
          @options[:node] ? "No #{type_plural} found on node #{@options[:node]}" : "No #{type_plural} found matching criteria"
        else
          "No #{type_plural} found for given IDs"
        end
  $stderr.puts "Error: #{msg}"
  ExitCodes::NOT_FOUND
end

#output_results(results) ⇒ void

This method returns an undefined value.

Outputs operation results using the configured formatter.

Parameters:



220
221
222
223
224
225
226
227
228
# File 'lib/pvectl/commands/irreversible_command.rb', line 220

def output_results(results)
  presenter = build_presenter
  format = @global_options[:output] || "table"
  color_flag = @global_options[:color]

  formatter = Pvectl::Formatters::Registry.for(format)
  output = formatter.format(results, presenter, color: color_flag)
  puts output
end

#perform_operationInteger

Performs the operation. Override in submodule for custom flow.

Returns:

  • (Integer)

    exit code



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/pvectl/commands/irreversible_command.rb', line 94

def perform_operation
  load_config
  connection = Pvectl::Connection.new(@config)

  resources = resolve_resources(connection)
  return no_resources_found if resources.empty?
  return ExitCodes::SUCCESS unless confirm_operation(resources)

  results = perform_service_call(resources, connection)
  output_results(results)
  determine_exit_code(results)
rescue Pvectl::Config::ConfigNotFoundError,
       Pvectl::Config::InvalidConfigError,
       Pvectl::Config::ContextNotFoundError,
       Pvectl::Config::ClusterNotFoundError,
       Pvectl::Config::UserNotFoundError
  raise
rescue StandardError => e
  $stderr.puts "Error: #{e.message}"
  ExitCodes::GENERAL_ERROR
end

#perform_service_call(resources, connection) ⇒ Array<Models::OperationResult>

Performs the actual service call for the operation. Must be implemented by the including module.

Parameters:

  • resources (Array)

    resources to operate on

  • connection (Connection)

    API connection

Returns:

Raises:

  • (NotImplementedError)


183
184
185
# File 'lib/pvectl/commands/irreversible_command.rb', line 183

def perform_service_call(resources, connection)
  raise NotImplementedError, "#{self.class} must implement #perform_service_call"
end

#resolve_resources(connection) ⇒ Array<Models::Vm, Models::Container>

Resolves resources based on IDs, --all flag, or selectors.

Parameters:

Returns:



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/pvectl/commands/irreversible_command.rb', line 120

def resolve_resources(connection)
  repo = build_repository(connection)

  resources = if @options[:all]
                repo.list(node: @options[:node])
              elsif @resource_ids.any?
                resolved = @resource_ids.map { |id| repo.get(id.to_i) }.compact
                resolved = resolved.select { |r| r.node == @options[:node] } if @options[:node]
                resolved
              else
                return [] if selector_strings.empty?
                repo.list(node: @options[:node])
              end

  apply_selectors(resources)
end

#resource_type_symbolSymbol

Returns the resource type symbol (:vm or :container).

Returns:

  • (Symbol)

    resource type



73
74
75
# File 'lib/pvectl/commands/irreversible_command.rb', line 73

def resource_type_symbol
  self.class::RESOURCE_TYPE
end

#selector_stringsArray<String>

Returns selector strings from options.

Returns:

  • (Array<String>)

    selector strings



152
153
154
# File 'lib/pvectl/commands/irreversible_command.rb', line 152

def selector_strings
  Array(@options[:selector] || @options[:l])
end

#service_optionsHash

Builds base service options from command options.

Returns:

  • (Hash)

    service options



207
208
209
210
211
212
213
214
# File 'lib/pvectl/commands/irreversible_command.rb', line 207

def service_options
  opts = {}
  opts[:timeout] = @options[:timeout] if @options[:timeout]
  opts[:async] = true if @options[:async]
  opts[:force] = true if @options[:force]
  opts[:fail_fast] = true if @options[:"fail-fast"]
  opts
end

#supported_resource?Boolean

Checks if resource type is supported.

Returns:

  • (Boolean)

    true if supported



87
88
89
# File 'lib/pvectl/commands/irreversible_command.rb', line 87

def supported_resource?
  supported_types.include?(@resource_type)
end

#supported_typesArray<String>

Returns supported resource type strings.

Returns:

  • (Array<String>)

    supported types



80
81
82
# File 'lib/pvectl/commands/irreversible_command.rb', line 80

def supported_types
  self.class::SUPPORTED_RESOURCES
end

#usage_error(message) ⇒ Integer

Outputs usage error and returns exit code.

Parameters:

  • message (String)

    error message

Returns:

  • (Integer)

    exit code



245
246
247
248
# File 'lib/pvectl/commands/irreversible_command.rb', line 245

def usage_error(message)
  $stderr.puts "Error: #{message}"
  ExitCodes::USAGE_ERROR
end