hybrid_platforms_conductor 32.9.0 → 32.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 378b6da76cab8e20f60c0517f7bbef90933b5d573069437ee16d913ca81c0689
4
- data.tar.gz: 1924dd81e2740b50ee55758271282729d6df1fd6a45b555f96c321d1b40e54e3
3
+ metadata.gz: 2162d052fbbe0833d8a26d94d9fc8a490e9c57b0fd35ecd5656be8d3a52a2893
4
+ data.tar.gz: 40d6251c2dac02c7588ea158174ecb6da4f2381712e8696b5395fdfcb05555ce
5
5
  SHA512:
6
- metadata.gz: f8a72778375c154e42829a430bec151a7553b79874d1a878e129d80eed22b0f77f094751ee370c84441e7ed658d9e7b87e80c8f950ba64b491e1e62eec1f4a2f
7
- data.tar.gz: e437a3f4871bde44d6bcbcd8a588dd83ff33c0374cee3769eada500a3acf04606e37144f98d2a49aaf69e0ff8cdd935d3eaa396fea5bb19ac485184df883db24
6
+ metadata.gz: 572f8136afc8153bcb645c2612b128b7c6aeb2b44fc795fa1785f0dc6bad06298171466b37aef5d03ad38d6f49cb39cab3c268ce6edc0807cb125328e24c6cba
7
+ data.tar.gz: 993478c8a81af0e5bfce3a74cfe5ce9c8bb4259d0f6063e41912ea905d493a2e6cca801525d45ffd080ba1570d80e2c0097e56aac33ee694526e53e6ee9685ee
@@ -11,13 +11,24 @@ module HybridPlatformsConductor
11
11
  # Array< Hash<Symbol, Object> >
12
12
  attr_reader :ignored_idempotence_tasks
13
13
 
14
- # Initialize the DSL
14
+ # List of ignored tasks info. Each info has the following properties:
15
+ # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
16
+ # * *ignored_tasks* (Hash<String, String>): List of task names for which we ignore divergence errors, with the corresponding descriptive reason for ignore.
17
+ # Array< Hash<Symbol, Object> >
18
+ attr_reader :ignored_divergent_tasks
19
+
20
+ # Initialize the DSL
15
21
  def init_idempotence_tests
16
22
  # List of ignored tasks info. Each info has the following properties:
17
23
  # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
18
24
  # * *ignored_tasks* (Hash<String, String>): List of task names for which we ignore idempotence errors, with the corresponding descriptive reason for ignore.
19
25
  # Array< Hash<Symbol, Object> >
20
26
  @ignored_idempotence_tasks = []
27
+ # List of ignored tasks info. Each info has the following properties:
28
+ # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
29
+ # * *ignored_tasks* (Hash<String, String>): List of task names for which we ignore divergence errors, with the corresponding descriptive reason for ignore.
30
+ # Array< Hash<Symbol, Object> >
31
+ @ignored_divergent_tasks = []
21
32
  end
22
33
 
23
34
  # Ignore idempotence errors on a set of tasks
@@ -31,6 +42,17 @@ module HybridPlatformsConductor
31
42
  }
32
43
  end
33
44
 
45
+ # Ignore idempotence errors on a set of tasks
46
+ #
47
+ # Parameters::
48
+ # * *tasks_to_ignore* (Hash<String, String>): Set of tasks to ignore, along with the reason
49
+ def ignore_divergent_tasks(tasks_to_ignore)
50
+ @ignored_divergent_tasks << {
51
+ ignored_tasks: tasks_to_ignore,
52
+ nodes_selectors_stack: current_nodes_selectors_stack,
53
+ }
54
+ end
55
+
34
56
  end
35
57
 
36
58
  end
@@ -18,11 +18,19 @@ module HybridPlatformsConductor
18
18
  attr_accessor *%i[logger logger_stderr]
19
19
 
20
20
  def check_response(response)
