Class: Knife::Proxmox::Provisioner

Inherits:
Object
  • Object
show all
Defined in:
lib/knife-proxmox-ve/provisioner.rb

Overview

The single place the clone -> configure -> start -> wait-for-IP sequence lives, and the one place cloud-init param encoding policy is defined. Everything that requires ordering between Api round-trips belongs here; Api itself stays stateless.

Dependencies are injected so the whole sequence is testable without sockets or sleeps:

* +api+        — Knife::Proxmox::Api
* +ui+         — duck-typed #info/#warn sink (optional; progress is best-effort)
* +port_check+ — ->(host, port) { bool }; defaults to a real, short TCP connect
* +sleeper+    — ->(seconds) {}; defaults to Kernel#sleep

Defined Under Namespace

Classes: Result

Constant Summary collapse

POLL_INTERVAL =

Backoff between TCP/agent polls while waiting for the guest to come up.

2
PORT_CHECK_TIMEOUT =

Time budget for a single TCP connect probe.

2
CLOUDINIT_DRIVE_KEY =

A cloud-init drive lives on one of these controllers with a value naming “cloudinit”.

/\A(?:ide|scsi|sata)\d+\z/
CLOUDINIT_DRIVE_VALUE =
/cloudinit/
DUPLICATE_ID =

Proxmox reports a VMID collision as a non-2xx body mentioning the existing id.

/already exists|config file already exists|VM \d+ already/i

Instance Method Summary collapse

Constructor Details

#initialize(api, ui: nil, port_check: nil, sleeper: nil) ⇒ Provisioner

Returns a new instance of Provisioner.



37
38
39
40
41
42
# File 'lib/knife-proxmox-ve/provisioner.rb', line 37

def initialize(api, ui: nil, port_check: nil, sleeper: nil)
  @api = api
  @ui = ui
  @port_check = port_check || method(:tcp_open?)
  @sleeper = sleeper || ->(seconds) { sleep(seconds) }
end

Instance Method Details

#provision(spec) ⇒ Object

Run the full provisioning sequence for spec (see the class header / WU contract for its shape). Returns a Result; raises Knife::Proxmox::* loudly on any failure.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/knife-proxmox-ve/provisioner.rb', line 46

def provision(spec)
  template = spec.fetch(:template)
  newid = clone_with_retry(spec, template)
  node = spec[:target_node] || template[:node]

  params = build_config(spec.fetch(:config, {}) || {})
  guard_cloud_init_drive!(node, newid, spec[:name]) if cloud_init?(params)
  @api.update_config(node:, vmid: newid, **params) unless params.empty?

  boot(node, newid)
  ip = wait_for_ip(spec, node, newid)

  Result.new(vmid: newid, node:, ip:)
end