hybrid_platforms_conductor 32.7.1 → 32.8.2

Sign up to get free protection for your applications and to get access to all the features.
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