21
- log_debug "Response from Proxmox API: #{response}"
22
- log_warn "Response from Proxmox API: #{response}" if response.code >= 400 && !log_debug?
21
+ msg = "Response from Proxmox API: #{response} - #{response.net_http_res.message}"
22
+ log_debug msg
23
+ log_warn msg if response.code >= 400 && !log_debug?
23
24
  super
24
25
  end
25
26
 
27
+ # Re-authenticate the Proxmox instance
28
+ # This can be useful when the API returns errors due to invalidated tokens
29
+ def reauthenticate
30
+ log_debug 'Force re-authentication to Proxmox'
31
+ @auth_params = create_ticket
32
+ end
33
+
26
34
  end
27
35
  ::Proxmox::Proxmox.prepend ProxmoxPatches
28
36
 
@@ -309,6 +317,8 @@ module HybridPlatformsConductor
309
317
  log_warn "[ #{@node}/#{@environment} ] - Proxmox API call get #{path} returned error #{response} (attempt ##{idx_try}/#{proxmox_test_info[:api_max_retries]})"
310
318
  raise "[ #{@node}/#{@environment} ] - Proxmox API call get #{path} returns #{response} continuously (tried #{idx_try + 1} times)" if idx_try >= proxmox_test_info[:api_max_retries]
311
319
  idx_try += 1
320
+ # We have to reauthenticate: error 500 raised by Proxmox are often due to token being invalidated wrongly
321
+ proxmox.reauthenticate
312
322
  sleep proxmox_test_info[:api_wait_between_retries_secs] + rand(5)
313
323
  end
314
324
  response
@@ -333,6 +343,8 @@ module HybridPlatformsConductor
333
343
  task = nil
334
344
  break if idx_try >= proxmox_test_info[:api_max_retries]
335
345
  idx_try += 1
346
+ # We have to reauthenticate: error 500 raised by Proxmox are often due to token being invalidated wrongly
347
+ proxmox.reauthenticate
336
348
  sleep proxmox_test_info[:api_wait_between_retries_secs] + rand(5)
337
349
  end
338
350
  end
@@ -605,10 +605,35 @@ class ProxmoxWaiter
605
605
  # Result::
606
606
  # * Array<Integer>: List of available VM IDs
607
607
  def free_vm_ids
608
- Range.new(*@config['vm_ids_range']).to_a -
608
+ vm_ids = Range.new(*@config['vm_ids_range']).to_a -
609
609
  api_get('nodes').map do |pve_node_info|
610
610
  api_get("nodes/#{pve_node_info['node']}/lxc").map { |lxc_info| Integer(lxc_info['vmid']) }
611
611
  end.flatten
612
+ # Make sure the vm_ids that are available don't have any leftovers in the cgroups.
613
+ # This can happen with some Proxmox bugs, and make the API returns 500 errors.
614
+ # cf. https://forum.proxmox.com/threads/lxc-console-cleanup-error.38293/
615
+ # TODO: Remove this when Proxmox will have solved the issue with leftovers of destroyed vms.
616
+ (vm_ids.map(&:to_s) & vm_ids_in_cgroups).each do |vm_id_str|
617
+ # We are having a vm_id that is available but still has some leftovers in cgroups.
618
+ # Clean those to avoid 500 errors in API.
619
+ log "Found VMID #{vm_id_str} with leftovers in cgroups. Cleaning those."
620
+ Dir.glob("/sys/fs/cgroup/*/lxc/#{vm_id_str}") do |cgroup_dir|
621
+ log "Removing #{cgroup_dir}"
622
+ FileUtils.rm_rf cgroup_dir
623
+ end
624
+ end
625
+ vm_ids
626
+ end
627
+
628
+ # Return the list of VM IDs present in cgroups
629
+ #
630
+ # Result::
631
+ # * Array<String>: List of VM IDs as strings (as some are not Integers like '1010-1')
632
+ def vm_ids_in_cgroups
633
+ Dir.glob('/sys/fs/cgroup/*/lxc/*').map do |file|
634
+ basename = File.basename(file)
635
+ basename =~ /^\d.+$/ ? basename : nil
636
+ end.compact.sort.uniq
612
637
  end
