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.
- checksums.yaml +4 -4
- data/lib/hybrid_platforms_conductor/deployer.rb +12 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +157 -81
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +11 -9
- data/lib/hybrid_platforms_conductor/provisioner.rb +9 -0
- data/lib/hybrid_platforms_conductor/version.rb +1 -1
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +67 -5
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +50 -6
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +1 -1
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +34 -10
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioner_spec.rb +74 -10
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/{config_spec.rb → config_dsl_spec.rb} +4 -4
- data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +2 -1
- data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +33 -11
- data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +46 -12
- metadata +3 -3
data/spec/hybrid_platforms_conductor_test/api/nodes_handler/{config_spec.rb → config_dsl_spec.rb}
RENAMED
|
@@ -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]).
|
|
63
|
-
expect(test_config.sudo_procs[0][:sudo_proc].call('test_user')).
|
|
64
|
-
expect(test_config.sudo_procs[1][:nodes_selectors_stack]).
|
|
65
|
-
expect(test_config.sudo_procs[1][:sudo_proc].call('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
|
-
|
|
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<
|
|
12
|
-
# *
|
|
13
|
-
#
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
74
|
-
|
|
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<
|
|
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 #{
|
|
39
|
-
proc { [0, "#{
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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.
|
|
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-
|
|
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/
|
|
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
|