hybrid_platforms_conductor 32.5.0 → 32.7.3

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.
@@ -59,10 +59,10 @@ describe HybridPlatformsConductor::NodesHandler do
59
59
  '
60
60
  ) do
61
61
  expect(test_config.sudo_procs.size).to eq 2
62
- expect(test_config.sudo_procs[0][:nodes_selectors_stack]). to eq []
63
- expect(test_config.sudo_procs[0][:sudo_proc].call('test_user')). to eq 'alt_sudo1 -p test_user'
64
- expect(test_config.sudo_procs[1][:nodes_selectors_stack]). to eq ['node2']
65
- expect(test_config.sudo_procs[1][:sudo_proc].call('test_user')). to eq 'alt_sudo2 -q test_user'
62
+ expect(test_config.sudo_procs[0][:nodes_selectors_stack]).to eq []
63
+ expect(test_config.sudo_procs[0][:sudo_proc].call('test_user')).to eq 'alt_sudo1 -p test_user'
64
+ expect(test_config.sudo_procs[1][:nodes_selectors_stack]).to eq ['node2']
65
+ expect(test_config.sudo_procs[1][:sudo_proc].call('test_user')).to eq 'alt_sudo2 -q test_user'
66
66
  end
67
67
  end
68
68
 
@@ -50,7 +50,8 @@ describe 'executables\' common options' do
50
50
  with_test_platform_for_common_options do
51
51
  exit_code, stdout, stderr = run executable, *(['--debug'] + default_options)
52
52
  expect(exit_code).to eq 0
53
- expect(stderr).to eq ''
53
+ # Make sure to ignore the deployment markers from stderr.
54
+ expect(stderr.gsub("===== [ node1 / node1_service ] - HPC Service Check ===== Begin\n===== [ node1 / node1_service ] - HPC Service Check ===== End\n", '')).to eq ''
54
55
  end
55
56
  end
56
57
 
@@ -8,14 +8,27 @@ module HybridPlatformsConductorTest
8
8
  # Run expectations on the expected commands to be called.
9
9
  #
10
10
  # Parameters::
11
- # * *commands* (Array< [String or Regexp, Proc] >): Expected commands that should be called on CmdRunner: the command name or regexp and the corresponding mocked code
12
- # * Parameters::
13
- # * Same parameters as CmdRunner@run_cmd
11
+ # * *commands* (Array<Array>): List of expected commands that should be called on CmdRunner. Each specification is a list containing those items:
12
+ # * *0* (String or Regexp): The command name or regexp matching the command name
13
+ # * *1* (Proc): The mocking code to be called in place of the real command:
14
+ # * Parameters::
15
+ # * Same parameters as CmdRunner@run_cmd
16
+ # * Result::
17
+ # * Same results as CmdRunner@run_cmd
18
+ # * *2* (Hash): Optional hash of options. Can be ommited. [default = {}]
19
+ # * *optional* (Boolean): If true then don't fail if the command to be mocked has not been called [default: false]
14
20
  # * *cmd_runner* (CmdRunner): The CmdRunner to mock [default: test_cmd_runner]
15
21
  # * Proc: Code called with the command runner mocked
16
22
  def with_cmd_runner_mocked(commands, cmd_runner: test_cmd_runner)
17
- unexpected_commands = []
18
- remaining_expected_commands = commands.clone
23
+ remaining_expected_commands = commands.map do |(expected_command, command_code, options)|
24
+ [
25
+ expected_command,
26
+ command_code,
27
+ {
28
+ optional: false
29
+ }.merge(options || {})
30
+ ]
31
+ end
19
32
  # We need to protect the access to this array as the mocked commands can be called by competing threads
20
33
  remaining_expected_commands_mutex = Mutex.new
21
34
  allow(cmd_runner).to receive(:run_cmd) do |cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false|
@@ -23,7 +36,7 @@ module HybridPlatformsConductorTest
23
36
  found_command = nil
24
37
  found_command_code = nil
25
38
  remaining_expected_commands_mutex.synchronize do
26
- remaining_expected_commands.delete_if do |(expected_command, command_code)|
39
+ remaining_expected_commands.delete_if do |(expected_command, command_code, _options)|
27
40
  break unless found_command.nil?
28
41
  if (expected_command.is_a?(String) && expected_command == cmd) || (expected_command.is_a?(Regexp) && cmd =~ expected_command)
29
42
  found_command = expected_command
@@ -64,14 +77,23 @@ module HybridPlatformsConductorTest
64
77
  log_stderr_to_io << mocked_stderr if !mocked_stderr.empty? && !log_stderr_to_io.nil?
65
78
  [mocked_exit_status, mocked_stdout, mocked_stderr]
66
79
  else
67
- logger.error "[ Mocked CmdRunner ] - !!! Unexpected command run: #{cmd}"
68
- unexpected_commands << cmd
69
- [:unexpected_command_to_mock, '', "Could not mock unexpected command #{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
+ }"
70
85
  end