613
638
 
614
639
  # Wait for a given Proxmox task completion
@@ -653,6 +678,9 @@ class ProxmoxWaiter
653
678
  break unless @gets_cache[path].is_a?(String) && @gets_cache[path] =~ /^NOK: error code = 5\d\d$/
654
679
  raise "Proxmox API get #{path} returns #{@gets_cache[path]} continuously (tried #{idx_try + 1} times)" if idx_try >= @config['api_max_retries']
655
680
  idx_try += 1
681
+ # We have to reauthenticate: error 500 raised by Proxmox are often due to token being invalidated wrongly
682
+ # TODO: Provide a way to do it properly in the official gem
683
+ @proxmox.instance_variable_set(:@auth_params, @proxmox.send(:create_ticket))
656
684
  sleep @config['api_wait_between_retries_secs']
657
685
  end
658
686
  end
@@ -28,6 +28,6 @@
28
28
  </table>
29
29
  <p>
30
30
  <br/>
31
- Generated at <%= Time.now.utc.strftime('%F %T') %> UTC by <a href="https://www.site.my_company.net/git/projects/PROJECTrepos/hybrid_platforms_conductor/browse">Hybrid Platforms Conductor</a>.
31
+ Generated at <%= Time.now.utc.strftime('%F %T') %> UTC by <a href="https://github.com/sweet-delights/hybrid-platforms-conductor">Hybrid Platforms Conductor</a>.
32
32
  <br/>
33
33
  </p>
@@ -81,7 +81,10 @@ module HybridPlatformsConductor
81
81
  exit_status, stdout, stderr = deployer.deploy_on(@node)[@node]
82
82
  assert_equal exit_status, 0, "Check-node after deployment returned error code #{exit_status}", log_debug? ? nil : deployer.stdouts_to_s
83
83
  # Check that the output of the check-node returns no changes.
84
- ignored_tasks = @nodes_handler.select_confs_for_node(@node, @config.ignored_idempotence_tasks).inject({}) do |merged_ignored_tasks, conf|
84
+ ignored_tasks = (
85
+ @nodes_handler.select_confs_for_node(@node, @config.ignored_idempotence_tasks) +
86
+ @nodes_handler.select_confs_for_node(@node, @config.ignored_divergent_tasks)
87
+ ).inject({}) do |merged_ignored_tasks, conf|
85
88
  merged_ignored_tasks.merge(conf[:ignored_tasks])
86
89
  end
87
90
  @deployer.parse_deploy_output(@node, stdout, stderr).each do |task_info|
@@ -11,8 +11,23 @@ module HybridPlatformsConductor
11
11
 
12
12
  # Check my_test_plugin.rb.sample documentation for signature details.
13
13
  def test_on_check_node(stdout, stderr, exit_status)
14
+ # Check that the output of the check-node returns no changes.
15
+ ignored_tasks = @nodes_handler.select_confs_for_node(@node, @config.ignored_divergent_tasks).inject({}) do |merged_ignored_tasks, conf|
16
+ merged_ignored_tasks.merge(conf[:ignored_tasks])
17
+ end
14
18
  @deployer.parse_deploy_output(@node, stdout, stderr).each do |task_info|
15
- error "Task #{task_info[:name]} has diverged", JSON.pretty_generate(task_info) if task_info[:status] == :changed
19
+ if task_info[:status] == :changed
20
+ if ignored_tasks.key?(task_info[:name])
21
+ # It was expected that this task is not idempotent
22
+ log_debug "Task #{task_info[:name]} was expected to be divergent. Reason: #{ignored_tasks[task_info[:name]]}"
23
+ else
24
+ extra_details = task_info.slice(*(task_info.keys - %i[name status diffs]))
25
+ error_details = []
26
+ error_details << "----- Changes:\n#{task_info[:diffs].strip}\n-----" if task_info[:diffs]
27
+ error_details << "----- Additional details:\n#{JSON.pretty_generate(extra_details)}\n-----" unless extra_details.empty?
28
+ error "Task #{task_info[:name]} has diverged", error_details.empty? ? nil : error_details.join("\n")
29
+ end
30
+ end
16
31
  end
