hybrid_platforms_conductor 32.7.1 → 32.8.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: 04d2e323c123bfaa35c0fb61f95dcc7a9ade67b19f184eff75c61419e249ac8e
4
- data.tar.gz: 25afb265a466103db73b891c62ec9a9ee32e5529bf8d9f0df71df379bab5ec7a
3
+ metadata.gz: f190c223603682dd67a933cb989ef89858d71459ebb1d962bfffda9def7666f2
4
+ data.tar.gz: 4fd85a4b961dc8ba8cc2c83b3f9749b49a021203c37afbe91db5123e54068007
5
5
  SHA512:
6
- metadata.gz: d8112983393928123c7f8f35395506c4ec5e401e84577e3ab1e24380ab0cd92affd90b6be0cb75645314d06cc8350cd8c478e1d1b9e3c600143296bdec73af47
7
- data.tar.gz: 6177c45b5489b077ee2d3e6d7535d6c07e94715c755c282ea8dc424e54d35ac1603c4be9d2e2d9b4cbfc4f450463c792a3e449f6dbbc5eb6bba4a0161c695c65
6
+ metadata.gz: e77c67013ea488956dd55927a06b22733efc7f283aa234bd3527b02a8044517447177a686c4964e27c7ff62afd9513de34efe33321ee6fdb16f7e3d6cd0dd06c
7
+ data.tar.gz: 0e041f3b998df727e83ec6580b09a02e4ee22c246e23a2f7ef5e3b64a3d60fd39745c1ed2241846467c14ba9d11b6dc8e8955929b6940f7ef87132369ecc9c80
@@ -311,13 +311,24 @@ module HybridPlatformsConductor
311
311
  environment: environment,
312
312
  logger: @logger,
313
313
  logger_stderr: @logger_stderr,
314
- config: @config,
314
+ config: sub_executable.config,
315
315
  cmd_runner: @cmd_runner,
316
316
  # Here we use the NodesHandler that will be bound to the sub-Deployer only, as the node's metadata might be modified by the Provisioner.
317
317
  nodes_handler: sub_executable.nodes_handler,
318
318
  actions_executor: @actions_executor
319
319
  )
320
320
  instance.with_running_instance(stop_on_exit: true, destroy_on_exit: !reuse_instance, port: 22) do
321
+ # Test-provisioned nodes have SSH Session Exec capabilities
322
+ sub_executable.nodes_handler.override_metadata_of node, :ssh_session_exec, 'true'
323
+ # Test-provisioned nodes use default sudo
324
+ sub_executable.config.sudo_procs.replace(sub_executable.config.sudo_procs.map do |sudo_proc_info|
325
+ {
326
+ nodes_selectors_stack: sudo_proc_info[:nodes_selectors_stack].map do |nodes_selector|
327
+ @nodes_handler.select_nodes(nodes_selector).select { |selected_node| selected_node != node }
328
+ end,
329
+ sudo_proc: sudo_proc_info[:sudo_proc]
330
+ }
331
+ end)
321
332
  actions_executor = sub_executable.actions_executor
322
333
  deployer = sub_executable.deployer
323
334
  # Setup test environment for this container
@@ -505,15 +505,25 @@ module HybridPlatformsConductor
505
505
  if @nodes_handler.get_ssh_session_exec_of(node) == 'false'
506
506
  # Here we have to create a ControlMaster using an interactive session, as the SSH server prohibits ExecSession, and so command executions.
507
507
  # We'll do that using another terminal spawned in the background.
