kitchen-proxmox 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/kitchen/driver/proxmox/api_client.rb +14 -2
- data/lib/kitchen/driver/proxmox/errors.rb +39 -0
- data/lib/kitchen/driver/proxmox.rb +59 -10
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8592a8c21a5245c5517893fb3de6c2c2a9cae921cdb8c499fd3a7bcfcc8dc35a
|
|
4
|
+
data.tar.gz: eb18680f687a41ef9a5a5c2f4b290e586e867de4795cdc5733998189fb3b4e85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8da473dbe9ddf5891bff39a77fe3a55db3fd263a4413592e0c55d826387dbde2e60f77287516f31dd5083200d8d5c962ceaf811f43ad0b89e1a794311ece678b
|
|
7
|
+
data.tar.gz: c93d6729fa61fbc286cdd6b64e03febad2ea3995c1a29e346e5c35037f51339f84aef79370c90443ce96f4ec94fb494225fbf05a3ad27942378c97b54e2d9e36
|
|
@@ -5,6 +5,7 @@ require 'json'
|
|
|
5
5
|
require 'uri'
|
|
6
6
|
require 'openssl'
|
|
7
7
|
require 'kitchen'
|
|
8
|
+
require_relative 'errors'
|
|
8
9
|
|
|
9
10
|
module Kitchen
|
|
10
11
|
module Driver
|
|
@@ -26,6 +27,10 @@ module Kitchen
|
|
|
26
27
|
get('/api2/json/cluster/nextid')
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
def validate_vmid(vm_id)
|
|
31
|
+
get("/api2/json/cluster/nextid?vmid=#{vm_id}")
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
def clone_vm(node:, template_id:, new_id:, **options)
|
|
30
35
|
full = options.fetch(:full, true)
|
|
31
36
|
body = { newid: new_id, full: full ? 1 : 0, target: node }
|
|
@@ -75,7 +80,14 @@ module Kitchen
|
|
|
75
80
|
deadline = Time.now + timeout
|
|
76
81
|
loop do
|
|
77
82
|
status = task_status(node:, upid:)
|
|
78
|
-
|
|
83
|
+
if status['status'] == 'stopped'
|
|
84
|
+
exitstatus = status['exitstatus'].to_s
|
|
85
|
+
unless exitstatus == 'OK'
|
|
86
|
+
raise ProxmoxErrors::ApiError.new(500, exitstatus)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
return status
|
|
90
|
+
end
|
|
79
91
|
raise "Task timeout after #{timeout}s: #{upid}" if Time.now > deadline
|
|
80
92
|
|
|
81
93
|
sleep interval
|
|
@@ -145,7 +157,7 @@ module Kitchen
|
|
|
145
157
|
end
|
|
146
158
|
|
|
147
159
|
def handle_response(response)
|
|
148
|
-
raise
|
|
160
|
+
raise ProxmoxErrors::ApiError.new(response.code, response.body) unless response.is_a?(Net::HTTPSuccess)
|
|
149
161
|
|
|
150
162
|
JSON.parse(response.body)['data']
|
|
151
163
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kitchen
|
|
4
|
+
module Driver
|
|
5
|
+
module ProxmoxErrors
|
|
6
|
+
# Structured error raised by ApiClient on non-2xx responses.
|
|
7
|
+
class ApiError < ::StandardError
|
|
8
|
+
attr_reader :status_code, :response_body
|
|
9
|
+
|
|
10
|
+
def initialize(status_code, response_body)
|
|
11
|
+
@status_code = status_code.to_i
|
|
12
|
+
@response_body = response_body
|
|
13
|
+
super("Proxmox API error #{status_code}: #{response_body}")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns true when the error indicates a VMID is already in use
|
|
17
|
+
# or cannot be locked (concurrent clone race).
|
|
18
|
+
def vmid_conflict?
|
|
19
|
+
return false unless status_code == 400 || status_code == 500
|
|
20
|
+
|
|
21
|
+
response_body.match?(/already exists/i) ||
|
|
22
|
+
response_body.match?(/unable to create VM \d+/i) ||
|
|
23
|
+
response_body.match?(/can't lock file.*lock-\d+/i)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Returns true when the error indicates another process owns the VM
|
|
27
|
+
# (lost a VMID race — VM was started or configured by another clone).
|
|
28
|
+
def vmid_race_lost?
|
|
29
|
+
return false unless status_code == 400 || status_code == 500
|
|
30
|
+
|
|
31
|
+
response_body.match?(/already running/i) ||
|
|
32
|
+
response_body.match?(/hotplug problem/i) ||
|
|
33
|
+
response_body.match?(/does not exist/i) ||
|
|
34
|
+
response_body.match?(/can't lock file.*lock-\d+/i)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'kitchen'
|
|
4
4
|
require_relative 'proxmox_version'
|
|
5
|
+
require_relative 'proxmox/errors'
|
|
5
6
|
require_relative 'proxmox/api_client'
|
|
6
7
|
|
|
7
8
|
module Kitchen
|
|
@@ -16,6 +17,8 @@ module Kitchen
|
|
|
16
17
|
|
|
17
18
|
plugin_version Kitchen::Driver::PROXMOX_VERSION
|
|
18
19
|
|
|
20
|
+
ApiError = Kitchen::Driver::ProxmoxErrors::ApiError
|
|
21
|
+
|
|
19
22
|
required_config :proxmox_url
|
|
20
23
|
required_config :proxmox_token_id
|
|
21
24
|
required_config :proxmox_token_secret
|
|
@@ -32,6 +35,9 @@ module Kitchen
|
|
|
32
35
|
default_config :clone_timeout, 300
|
|
33
36
|
default_config :start_timeout, 300
|
|
34
37
|
default_config :ip_wait_timeout, 120
|
|
38
|
+
default_config :clone_retries, 5
|
|
39
|
+
default_config :vmid_range_min, 900_000
|
|
40
|
+
default_config :vmid_range_max, 999_999
|
|
35
41
|
|
|
36
42
|
def create(state)
|
|
37
43
|
return if state[:vm_id]
|
|
@@ -63,23 +69,66 @@ module Kitchen
|
|
|
63
69
|
end
|
|
64
70
|
|
|
65
71
|
def clone_and_start(state)
|
|
66
|
-
|
|
72
|
+
retries = config[:clone_retries]
|
|
73
|
+
last_error = nil
|
|
74
|
+
|
|
75
|
+
retries.times do |attempt|
|
|
76
|
+
info("Creating Proxmox VM from template #{config[:template_id]}...")
|
|
77
|
+
|
|
78
|
+
vm_id, vm_name = allocate_and_clone
|
|
79
|
+
state[:vm_id] = vm_id
|
|
80
|
+
state[:vm_name] = vm_name
|
|
81
|
+
|
|
82
|
+
begin
|
|
83
|
+
configure_hardware(vm_id)
|
|
84
|
+
start_and_wait_for_ip(state, vm_id)
|
|
85
|
+
info("Proxmox VM #{vm_name} (#{vm_id}) created.")
|
|
86
|
+
return
|
|
87
|
+
rescue ApiError => e
|
|
88
|
+
raise unless e.vmid_race_lost?
|
|
89
|
+
|
|
90
|
+
last_error = e
|
|
91
|
+
warn("VMID #{vm_id} race lost (attempt #{attempt + 1}/#{retries}): #{e.message}")
|
|
92
|
+
warn("Another process owns VM #{vm_id} — abandoning and retrying...")
|
|
93
|
+
clear_state(state)
|
|
94
|
+
sleep backoff_delay(attempt)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
raise last_error
|
|
99
|
+
end
|
|
67
100
|
|
|
68
|
-
|
|
69
|
-
|
|
101
|
+
def allocate_and_clone
|
|
102
|
+
retries = config[:clone_retries]
|
|
103
|
+
last_error = nil
|
|
70
104
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
105
|
+
retries.times do |attempt|
|
|
106
|
+
vm_id = allocate_vm_id
|
|
107
|
+
vm_name = generate_vm_name(instance.name)
|
|
74
108
|
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
begin
|
|
110
|
+
clone_template(vm_id, vm_name)
|
|
111
|
+
return [vm_id, vm_name]
|
|
112
|
+
rescue ApiError => e
|
|
113
|
+
raise unless e.vmid_conflict?
|
|
114
|
+
|
|
115
|
+
last_error = e
|
|
116
|
+
warn("VMID #{vm_id} conflict (attempt #{attempt + 1}/#{retries}), retrying...")
|
|
117
|
+
sleep backoff_delay(attempt)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
raise last_error
|
|
122
|
+
end
|
|
77
123
|
|
|
78
|
-
|
|
124
|
+
def backoff_delay(attempt)
|
|
125
|
+
(0.5 * (2**attempt)) + rand(0.0..1.0)
|
|
79
126
|
end
|
|
80
127
|
|
|
81
128
|
def allocate_vm_id
|
|
82
|
-
|
|
129
|
+
vm_id = rand(config[:vmid_range_min]..config[:vmid_range_max])
|
|
130
|
+
api_client.validate_vmid(vm_id)
|
|
131
|
+
vm_id
|
|
83
132
|
end
|
|
84
133
|
|
|
85
134
|
def generate_vm_name(suite_name)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-proxmox
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Richard Nixon
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04
|
|
11
|
+
date: 2026-05-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A Test Kitchen driver for Proxmox VE. Manages VM lifecycle (create, destroy)
|
|
14
14
|
via the Proxmox REST API. Supports cloning from templates.
|
|
@@ -22,6 +22,7 @@ files:
|
|
|
22
22
|
- README.md
|
|
23
23
|
- lib/kitchen/driver/proxmox.rb
|
|
24
24
|
- lib/kitchen/driver/proxmox/api_client.rb
|
|
25
|
+
- lib/kitchen/driver/proxmox/errors.rb
|
|
25
26
|
- lib/kitchen/driver/proxmox_version.rb
|
|
26
27
|
homepage: https://github.com/trickyearlobe-chef/kitchen-proxmox
|
|
27
28
|
licenses:
|