17
32
  end
18
33
 
@@ -11,24 +11,38 @@ module HybridPlatformsConductor
11
11
 
12
12
  # Check my_test_plugin.rb.sample documentation for signature details.
13
13
  def test
14
- example_platform = PlatformsHandler.new.known_platforms.first
15
- example_node = example_platform.known_nodes.first
16
- [
17
- "#{CmdRunner.executables_prefix}check-node --node #{example_node} --show-commands",
18
- "#{CmdRunner.executables_prefix}deploy --node #{example_node} --show-commands --why-run",
14
+ tests = [
19
15
  "#{CmdRunner.executables_prefix}dump_nodes_json --help",
20
16
  "#{CmdRunner.executables_prefix}free_ips",
21
17
  "#{CmdRunner.executables_prefix}free_veids",
22
- "#{CmdRunner.executables_prefix}get_impacted_nodes --platform #{example_platform.name} --show-commands",
23
- "#{CmdRunner.executables_prefix}last_deploys --node #{example_node} --show-commands",
24
- "#{CmdRunner.executables_prefix}nodes_to_deploy --node #{example_node} --show-commands",
25
- "#{CmdRunner.executables_prefix}report --node #{example_node} --format stdout",
26
- "#{CmdRunner.executables_prefix}run --node #{example_node} --show-commands --interactive",
27
18
  "#{CmdRunner.executables_prefix}setup --help",
28
19
  "#{CmdRunner.executables_prefix}ssh_config",
29
- "#{CmdRunner.executables_prefix}test --help",
30
- "#{CmdRunner.executables_prefix}topograph --from \"--node #{example_node}\" --to \"--node #{example_node}\" --skip-run --output graphviz:graph.gv"
31
- ].each do |cmd|
20
+ "#{CmdRunner.executables_prefix}test --help"
21
+ ]
22
+ example_platform = PlatformsHandler.new(
23
+ logger: @logger,
24
+ logger_stderr: @logger_stderr,
25
+ config: @config,
26
+ cmd_runner: @cmd_runner
27
+ ).known_platforms.first
28
+ unless example_platform.nil?
29
+ tests.concat [
30
+ "#{CmdRunner.executables_prefix}get_impacted_nodes --platform #{example_platform.name} --show-commands",
31
+ ]
32
+ example_node = example_platform.known_nodes.first
33
+ unless example_node.nil?
34
+ tests.concat [
35
+ "#{CmdRunner.executables_prefix}check-node --node #{example_node} --show-commands",
36
+ "#{CmdRunner.executables_prefix}deploy --node #{example_node} --show-commands --why-run",
37
+ "#{CmdRunner.executables_prefix}last_deploys --node #{example_node} --show-commands",
38
+ "#{CmdRunner.executables_prefix}nodes_to_deploy --node #{example_node} --show-commands",
39
+ "#{CmdRunner.executables_prefix}report --node #{example_node} --format stdout",
40
+ "#{CmdRunner.executables_prefix}run --node #{example_node} --show-commands --interactive",
41
+ "#{CmdRunner.executables_prefix}topograph --from \"--node #{example_node}\" --to \"--node #{example_node}\" --skip-run --output graphviz:graph.gv"
42
+ ]
43
+ end
44
+ end
45
+ tests.sort.each do |cmd|
32
46
  log_debug "Testing #{cmd}"
33
47
  exit_status, stdout, _stderr = @cmd_runner.run_cmd "#{cmd} 2>&1", no_exception: true, log_to_stdout: log_debug?