508
- Thread.new do
509
- log_debug "[ ControlMaster - #{ssh_url} ] - Spawn interactive ControlMaster in separate terminal"
510
- @cmd_runner.run_cmd "xterm -e '#{ssh_exec} -o ControlMaster=yes -o ControlPersist=yes #{ssh_url}'", log_to_stdout: log_debug?
511
- log_debug "[ ControlMaster - #{ssh_url} ] - Separate interactive ControlMaster closed"
508
+ if ENV['hpc_interactive'] == 'false'
509
+ error = "Can't spawn interactive ControlMaster to #{node} in non-interactive mode. You may want to change the hpc_interactive env variable."
510
+ if no_exception
511
+ log_error error
512
+ exit_status = :non_interactive
513
+ else
514
+ raise error
515
+ end
516
+ else
517
+ Thread.new do
518
+ log_debug "[ ControlMaster - #{ssh_url} ] - Spawn interactive ControlMaster in separate terminal"
519
+ @cmd_runner.run_cmd "xterm -e '#{ssh_exec} -o ControlMaster=yes -o ControlPersist=yes #{ssh_url}'", log_to_stdout: log_debug?
520
+ log_debug "[ ControlMaster - #{ssh_url} ] - Separate interactive ControlMaster closed"
521
+ end
522
+ out 'External ControlMaster has been spawned.'
523
+ out 'Please login into it, keep its session opened and press enter here when done...'
524
+ $stdin.gets
525
+ exit_status = 0
512
526
  end
513
- out 'External ControlMaster has been spawned.'
514
- out 'Please login into it, keep its session opened and press enter here when done...'
515
- $stdin.gets
516
- exit_status = 0
517
527
  else
518
528
  # Create the control master
519
529
  ssh_control_master_start_cmd = "#{ssh_exec}#{@passwords.key?(node) || @auth_password ? '' : ' -o BatchMode=yes'} -o ControlMaster=yes -o ControlPersist=yes #{ssh_url} true"
@@ -54,17 +54,19 @@ module HybridPlatformsConductor
54
54
  instance.stop
55
55
  instance.with_running_instance(port: 22) do
56
56
 
57
- # ===== Deploy removes root access
58
- # Check that we can't connect with root
59
- ssh_ok = false
60
- begin
61
- Net::SSH.start(instance.ip, 'root', password: 'root_pwd', auth_methods: ['password'], verify_host_key: :never) do |ssh|
62
- ssh_ok = ssh.exec!('echo Works').strip == 'Works'
57
+ unless @nodes_handler.get_root_access_allowed_of(@node) == 'true'
58
+ # ===== Deploy removes root access
59
+ # Check that we can't connect with root
60
+ ssh_ok = false
61
+ begin
62
+ Net::SSH.start(instance.ip, 'root', password: 'root_pwd', auth_methods: ['password'], verify_host_key: :never) do |ssh|
63
+ ssh_ok = ssh.exec!('echo Works').strip == 'Works'
64
+ end
65
+ rescue
63
66
  end
64
- rescue
67
+ assert_equal ssh_ok, false, 'Root can still connect on the image after deployment'
68
+ # Even if we can connect using root, run the idempotence test
65
69
  end
66
- assert_equal ssh_ok, false, 'Root can still connect on the image after deployment'
67
- # Even if we can connect using root, run the idempotence test
68
70
 
69
71
  # ===== Idempotence
70
72
  unless ssh_ok
@@ -76,6 +76,15 @@ module HybridPlatformsConductor
76
76
  # Make sure we update it.
77
77
  @nodes_handler.override_metadata_of @node, :host_ip, instance_ip
78
78
  @nodes_handler.invalidate_metadata_of @node, :host_keys
79
+ # Make sure the SSH transformations don't apply to this node
80
+ @config.ssh_connection_transforms.replace(@config.ssh_connection_transforms.map do |ssh_transform_info|
81
+ {
82
+ nodes_selectors_stack: ssh_transform_info[:nodes_selectors_stack].map do |nodes_selector|
83
+ @nodes_handler.select_nodes(nodes_selector).select { |selected_node| selected_node != @node }
84
+ end,
85
+ transform: ssh_transform_info[:transform]
86
+ }
87
+ end)
79
88
  end
80
89
  wait_for_port!(port) if port
81
90
  yield
@@ -428,9 +428,11 @@ module HybridPlatformsConductor
428
428
  end
429
429
  end
430
430
  # Compute the timeout that will be applied, from the max timeout sum for every node that has tests to run
