Class: Pvectl::Services::EditDns

Inherits:
Object
  • Object
show all
Defined in:
lib/pvectl/services/edit_dns.rb,
sig/pvectl/services/edit_dns.rbs

Overview

Orchestrates the interactive editing flow for node DNS settings.

Fetches current DNS config, presents it as YAML in an editor, computes a diff, and applies changes via the Proxmox API. The Proxmox PUT endpoint requires the search field — the service validates this before calling.

Examples:

Basic usage

service = EditDns.new(dns_repository: repo)
result = service.execute(node_name: "pve1")

Dry run

service = EditDns.new(dns_repository: repo, options: { dry_run: true })
result = service.execute(node_name: "pve1")

Constant Summary collapse

EDITABLE_KEYS =

Editable keys exposed in the YAML editor (in order).

Returns:

  • (Array[Symbol])
i[search dns1 dns2 dns3].freeze

Instance Method Summary collapse

Constructor Details

#initialize(dns_repository:, editor_session: nil, options: {}) ⇒ EditDns

Creates a new EditDns service.

Parameters:

  • dns_repository (Repositories::Dns)

    DNS repository

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

    optional injected editor session

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

    options (dry_run)

  • dns_repository: (Object)
  • editor_session: (Object, nil) (defaults to: nil)
  • options: (Hash[Symbol, untyped]) (defaults to: {})


28
29
30
31
32
# File 'lib/pvectl/services/edit_dns.rb', line 28

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

Instance Method Details

#build_editable(dns) ⇒ Hash{String=>String}

Builds editable hash (string-keyed) from a DnsConfig model.

Parameters:

Returns:

  • (Hash{String=>String})

    editable values



80
81
82
83
84
85
86
87
# File 'lib/pvectl/services/edit_dns.rb', line 80

def build_editable(dns)
  {
    "search" => dns.search,
    "dns1" => dns.dns1,
    "dns2" => dns.dns2,
    "dns3" => dns.dns3
  }.compact
end

#build_result(resource_info, **attrs) ⇒ Models::NodeOperationResult

Builds a NodeOperationResult with the :edit operation.

Parameters:

  • resource_info (Hash)

    resource info (node_name)

  • attrs (Hash)

    additional result attributes

Returns:



151
152
153
154
155
156
# File 'lib/pvectl/services/edit_dns.rb', line 151

def build_result(resource_info, **attrs)
  node_model = Models::Node.new(name: resource_info[:node_name])
  Models::NodeOperationResult.new(
    operation: :edit, node_model: node_model, resource: resource_info, **attrs
  )
end

#build_update_params(edited) ⇒ Hash

Builds API update parameters from edited config.

The PUT endpoint requires search and accepts optional dns1-3. Removed keys are sent as empty strings to clear the field (Proxmox accepts empty strings as "unset" for optional dns* fields).

Parameters:

  • edited (Hash)

    edited config (symbol keys)

Returns:

  • (Hash)

    API parameters with all editable keys present



135
136
137
138
139
140
141
142
143
144
# File 'lib/pvectl/services/edit_dns.rb', line 135

def build_update_params(edited)
  params = {}
  EDITABLE_KEYS.each do |key|
    value = edited[key]
    params[key] = value.nil? ? "" : value
  end
  # search must always have a real value (validated above)
  params[:search] = edited[:search]
  params
end

#compute_diff(original, edited) ⇒ Hash

Computes diff between original and edited configs.

Parameters:

  • original (Hash)

    original config (symbol keys)

  • edited (Hash)

    edited config (symbol keys)

Returns:

  • (Hash)

    diff with :changed, :added, :removed



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/pvectl/services/edit_dns.rb', line 106

def compute_diff(original, edited)
  changed = {}
  added = {}
  removed = []

  edited.each do |key, value|
    orig_value = original[key]
    if orig_value.nil?
      added[key] = value
    elsif orig_value.to_s != value.to_s
      changed[key] = [orig_value.to_s, value.to_s]
    end
  end

  original.each_key do |key|
    removed << key unless edited.key?(key)
  end

  { changed: changed, added: added, removed: removed }
end

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

Executes the interactive DNS edit flow.

Parameters:

  • node_name (String)

    node name

  • node_name: (String)

Returns:



38
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
# File 'lib/pvectl/services/edit_dns.rb', line 38

def execute(node_name:)
  dns = @dns_repository.fetch(node_name)
  resource_info = { node_name: node_name }

  editable = build_editable(dns)
  yaml_content = "# Node: #{node_name} — DNS configuration\n" \
                 "# 'search' is required by Proxmox. dns1/dns2/dns3 are optional.\n" +
                 editable.to_yaml

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

  return nil unless edited

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

  original_symbolized = editable.transform_keys(&:to_sym)
  changes = compute_diff(original_symbolized, edited_config)

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

  validate!(edited_config)

  resource_info[:diff] = changes

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

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

#validate!(edited) ⇒ void

This method returns an undefined value.

Validates the edited config satisfies API requirements.

Parameters:

  • edited (Hash)

    edited config with symbol keys

Raises:

  • (ArgumentError)

    if search is missing or empty



94
95
96
97
98
99
# File 'lib/pvectl/services/edit_dns.rb', line 94

def validate!(edited)
  search = edited[:search]
  if search.nil? || search.to_s.strip.empty?
    raise ArgumentError, "search field is required by Proxmox API"
  end
end