34
48
  assert_equal(exit_status, 0, "Command #{cmd} returned code #{exit_status}:\n#{stdout}")
@@ -43,7 +43,10 @@ module HybridPlatformsConductor
43
43
  assert_equal tested_node, @node, "Wrong node being tested: #{tested_node} should be #{@node}"
44
44
  assert_equal exit_status, 0, "Check-node returned error code #{exit_status}"
45
45
  # Check that the output of the check-node returns no changes.
46
- ignored_tasks = @nodes_handler.select_confs_for_node(@node, @config.ignored_idempotence_tasks).inject({}) do |merged_ignored_tasks, conf|
46
+ ignored_tasks = (
47
+ @nodes_handler.select_confs_for_node(@node, @config.ignored_idempotence_tasks) +
48
+ @nodes_handler.select_confs_for_node(@node, @config.ignored_divergent_tasks)
49
+ ).inject({}) do |merged_ignored_tasks, conf|
47
50
  merged_ignored_tasks.merge(conf[:ignored_tasks])
48
51
  end
49
52
  @deployer.parse_deploy_output(@node, stdout, stderr).each do |task_info|
@@ -237,6 +237,6 @@
237
237
  </table>
238
238
  <p>
239
239
  <br/>
240
- Generated at <%= Time.now.utc.strftime('%F %T') %> UTC by <a href="https://www.site.my_company.net/git/projects/PROJECTrepos/hybrid_platforms_conductor/browse">Hybrid Platforms Conductor</a>.
240
+ Generated at <%= Time.now.utc.strftime('%F %T') %> UTC by <a href="https://github.com/sweet-delights/hybrid-platforms-conductor">Hybrid Platforms Conductor</a>.
241
241
  <br/>
242
242
  </p>
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '32.9.0'
3
+ VERSION = '32.11.2'
4
4
 
5
5
  end
@@ -10,6 +10,7 @@ describe HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox do
10
10
  with_sync_node do
11
11
  mock_proxmox(mocked_pve_nodes: [{ 'pve_node_name' => {} }] * 5)
12
12
  expect(call_reserve_proxmox_container(2, 128 * 1024, 4, max_retries: 5)).to eq(error: 'not_enough_resources')
13
+ expect_proxmox_actions_to_be []
13
14
  end
14
15
  end
15
16
 
@@ -25,6 +26,23 @@ describe HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox do
25
26
  vm_id: 1000,
26
27
  vm_ip: '192.168.0.100'
27
28
  )
29
+ expect_proxmox_actions_to_be [
30
+ [
31
+ :post,
32
+ 'nodes/pve_node_name/lxc',
33
+ {
34
+ 'ostemplate' => 'test_template.iso',
35
+ 'hostname' => 'test.hostname.my-domain.com',
36
+ 'description' => /node: test_node\nenvironment: test_env/,
37
+ 'cores' => 2,
38
+ 'cpulimit' => 2,
39
+ 'memory' => 1024,
40
+ 'rootfs' => 'local-lvm:4',
41
+ 'net0' => 'name=eth0,bridge=vmbr0,gw=172.16.16.16,ip=192.168.0.100/32',
42
+ 'vmid' => 1000
43
+ }
44
+ ]
45
+ ]
28
46
  end
29
47
  end
30
48
 
@@ -34,6 +52,12 @@ describe HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox do
34
52
  result = call_reserve_proxmox_container(2, 1024, 4, config: { api_max_retries: 4 })
35
53
  expect(result[:error]).not_to eq nil
36
54
  expect(result[:error]).to match /Unhandled exception from reserve_proxmox_container: Proxmox API get nodes\/pve_node_name\/lxc returns NOK: error code = 500 continuously \(tried 5 times\)/
55
+ expect_proxmox_actions_to_be [
56
+ [:create_ticket],
57
+ [:create_ticket],
58
+ [:create_ticket],
59
+ [:create_ticket]
60
+ ]
37
61
  end
38
62
  end