431
- timeout = CONNECTION_TIMEOUT + @cmds_to_run.map do |_node, cmds_list|
432
- cmds_list.inject(0) { |total_timeout, (_cmd, test_info)| test_info[:timeout] + total_timeout }
433
- end.max
431
+ timeout = CONNECTION_TIMEOUT + (
432
+ @cmds_to_run.map do |_node, cmds_list|
433
+ cmds_list.inject(0) { |total_timeout, (_cmd, test_info)| test_info[:timeout] + total_timeout }
434
+ end.max || 0
435
+ )
434
436
  # Run commands on nodes, in grouped way to avoid too many connections, per node
435
437
  # Hash< String, Array<String> >
436
438
  @test_cmds = Hash[@cmds_to_run.map do |node, cmds_list|
@@ -464,33 +466,35 @@ module HybridPlatformsConductor
464
466
  end,
465
467
  test_execution: proc do |test|
466
468
  exit_status, stdout, stderr = @actions_result[test.node]
467
- if exit_status.is_a?(Symbol)
468
- test.error "Error while executing tests: #{exit_status}: #{stderr}"
469
- else
470
- log_debug <<~EOS
471
- ----- Commands for #{test.node}:
472
- #{@test_cmds[test.node][:remote_bash].join("\n")}
473
- ----- STDOUT:
474
- #{stdout}
475
- ----- STDERR:
476
- #{stderr}
477
- -----
478
- EOS
479
- # Skip the first section, as it can contain SSH banners
480
- cmd_stdouts = stdout.split("#{CMD_SEPARATOR}\n")[1..-1]
481
- cmd_stdouts = [] if cmd_stdouts.nil?
482
- cmd_stderrs = stderr.split("#{CMD_SEPARATOR}\n")[1..-1]
483
- cmd_stderrs = [] if cmd_stderrs.nil?
484
- @cmds_to_run[test.node].zip(cmd_stdouts, cmd_stderrs).each do |(cmd, test_info), cmd_stdout, cmd_stderr|
485
- # Find the section that corresponds to this test
486
- if test_info[:test] == test
487
- cmd_stdout = '' if cmd_stdout.nil?
488
- cmd_stderr = '' if cmd_stderr.nil?
489
- stdout_lines = cmd_stdout.split("\n")
490
- # Last line of stdout is the return code
491
- return_code = stdout_lines.empty? ? :command_cant_run : Integer(stdout_lines.last)
492
- test.error "Command '#{cmd}' returned error code #{return_code}", "----- STDOUT:\n#{stdout_lines[0..-2].join("\n")}\n----- STDERR:\n#{cmd_stderr}" unless return_code == 0
493
- test_info[:validator].call(stdout_lines[0..-2], cmd_stderr.split("\n"), return_code)
469
+ unless exit_status.nil?
470
+ if exit_status.is_a?(Symbol)
471
+ test.error "Error while executing tests: #{exit_status}: #{stderr}"
472
+ else
473
+ log_debug <<~EOS
474
+ ----- Commands for #{test.node}:
475
+ #{@test_cmds[test.node][:remote_bash].join("\n")}
476
+ ----- STDOUT:
477
+ #{stdout}
478
+ ----- STDERR:
479
+ #{stderr}
480
+ -----
481
+ EOS
482
+ # Skip the first section, as it can contain SSH banners
483
+ cmd_stdouts = stdout.split("#{CMD_SEPARATOR}\n")[1..-1]
484
+ cmd_stdouts = [] if cmd_stdouts.nil?
485
+ cmd_stderrs = stderr.split("#{CMD_SEPARATOR}\n")[1..-1]
486
+ cmd_stderrs = [] if cmd_stderrs.nil?
487
+ @cmds_to_run[test.node].zip(cmd_stdouts, cmd_stderrs).each do |(cmd, test_info), cmd_stdout, cmd_stderr|
488
+ # Find the section that corresponds to this test
489
+ if test_info[:test] == test
490
+ cmd_stdout = '' if cmd_stdout.nil?
491
+ cmd_stderr = '' if cmd_stderr.nil?
492
+ stdout_lines = cmd_stdout.split("\n")
493
+ # Last line of stdout is the return code
494
+ return_code = stdout_lines.empty? ? :command_cant_run : Integer(stdout_lines.last)
495
+ test.error "Command '#{cmd}' returned error code #{return_code}", "----- STDOUT:\n#{stdout_lines[0..-2].join("\n")}\n----- STDERR:\n#{cmd_stderr}" unless return_code == 0
496
+ test_info[:validator].call(stdout_lines[0..-2], cmd_stderr.split("\n"), return_code)
497
+ end
494
498
  end
495
499
  end
496
500
  end
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '32.7.1'
3
+ VERSION = '32.8.2'
4
4
 
5
5
  end
@@ -94,6 +94,7 @@ module HybridPlatformsConductorTest
94
94
  ENV.delete 'hpc_password_for_thycotic'
95
95
  ENV.delete 'hpc_domain_for_thycotic'
96
96
  ENV.delete 'hpc_certificates'
97
+ ENV.delete 'hpc_interactive'
97
98
  # Set the necessary Hybrid Platforms Conductor environment variables
98
99
  ENV['hpc_ssh_user'] = 'test_user'
99
100
  HybridPlatformsConductor::ServicesHandler.packaged_deployments.clear
@@ -44,6 +44,58 @@ describe HybridPlatformsConductor::ActionsExecutor do
44
44
  end
45
45
  end
46
46
 
47
+ it 'can\'t create an SSH master to 1 node not having Session Exec capabilities when hpc_interactive is false' do
48
+ with_test_platform(nodes: { 'node' => { meta: { host_ip: '192.168.42.42', ssh_session_exec: 'false' } } }) do
49
+ ENV['hpc_interactive'] = 'false'
50
+ with_cmd_runner_mocked(
51
+ [
52
+ ['which env', proc { [0, "/usr/bin/env\n", ''] }],
53
+ ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }]
54
+ ] + ssh_expected_commands_for(
55
+ { 'node' => { connection: '192.168.42.42', user: 'test_user' } },
56
+ with_control_master_create: false,
57
+ with_control_master_destroy: false
58
+ )
59
+ ) do
60
+ test_connector.ssh_user = 'test_user'
61
+ expect do
62
+ test_connector.with_connection_to(['node']) do
63
+ end
64
+ end.to raise_error 'Can\'t spawn interactive ControlMaster to node in non-interactive mode. You may want to change the hpc_interactive env variable.'
65
+ end
66
+ end
67
+ end
68
+
69
+ it 'fails without creating exception when creating an SSH master to 1 node not having Session Exec capabilities when hpc_interactive is false and we use no_exception' do
70
+ with_test_platform(nodes: {
71
+ 'node1' => { meta: { host_ip: '192.168.42.1' } },
72
+ 'node2' => { meta: { host_ip: '192.168.42.2', ssh_session_exec: 'false' } },
73
+ 'node3' => { meta: { host_ip: '192.168.42.3' } }
74
+ }) do
75
+ ENV['hpc_interactive'] = 'false'
76
+ with_cmd_runner_mocked(
77
+ [
78
+ ['which env', proc { [0, "/usr/bin/env\n", ''] }],
79
+ ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }]
80
+ ] + ssh_expected_commands_for(
81
+ 'node1' => { connection: '192.168.42.1', user: 'test_user' },
82
+ 'node3' => { connection: '192.168.42.3', user: 'test_user' }
83
+ ) + ssh_expected_commands_for(
84
+ {
85
+ 'node2' => { connection: '192.168.42.2', user: 'test_user' }
86
+ },
87
+ with_control_master_create: false,
88
+ with_control_master_destroy: false
89
+ )
90
+ ) do
91
+ test_connector.ssh_user = 'test_user'
92
+ test_connector.with_connection_to(%w[node1 node2 node3], no_exception: true) do |connected_nodes|
93
+ expect(connected_nodes.sort).to eq %w[node1 node3].sort
94
+ end
95
+ end
96
+ end
97
+ end
98
+
47
99
  it 'creates SSH master to several nodes' do