71
86
  end
72
87
  yield
73
- expect(unexpected_commands).to eq []
74
- expect(remaining_expected_commands).to eq([]), "Expected CmdRunner commands were not run:\n#{remaining_expected_commands.map(&:first).join("\n")}"
88
+ expect(
89
+ remaining_expected_commands.select do |(_expected_command, _command_code, options)|
90
+ !options[:optional]
91
+ end
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
+ }"
75
97
  # Un-mock the command runner
76
98
  allow(cmd_runner).to receive(:run_cmd).and_call_original
77
99
  end
@@ -10,39 +10,60 @@ module HybridPlatformsConductorTest
10
10
  # Parameters::
11
11
  # * *nodes_connections* (Hash<String, Hash<Symbol,Object> >): Nodes' connections info, per node name:
12
12
  # * *connection* (String): Connection string (fqdn, IP...) used by SSH
13
+ # * *ip* (String): IP used by SSH (can be different from connection in case of transformed SSH) [default: connection]
13
14
  # * *user* (String): User used by SSH
14
15
  # * *times* (Integer): Number of times this connection should be used [default: 1]
15
16
  # * *control_master_create_error* (String or nil): Error to simulate during the SSH ControlMaster creation, or nil for none [default: nil]
16
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]
17
19
  # * *with_control_master_check* (Boolean): Do we check the control master? [default: false]
18
20
  # * *with_control_master_destroy* (Boolean): Do we destroy the control master? [default: true]
21
+ # * *with_control_master_destroy_optional* (Boolean): If true, then consider the ControlMaster destruction to be optional [default: false]
19
22
  # * *with_strict_host_key_checking* (Boolean): Do we use strict host key checking? [default: true]
20
23
  # * *with_batch_mode* (Boolean): Do we use BatchMode when creating the control master? [default: true]
24
+ # * *with_session_exec* (Boolean): Do we use Sessien Exec capabilities when creating the control master? [default: true]
21
25
  # Result::
22
- # * Array< [String or Regexp, Proc] >: The expected commands that should be used, and their corresponding mocked code
26
+ # * Array<Array>: The expected commands that should be used, and their corresponding mocked code and options
23
27
  def ssh_expected_commands_for(
24
28
  nodes_connections,
25
29
  with_control_master_create: true,
30
+ with_control_master_create_optional: false,
26
31
  with_control_master_check: false,
27
32
  with_control_master_destroy: true,
33
+ with_control_master_destroy_optional: false,
28
34
  with_strict_host_key_checking: true,
29
- with_batch_mode: true
35
+ with_batch_mode: true,
36
+ with_session_exec: true
30
37
  )
31
38
  nodes_connections.map do |node, node_connection_info|
32
39
  node_connection_info[:times] = 1 unless node_connection_info.key?(:times)
33
40
  ssh_commands_once = []
34
41
  ssh_commands_per_connection = []
35
42
  if with_strict_host_key_checking
43
+ ip = node_connection_info[:ip] || node_connection_info[:connection]
36
44
  ssh_commands_once.concat([
37
45
  [
38
- "ssh-keyscan #{node_connection_info[:connection]}",
39
- proc { [0, "#{node_connection_info[:connection]} ssh-rsa fake_host_key_for_#{node_connection_info[:connection]}", ''] }
46
+ "ssh-keyscan #{ip}",
47
+ proc { [0, "#{ip} ssh-rsa fake_host_key_for_#{ip}", ''] }
40
48
  ]
41
49
  ])
42
50
  end
43
51
  if with_control_master_create
52
+ control_master_created = false
44
53
  ssh_commands_per_connection << [
45
- /^.+\/ssh #{with_batch_mode ? '-o BatchMode=yes ' : ''}-o ControlMaster=yes -o ControlPersist=yes #{Regexp.escape(node_connection_info[:user])}@hpc\.#{Regexp.escape(node)} true$/,
54
+ if with_session_exec
55
+ /^.+\/ssh #{with_batch_mode ? '-o BatchMode=yes ' : ''}-o ControlMaster=yes -o ControlPersist=yes hpc\.#{Regexp.escape(node)} true$/
56
+ else
57
+ # Mock the user hitting enter as the Control Master will be created in another thread and the main thread waits for user input.
58
+ expect($stdin).to receive(:gets) do
59
+ # We have to wait for the Control Master creation thread to actually create the Control Master before hitting Enter.
60
+ while !control_master_created do
61
+ sleep 0.1
62
+ end
63
+ "\n"
64
+ end
65
+ /^xterm -e '.+\/ssh -o ControlMaster=yes -o ControlPersist=yes hpc\.#{Regexp.escape(node)}'$/
66
+ end,
46
67
  proc do
47
68
  control_file = test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], '22', node_connection_info[:user])