39
63
 
@@ -45,6 +69,26 @@ describe HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox do
45
69
  vm_id: 1000,
46
70
  vm_ip: '192.168.0.100'
47
71
  )
72
+ expect_proxmox_actions_to_be [
73
+ [:create_ticket],
74
+ [:create_ticket],
75
+ [:create_ticket],
76
+ [
77
+ :post,
78
+ 'nodes/pve_node_name/lxc',
79
+ {
80
+ 'ostemplate' => 'test_template.iso',
81
+ 'hostname' => 'test.hostname.my-domain.com',
82
+ 'description' => /node: test_node\nenvironment: test_env/,
83
+ 'cores' => 2,
84
+ 'cpulimit' => 2,
85
+ 'memory' => 1024,
86
+ 'rootfs' => 'local-lvm:4',
87
+ 'net0' => 'name=eth0,bridge=vmbr0,gw=172.16.16.16,ip=192.168.0.100/32',
88
+ 'vmid' => 1000
89
+ }
90
+ ]
91
+ ]
48
92
  end
49
93
  end
50
94
 
@@ -60,6 +60,98 @@ describe HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox do
60
60
  end
61
61
  end
62
62
 
63
+ it 'makes sure to remove cgroup files that are leftovers of removed containers' do
64
+ with_sync_node(leftovers: [
65
+ '/sys/fs/cgroup/memory/lxc/1003'
66
+ ]) do
67
+ mock_proxmox(mocked_pve_nodes: {
68
+ 'pve_node_name' => {
69
+ lxc_containers: {
70
+ 1000 => { ip: '192.168.1.100' },
71
+ 1001 => { ip: '192.168.1.101' }
72
+ }
73
+ }
74
+ })
75
+ expect(call_reserve_proxmox_container(2, 1024, 1, config: { vm_ids_range: [1000, 1100] })).to eq(
76
+ pve_node: 'pve_node_name',
77
+ vm_id: 1002,
78
+ vm_ip: '192.168.0.100'
79
+ )
80
+ end
81
+ end
82
+
83
+ it 'makes sure to remove cgroup files that are leftovers of removed containers even when they are reusing the VM ID' do
84
+ with_sync_node(leftovers: [
85
+ '/sys/fs/cgroup/memory/lxc/1002'
86
+ ]) do
87
+ mock_proxmox(mocked_pve_nodes: {
88
+ 'pve_node_name' => {
89
+ lxc_containers: {
90
+ 1000 => { ip: '192.168.1.100' },
91
+ 1001 => { ip: '192.168.1.101' }
92
+ }
93
+ }
94
+ })
95
+ expect(call_reserve_proxmox_container(2, 1024, 1, config: { vm_ids_range: [1000, 1100] })).to eq(
96
+ pve_node: 'pve_node_name',
97
+ vm_id: 1002,
98
+ vm_ip: '192.168.0.100'
99
+ )
100
+ end
101
+ end
102
+
103
+ it 'makes sure to remove cgroup files that are leftovers of removed containers when several cgroups contain files' do
104
+ with_sync_node(leftovers: [
105
+ '/sys/fs/cgroup/memory/lxc/1003',
106
+ '/sys/fs/cgroup/network/lxc/1003',
107
+ '/sys/fs/cgroup/cpu/lxc/1003'
108
+ ]) do
109
+ mock_proxmox(mocked_pve_nodes: {
110
+ 'pve_node_name' => {
111
+ lxc_containers: {
112
+ 1000 => { ip: '192.168.1.100' },
113
+ 1001 => { ip: '192.168.1.101' }
114
+ }
115
+ }
116
+ })
117
+ expect(call_reserve_proxmox_container(2, 1024, 1, config: { vm_ids_range: [1000, 1100] })).to eq(
118
+ pve_node: 'pve_node_name',
119
+ vm_id: 1002,
120
+ vm_ip: '192.168.0.100'
121
+ )
122
+ end
123
+ end
124
+
125
+ it 'makes sure to remove only cgroup files that are leftovers of removed containers inside our VM ID range' do
126
+ with_sync_node(
127
+ leftovers: [
128
+ '/sys/fs/cgroup/memory/lxc/100',
129
+ '/sys/fs/cgroup/memory/lxc/1001',
130
+ '/sys/fs/cgroup/memory/lxc/1002',
131
+ '/sys/fs/cgroup/memory/lxc/1003'
132
+ ],
133
+ expect_remaining_leftovers: [
134
+ '/sys/fs/cgroup/memory/lxc/100',
135
+ '/sys/fs/cgroup/memory/lxc/1001'
136
+ ]
137
+ ) do
138
+ mock_proxmox(mocked_pve_nodes: {
139
+ 'pve_node_name' => {
140
+ lxc_containers: {
141
+ 100 => { ip: '192.168.1.10' },
142
+ 1000 => { ip: '192.168.1.100' },
143
+ 1001 => { ip: '192.168.1.101' }
144
+ }
145
+ }
146
+ })
147
+ expect(call_reserve_proxmox_container(2, 1024, 1, config: { vm_ids_range: [1000, 1100] })).to eq(
148
+ pve_node: 'pve_node_name',
149
+ vm_id: 1002,
150
+ vm_ip: '192.168.0.100'
151
+ )
152
+ end
153
+ end
154
+
63
155
  end
