Class: Pvectl::Services::EditNode

Inherits:
Object
  • Object
show all
Defined in:
lib/pvectl/services/edit_node.rb

Overview

Orchestrates the interactive node configuration editing flow.

Fetches current config, presents it as YAML in an editor, computes diff, and applies changes. Uses plain YAML (not ConfigSerializer) since node config is flat key-value.

Examples:

Basic usage

service = EditNode.new(node_repository: repo)
result = service.execute(node_name: "pve1")

Dry run with injected editor session

service = EditNode.new(node_repository: repo, editor_session: session,
                       options: { dry_run: true })
result = service.execute(node_name: "pve1")

Constant Summary collapse

READONLY_KEYS =

Read-only keys that should not be sent back to the API.

%i[digest].freeze

Instance Method Summary collapse

Constructor Details

#initialize(node_repository:, editor_session: nil, options: {}) ⇒ EditNode

Creates a new EditNode service.

Parameters:

  • node_repository (Repositories::Node)

    Node repository

  • editor_session (EditorSession, nil) (defaults to: nil)

    optional injected editor session

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

    options (dry_run)



29
30
31
32
33
# File 'lib/pvectl/services/edit_node.rb', line 29

def initialize(node_repository:, editor_session: nil, options: {})
  @node_repository = node_repository
  @editor_session = editor_session
  @options = options
end

Instance Method Details

#execute(node_name:) ⇒ Models::NodeOperationResult?

Executes the interactive node edit flow.

Parameters:

  • node_name (String)

    node name

Returns:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/pvectl/services/edit_node.rb', line 39

def execute(node_name:)
  node = @node_repository.get(node_name)
  return not_found_result(node_name) unless node

  config = @node_repository.fetch_config(node_name)
  resource_info = { node_name: node_name, status: node.status }

  # Build editable YAML (exclude digest — it's for optimistic locking only)
  editable = config.reject { |k, _| READONLY_KEYS.include?(k) }
  # Convert symbol keys to strings for clean YAML output (avoids :key: format)
  string_keyed = editable.transform_keys(&:to_s)
  yaml_content = "# Node: #{node_name}\n# Edit configuration below. Save and close to apply.\n" +
                  string_keyed.to_yaml

  session = @editor_session || EditorSession.new
  edited = session.edit(yaml_content)

  return nil unless edited

  # Parse edited YAML (strip comment lines)
  cleaned = edited.lines.reject { |l| l.strip.start_with?("#") }.join
  edited_config = YAML.safe_load(cleaned, symbolize_names: true) || {}

  # Compute diff against original editable config (with symbol keys)
  changes = compute_diff(editable, edited_config)

  if changes[:changed].empty? && changes[:added].empty? && changes[:removed].empty?
    return nil
  end

  resource_info[:diff] = changes

  if @options[:dry_run]
    return build_result(resource_info, success: true)
  end

  update_params = build_update_params(changes, config)
  @node_repository.update(node_name, update_params)
  build_result(resource_info, success: true)
rescue StandardError => e
  build_result({ node_name: node_name }, success: false, error: e.message)
end