48
100
  with_test_platform(nodes: {
49
101
  'node1' => { meta: { host_ip: '192.168.42.1' } },
@@ -123,6 +175,8 @@ describe HybridPlatformsConductor::ActionsExecutor do
123
175
  'node3' => { connection: '192.168.42.3', user: 'test_user' }
124
176
  },
125
177
  # Here the threads for node1's and node3's ControlMasters might not trigger before the one for node2, so they will not destroy it.
178
+ # Sometimes they don't even have time to create the Control Masters that node2 has already failed.
179
+ with_control_master_create_optional: true,
126
180
  with_control_master_destroy_optional: true
127
181
  ) + ssh_expected_commands_for(
128
182
  {
@@ -17,8 +17,8 @@ describe HybridPlatformsConductor::Deployer do
17
17
  block.call
18
18
  end
19
19
  provisioner = nil
20
- test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |test_deployer, test_instance|
21
- expect(test_deployer.local_environment).to eq true
20
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |sub_test_deployer, test_instance|
21
+ expect(sub_test_deployer.local_environment).to eq true
22
22
  provisioner = test_instance
23
23
  expect(test_instance.node).to eq 'node'
24
24
  expect(test_instance.environment).to match /^#{Regexp.escape(`whoami`.strip)}_hpc_testing_provisioner_\d+_\d+_\w+$/
@@ -40,8 +40,8 @@ describe HybridPlatformsConductor::Deployer do
40
40
  block.call
41
41
  end
42
42
  provisioner = nil
43
- test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |test_deployer, test_instance|
44
- expect(test_deployer.local_environment).to eq true
43
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |sub_test_deployer, test_instance|
44
+ expect(sub_test_deployer.local_environment).to eq true
45
45
  provisioner = test_instance
46
46
  expect(test_instance.node).to eq 'node'
47
47
  expect(test_instance.environment).to match /^#{Regexp.escape(`whoami`.strip)}_hpc_testing_provisioner_\d+_\d+_\w+$/
@@ -50,6 +50,70 @@ describe HybridPlatformsConductor::Deployer do
50
50
  end
51
51
  end
52
52
 
53
+ it 'gives a new test instance ready to be used in place of the node without SSH transformations' do
54
+ with_test_platform(
55
+ {
56
+ nodes: {
57
+ 'node1' => { meta: { host_ip: '192.168.42.1', ssh_session_exec: 'false' } },
58
+ 'node2' => { meta: { host_ip: '192.168.42.2', ssh_session_exec: 'false' } }
59
+ }
60
+ },
61
+ false,
62
+ '
63
+ for_nodes(%w[node1 node2]) do
64
+ transform_ssh_connection do |node, connection, connection_user, gateway, gateway_user|
65
+ ["#{connection}_#{node}", "#{connection_user}_#{node}", "#{gateway}_#{node}", "#{gateway_user}_#{node}"]
66
+ end
67
+ end
68
+ '
69
+ ) do |repository|
70
+ register_plugins(:provisioner, { test_provisioner: HybridPlatformsConductorTest::TestProvisioner })
71
+ File.write("#{test_config.hybrid_platforms_dir}/dummy_secrets.json", '{}')
72
+ HybridPlatformsConductorTest::TestProvisioner.mocked_states = %i[created created running exited]
73
+ HybridPlatformsConductorTest::TestProvisioner.mocked_ip = '172.17.0.1'
74
+ expect(Socket).to receive(:tcp).with('172.17.0.1', 22, { connect_timeout: 1 }) do |&block|
75
+ block.call
76
+ end
77
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node1', environment: 'hpc_testing_provisioner') do |sub_test_deployer, test_instance|
78
+ expect(sub_test_deployer.instance_eval { @nodes_handler.get_ssh_session_exec_of('node1') }).to eq 'true'
79
+ expect(sub_test_deployer.instance_eval { @nodes_handler.get_ssh_session_exec_of('node2') }).to eq 'false'
80
+ ssh_transforms = test_instance.instance_eval { @config.ssh_connection_transforms }
81
+ expect(ssh_transforms.size).to eq 1
82
+ expect(ssh_transforms[0][:nodes_selectors_stack]).to eq [%w[node2]]
83
+ end
84
+ end
85
+ end
86
+
87
+ it 'gives a new test instance ready to be used in place of the node without sudo specificities' do
88
+ with_test_platform(
89
+ {
90
+ nodes: {
91
+ 'node1' => { meta: { host_ip: '192.168.42.1' } },
92
+ 'node2' => { meta: { host_ip: '192.168.42.2' } }
93
+ }
94
+ },
95
+ false,
96
+ '
97
+ for_nodes(%w[node1 node2]) do
98
+ sudo_for { |user| "other_sudo --user #{user}" }
99
+ end
100
+ '
101
+ ) do |repository|
102
+ register_plugins(:provisioner, { test_provisioner: HybridPlatformsConductorTest::TestProvisioner })
103
+ File.write("#{test_config.hybrid_platforms_dir}/dummy_secrets.json", '{}')
104
+ HybridPlatformsConductorTest::TestProvisioner.mocked_states = %i[created created running exited]
105
+ HybridPlatformsConductorTest::TestProvisioner.mocked_ip = '172.17.0.1'
106
+ expect(Socket).to receive(:tcp).with('172.17.0.1', 22, { connect_timeout: 1 }) do |&block|
107
+ block.call
108
+ end
109
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node1', environment: 'hpc_testing_provisioner') do |sub_test_deployer, test_instance|
110
+ sudo_procs = test_instance.instance_eval { @config.sudo_procs }
111
+ expect(sudo_procs.size).to eq 1
112
+ expect(sudo_procs[0][:nodes_selectors_stack]).to eq [%w[node2]]
113
+ end
114
+ end
115
+ end
116
+
53
117
  it 'does not destroy instances when asked to reuse' do
54
118
  with_test_platform(
55
119
  nodes: { 'node' => { meta: { host_ip: '192.168.42.42' } } }
@@ -62,8 +126,8 @@ describe HybridPlatformsConductor::Deployer do
62
126
  block.call
63
127
  end
64
128
  provisioner = nil
65
- test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner', reuse_instance: true) do |test_deployer, test_instance|
66
- expect(test_deployer.local_environment).to eq true
129
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner', reuse_instance: true) do |sub_test_deployer, test_instance|
130
+ expect(sub_test_deployer.local_environment).to eq true
67
131
  provisioner = test_instance
68
132
  expect(test_instance.node).to eq 'node'
69
133
  expect(test_instance.environment).to eq "#{`whoami`.strip}_hpc_testing_provisioner"
@@ -84,8 +148,8 @@ describe HybridPlatformsConductor::Deployer do
84
148
  block.call
85
149
  end
86
150
  provisioner = nil
87
- test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner', reuse_instance: true) do |test_deployer, test_instance|
88
- expect(test_deployer.local_environment).to eq true
151
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner', reuse_instance: true) do |sub_test_deployer, test_instance|
152
+ expect(sub_test_deployer.local_environment).to eq true
89
153
  provisioner = test_instance
90
154
  expect(test_instance.node).to eq 'node'
91
155
  expect(test_instance.environment).to eq "#{`whoami`.strip}_hpc_testing_provisioner"
@@ -102,7 +166,7 @@ describe HybridPlatformsConductor::Deployer do
102
166
  File.write("#{test_config.hybrid_platforms_dir}/dummy_secrets.json", '{}')
103
167
  HybridPlatformsConductorTest::TestProvisioner.mocked_states = %i[created created created exited exited]
104
168
  expect do
105
- test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |test_deployer, test_instance|
169
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |sub_test_deployer, test_instance|
106
170
  end
107
171
  end.to raise_error /\[ node\/#{Regexp.escape(`whoami`.strip)}_hpc_testing_provisioner_\d+_\d+_\w+ \] - Instance fails to be in a state among \(running\) with timeout 1\. Currently in state exited/
108
172
  end
@@ -120,7 +184,7 @@ describe HybridPlatformsConductor::Deployer do
120
184
  raise Errno::ETIMEDOUT, 'Timeout while reading from port 22'
121
185
  end
122
186
  expect do
123
- test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |test_deployer, test_instance|
187
+ test_deployer.with_test_provisioned_instance(:test_provisioner, 'node', environment: 'hpc_testing_provisioner') do |sub_test_deployer, test_instance|
124
188
  end
125
189
  end.to raise_error /\[ node\/#{Regexp.escape(`whoami`.strip)}_hpc_testing_provisioner_\d+_\d+_\w+ \] - Instance fails to have port 22 opened with timeout 1\./
126
190
  end
@@ -69,7 +69,7 @@ describe HybridPlatformsConductor::TestsRunner do
69
69
  'node12' => { 'test_node12.sh' => proc { |stdout, stderr, exit_code| ssh_executions << ['node12', stdout, stderr, exit_code] } },
70
70
  'node21' => { 'test_node21.sh' => proc { |stdout, stderr, exit_code| ssh_executions << ['node21', stdout, stderr, exit_code] } },
71
71
  'node22' => { 'test_node22.sh' => proc { |stdout, stderr, exit_code| ssh_executions << ['node22', stdout, stderr, exit_code] } }
72
- }}
72
+ } }
73
73
  expect(test_tests_runner.run_tests([{ all: true }])).to eq 0
74
74
  expect(ssh_executions.sort).to eq [
75
75
  ['node11', ['stdout11'], ['stderr11'], 0],
@@ -88,7 +88,7 @@ describe HybridPlatformsConductor::TestsRunner do
88
88
  HybridPlatformsConductorTest::TestPlugins::NodeSsh.node_tests = { node_ssh_test: {
89
89
  'node12' => { 'test_node12.sh' => proc { |stdout, stderr, exit_code| ssh_executions << ['node12', stdout, stderr, exit_code] } },
90
90
  'node22' => { 'test_node22.sh' => proc { |stdout, stderr, exit_code| ssh_executions << ['node22', stdout, stderr, exit_code] } }
91
- }}
91
+ } }
92
92
  expect(test_tests_runner.run_tests(%w[node12 node22])).to eq 0