64
156
 
65
157
  end
@@ -23,20 +23,6 @@ describe HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox do
23
23
  end
24
24
  end
25
25
 
26
-
27
- it '' do
28
- with_test_proxmox_platform do |instance|
29
- mock_proxmox_calls_with [
30
- # 1 - The info on existing containers
31
- mock_proxmox_to_get_nodes_info,
32
- # 2 - The start of the container - fail a few times
33
- mock_proxmox_to_start_node(nbr_api_errors: 2)
34
- ]
35
- instance.create
36
- instance.start
37
- end
38
- end
39
-
40
26
  it 'retries calls to the API when getting back errors 5xx' do
41
27
  with_test_proxmox_platform do |instance|
42
28
  mock_proxmox_calls_with [
@@ -156,6 +156,7 @@ module HybridPlatformsConductorTest
156
156
  idx_try += 1
157
157
  idx_try <= nbr_api_errors ? 'NOK: error code = 500' : task_name
158
158
  end
159
+ expect(proxmox).to receive(:reauthenticate).exactly(nbr_api_errors - (task_status.nil? ? 1 : 0)).times
159
160
  # Mock checking task status
160
161
  unless task_status.nil?
161
162
  # Mock checking task status
@@ -294,6 +295,7 @@ module HybridPlatformsConductorTest
294
295
  ]
295
296
  end
296
297
  end
298
+ expect(proxmox).to receive(:reauthenticate).exactly(nbr_api_errors - (status.nil? ? 1 : 0)).times
297
299
  unless status.nil?
298
300
  expect(proxmox).to receive(:get).with('nodes/pve_node_name/lxc/1024/status/current') do