48
69
  # Fail if the ControlMaster file already exists, as would SSH do if the file is stalled
@@ -51,27 +72,33 @@ module HybridPlatformsConductorTest
51
72
  elsif node_connection_info[:control_master_create_error].nil?
52
73
  # Really touch a fake control file, as ssh connector checks for its existence
53
74
  File.write(control_file, '')
75
+ control_master_created = true
76
+ # If there is no Session Exec, this is done in a separate thread.
77
+ # So keep it alive until the user wants to stop it (which is done using an ssh -O exit command).
78
+ loop { sleep 0.1 } unless with_session_exec
54
79
  [0, '', '']
55
80
  else
56
81
  [255, '', node_connection_info[:control_master_create_error]]
57
82
  end
58
- end
83
+ end,
84
+ { optional: with_control_master_create_optional }
59
85
  ]
60
86
  end
61
87
  if with_control_master_check
62
88
  ssh_commands_per_connection << [
63
- /^.+\/ssh -O check #{Regexp.escape(node_connection_info[:user])}@hpc\.#{Regexp.escape(node)}$/,
89
+ /^.+\/ssh -O check hpc\.#{Regexp.escape(node)}$/,
64
90
  proc { [0, '', ''] }
65
91
  ]
66
92
  end
67
93
  if with_control_master_destroy
68
94
  ssh_commands_per_connection << [
69
- /^.+\/ssh -O exit #{Regexp.escape(node_connection_info[:user])}@hpc\.#{Regexp.escape(node)} 2>&1 \| grep -v 'Exit request sent\.'$/,
95
+ /^.+\/ssh -O exit hpc\.#{Regexp.escape(node)} 2>&1 \| grep -v 'Exit request sent\.'$/,
70
96
  proc do
71
97
  # Really mock the control file deletion
72
98
  File.unlink(test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], '22', node_connection_info[:user]))
73
99
  [1, '', '']
74
- end
100
+ end,
101
+ { optional: with_control_master_destroy_optional }
75
102
  ]
76
103
  end
77
104
  ssh_commands_once + ssh_commands_per_connection * node_connection_info[:times]
@@ -95,6 +122,7 @@ module HybridPlatformsConductorTest
95
122
  # * *timeout* (Integer or nil): Timeout to prepare the connector for [default: nil]
96
123
  # * *password* (String or nil): Password to set for the node, or nil for none [default: nil]
97
124
  # * *additional_config* (String): Additional config [default: '']
125
+ # * *session_exec* (Boolean): Do we mock a node having an SSH connection accepting Session Exec? [default: true]
98
126
  # * Proc: Client code to execute testing
99
127
  def with_test_platform_for_remote_testing(
100
128
  expected_cmds: [],
@@ -102,9 +130,14 @@ module HybridPlatformsConductorTest
102
130
  expected_stderr: '',
103
131
  timeout: nil,
104
132
  password: nil,
105
- additional_config: ''
133
+ additional_config: '',
134
+ session_exec: true
106
135
  )
107
- with_test_platform({ nodes: { 'node' => { meta: { host_ip: '192.168.42.42' } } } }, false, additional_config) do
136
+ with_test_platform(
137
+ { nodes: { 'node' => { meta: { host_ip: '192.168.42.42', ssh_session_exec: session_exec ? 'true' : 'false' } } } },
138
+ false,
139
+ additional_config
140
+ ) do
108
141
  with_cmd_runner_mocked(
109
142
  [
110
143
  ['which env', proc { [0, "/usr/bin/env\n", ''] }],
@@ -113,7 +146,8 @@ module HybridPlatformsConductorTest
113
146
  (password ? [['sshpass -V', proc { [0, "sshpass 1.06\n", ''] }]] : []) +
114
147
  ssh_expected_commands_for(
115
148
  { 'node' => { connection: '192.168.42.42', user: 'test_user' } },
116
- with_batch_mode: password.nil?
149
+ with_batch_mode: password.nil?,
150
+ with_session_exec: session_exec
117
151
  ) +
118
152
  expected_cmds
119
153
  ) do
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.5.0
4
+ version: 32.7.3
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-10 00:00:00.000000000 Z
11
+ date: 2021-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: range_operators
@@ -463,7 +463,7 @@ files:
463
463
  - spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/platform_handlers_spec.rb
464
464
  - spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb
465
465
  - spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb
466
- - spec/hybrid_platforms_conductor_test/api/nodes_handler/config_spec.rb
466
+ - spec/hybrid_platforms_conductor_test/api/nodes_handler/config_dsl_spec.rb
467
467
  - spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb
468
468
  - spec/hybrid_platforms_conductor_test/api/nodes_handler/nodes_selectors_spec.rb
469
469
  - spec/hybrid_platforms_conductor_test/api/nodes_handler/platform_handlers_plugins_api_spec.rb