93
93
  expect(ssh_executions.sort).to eq [
94
94
  ['node12', ['stdout12'], ['stderr12'], 0],
@@ -97,6 +97,19 @@ describe HybridPlatformsConductor::TestsRunner do
97
97
  end
98
98
  end
99
99
 
100
+ it 'does not execute anything when the tests report no command' do
101
+ with_test_platform_for_node_connection_tests do
102
+ test_tests_runner.tests = [:node_ssh_test]
103
+ ssh_executions = []
104
+ HybridPlatformsConductorTest::TestPlugins::NodeSsh.node_tests = { node_ssh_test: {
105
+ 'node12' => {},
106
+ 'node22' => {}
107
+ } }
108
+ expect(test_tests_runner.run_tests(%w[node12 node22])).to eq 0
109
+ expect(ssh_executions).to eq []
110
+ end
111
+ end
112
+
100
113
  it 'executes several SSH node tests once per node with the correct command, grouping commands' do
101
114
  with_test_platform_for_node_connection_tests do
102
115
  expect_actions_executor_runs([proc do |actions|
@@ -77,7 +77,11 @@ module HybridPlatformsConductorTest
77
77
  log_stderr_to_io << mocked_stderr if !mocked_stderr.empty? && !log_stderr_to_io.nil?
78
78
  [mocked_exit_status, mocked_stdout, mocked_stderr]
79
79
  else
80
- raise "Unexpected command run: #{cmd}"
80
+ raise "Unexpected command run:\n#{cmd}\nRemaining expected commands:\n#{
81
+ remaining_expected_commands.map do |(expected_command, _command_code, _options)|
82
+ expected_command
83
+ end.join("\n")
84
+ }"
81
85
  end
82
86
  end
83
87
  yield
@@ -85,7 +89,11 @@ module HybridPlatformsConductorTest
85
89
  remaining_expected_commands.select do |(_expected_command, _command_code, options)|
86
90
  !options[:optional]
87
91
  end
88
- ).to eq([]), "Expected CmdRunner commands were not run:\n#{remaining_expected_commands.map(&:first).join("\n")}"
92
+ ).to eq([]), "Expected CmdRunner commands were not run:\n#{
93
+ remaining_expected_commands.map do |(expected_command, _command_code, options)|
94
+ "#{options[:optional] ? '[Optional] ' : ''}#{expected_command}"
95
+ end.join("\n")
96
+ }"
89
97
  # Un-mock the command runner
90
98
  allow(cmd_runner).to receive(:run_cmd).and_call_original
91
99
  end
@@ -15,6 +15,7 @@ module HybridPlatformsConductorTest
15
15
  # * *times* (Integer): Number of times this connection should be used [default: 1]
16
16
  # * *control_master_create_error* (String or nil): Error to simulate during the SSH ControlMaster creation, or nil for none [default: nil]
17
17
  # * *with_control_master_create* (Boolean): Do we create the control master? [default: true]
18
+ # * *with_control_master_create_optional* (Boolean): If true, then consider the ControlMaster creation to be optional [default: false]
18
19
  # * *with_control_master_check* (Boolean): Do we check the control master? [default: false]
19
20
  # * *with_control_master_destroy* (Boolean): Do we destroy the control master? [default: true]
20
21
  # * *with_control_master_destroy_optional* (Boolean): If true, then consider the ControlMaster destruction to be optional [default: false]
@@ -26,6 +27,7 @@ module HybridPlatformsConductorTest
26
27
  def ssh_expected_commands_for(
27
28
  nodes_connections,
28
29
  with_control_master_create: true,
30
+ with_control_master_create_optional: false,
29
31
  with_control_master_check: false,
30
32
  with_control_master_destroy: true,
31
33
  with_control_master_destroy_optional: false,
@@ -52,13 +54,15 @@ module HybridPlatformsConductorTest
52
54
  if with_session_exec
53
55
  /^.+\/ssh #{with_batch_mode ? '-o BatchMode=yes ' : ''}-o ControlMaster=yes -o ControlPersist=yes hpc\.#{Regexp.escape(node)} true$/
54
56
  else
55
- # Mock the user hitting enter as the Control Master will be created in another thread and the main thread waits for user input.
56
- expect($stdin).to receive(:gets) do
57
- # We have to wait for the Control Master creation thread to actually create the Control Master before hitting Enter.
58
- while !control_master_created do
59
- sleep 0.1
57
+ unless ENV['hpc_interactive'] == 'false'
58
+ # Mock the user hitting enter as the Control Master will be created in another thread and the main thread waits for user input.
59
+ expect($stdin).to receive(:gets) do
60
+ # We have to wait for the Control Master creation thread to actually create the Control Master before hitting Enter.
61
+ while !control_master_created do
62
+ sleep 0.1
63
+ end
64
+ "\n"
60
65
  end
61
- "\n"
62
66
  end
63
67
  /^xterm -e '.+\/ssh -o ControlMaster=yes -o ControlPersist=yes hpc\.#{Regexp.escape(node)}'$/
64
68
  end,
@@ -78,7 +82,8 @@ module HybridPlatformsConductorTest
78
82
  else
79
83
  [255, '', node_connection_info[:control_master_create_error]]
80
84
  end
81
- end
85
+ end,
86
+ { optional: with_control_master_create_optional }
82
87
  ]
83
88
  end
84
89
  if with_control_master_check
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.7.1
4
+ version: 32.8.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-12 00:00:00.000000000 Z
11
+ date: 2021-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: range_operators