299
301
  {
@@ -641,6 +643,10 @@ module HybridPlatformsConductorTest
641
643
  raise "Unknown Proxmox API post call: #{path}. Please adapt the test framework."
642
644
  end
643
645
  end
646
+ # Mock create_ticket
647
+ allow(proxmox).to receive(:create_ticket) do
648
+ @proxmox_actions << [:create_ticket]
649
+ end
644
650
  proxmox
645
651
  end
646
652
  end,
@@ -651,11 +657,36 @@ module HybridPlatformsConductorTest
651
657
  # Prepare a repository to test reserve_proxmox_container
652
658
  #
653
659
  # Parameters::
660
+ # * *leftovers* (Array<String>): List of leftover files among cgroups [default: []]
661
+ # * *expect_remaining_leftovers* (Array<String>): List of leftover files among cgroups that should remain after run [default: []]
654
662
  # * Proc: Code to be called with repository setup
655
- def with_sync_node
663
+ def with_sync_node(leftovers: [], expect_remaining_leftovers: [])
656
664
  with_repository('sync_node') do |repository|
665
+ # Mock the cgroup file system of the sync node
666
+ remaining_leftovers = leftovers.clone
667
+ allow(Dir).to receive(:glob).and_wrap_original do |original_glob, dir, &block|
668
+ case dir
669
+ when '/sys/fs/cgroup/*/lxc/*'
670
+ block.nil? ? remaining_leftovers : remaining_leftovers.each(&block)
671
+ when /^\/sys\/fs\/cgroup\/\*\/lxc\/(.+)$/
672
+ vm_id_str = $1
673
+ file_pattern = /^\/sys\/fs\/cgroup\/.+\/lxc\/#{Regexp.escape(vm_id_str)}$/
674
+ matched_files = remaining_leftovers.select { |file| file =~ file_pattern }
675
+ block.nil? ? matched_files : matched_files.each(&block)
676
+ else
677
+ original_glob.call(dir, &block)
678
+ end
679
+ end
680
+ allow(FileUtils).to receive(:rm_rf).and_wrap_original do |original_rm_rf, path|
681
+ if path.start_with?('/sys/fs/cgroup')
682
+ remaining_leftovers.delete_if { |file| file.start_with?(path) }
683
+ else
684
+ original_rm_rf.call(path)
685
+ end
686
+ end
657
687
  @repository = repository
658
688
  yield
689
+ expect(remaining_leftovers.sort).to eq expect_remaining_leftovers.sort
659
690
  end
660
691
  end
661
692
 
@@ -808,7 +839,13 @@ module HybridPlatformsConductorTest
808
839
  # Parameters::
809
840
  # * *expected_proxmox_actions* (Array<Array>): Expected Proxmox actions
810
841
  def expect_proxmox_actions_to_be(expected_proxmox_actions)
811
- expect(@proxmox_actions.size).to eq expected_proxmox_actions.size
842
+ expect(@proxmox_actions.size).to eq(expected_proxmox_actions.size), <<~EOS
843
+ Expected #{expected_proxmox_actions.size} Proxmox actions, but got #{@proxmox_actions.size} instead:
844
+ ----- Received:
845
+ #{@proxmox_actions.map(&:inspect).join("\n")}
846
+ ----- Expected:
847
+ #{expected_proxmox_actions.map(&:inspect).join("\n")}
848
+ EOS
812
849
  @proxmox_actions.zip(expected_proxmox_actions).each do |proxmox_action, expected_proxmox_action|
813
850
  expect(proxmox_action.size).to eq expected_proxmox_action.size
814
851
  expect(proxmox_action[0..1]).to eq expected_proxmox_action[0..1]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hybrid_platforms_conductor
3
3
  version: !ruby/object:Gem::Version
4
- version: 32.9.0
4
+ version: 32.11.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muriel Salvan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-22 00:00:00.000000000 Z
11
+ date: 2021-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: range_operators
@@ -281,20 +281,20 @@ description: Provides a complete toolset to help DevOps maintain, deploy, monito
281
281
  email:
282
282
  - muriel@x-aeon.com
283
283
  executables:
284
- - topograph
285
- - test
286
- - free_veids
287
- - free_ips
288
- - nodes_to_deploy
289
- - last_deploys
290
- - check-node
291
284
  - run
292
- - report
293
285
  - get_impacted_nodes
294
- - ssh_config
286
+ - dump_nodes_json
287
+ - check-node
288
+ - nodes_to_deploy
289
+ - free_veids
290
+ - free_ips
291
+ - test
295
292
  - deploy
293
+ - report
294
+ - topograph
296
295
  - setup
297
- - dump_nodes_json
296
+ - last_deploys
297
+ - ssh_config
298
298
  extensions: []
299
299
  extra_rdoc_files: []
300
300
  files: