hybrid_platforms_conductor 32.4.0 → 32.7.0
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/bin/nodes_to_deploy +11 -5
- data/lib/hybrid_platforms_conductor/deployer.rb +9 -8
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +147 -68
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +6 -7
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +7 -6
- data/lib/hybrid_platforms_conductor/nodes_handler.rb +45 -1
- data/lib/hybrid_platforms_conductor/services_handler.rb +9 -13
- 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 +57 -2
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +68 -12
- 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 +47 -9
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/config_dsl_spec.rb +71 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb +10 -0
- data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +25 -0
- data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +1 -5
- data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +37 -9
- data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +14 -14
- data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +70 -11
- data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +1 -1
- data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +2 -2
- metadata +12 -11
data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb
CHANGED
|
@@ -13,7 +13,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
13
13
|
|
|
14
14
|
it 'provides an SSH URL that can be used by other processes to connect to this node' do
|
|
15
15
|
with_test_platform_for_remote_testing do
|
|
16
|
-
expect(test_connector.ssh_url).to eq '
|
|
16
|
+
expect(test_connector.ssh_url).to eq 'hpc.node'
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb
CHANGED
|
@@ -6,7 +6,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
6
6
|
|
|
7
7
|
it 'executes bash commands remotely' do
|
|
8
8
|
with_test_platform_for_remote_testing(
|
|
9
|
-
expected_cmds: [[/.+\/ssh
|
|
9
|
+
expected_cmds: [[/.+\/ssh hpc\.node \/bin\/bash <<'EOF'\nbash_cmd.bash\nEOF/, proc { [0, 'Bash commands executed on node', ''] }]],
|
|
10
10
|
expected_stdout: 'Bash commands executed on node'
|
|
11
11
|
) do
|
|
12
12
|
test_connector.remote_bash('bash_cmd.bash')
|
|
@@ -17,7 +17,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
17
17
|
with_test_platform_for_remote_testing(
|
|
18
18
|
expected_cmds: [
|
|
19
19
|
[
|
|
20
|
-
/.+\/ssh
|
|
20
|
+
/.+\/ssh hpc\.node \/bin\/bash <<'EOF'\nbash_cmd.bash\nEOF/,
|
|
21
21
|
proc 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|
|
|
22
22
|
expect(timeout).to eq 5
|
|
23
23
|
[0, '', '']
|
|
@@ -33,7 +33,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
33
33
|
it 'executes interactive commands remotely' do
|
|
34
34
|
with_test_platform_for_remote_testing do
|
|
35
35
|
expect(test_connector).to receive(:system) do |cmd|
|
|
36
|
-
expect(cmd).to match /^.+\/ssh
|
|
36
|
+
expect(cmd).to match /^.+\/ssh hpc\.node$/
|
|
37
37
|
end
|
|
38
38
|
test_connector.remote_interactive
|
|
39
39
|
end
|
|
@@ -43,7 +43,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
43
43
|
with_test_platform_for_remote_testing(
|
|
44
44
|
expected_cmds: [
|
|
45
45
|
[
|
|
46
|
-
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+src.file \| \/.+\/ssh\s+
|
|
46
|
+
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+src.file \| \/.+\/ssh\s+hpc\.node\s+"tar\s+--extract\s+--gunzip\s+--file -\s+--directory \/remote_path\/to\/dst.dir\s+--owner root\s+"/,
|
|
47
47
|
proc { [0, '', ''] }
|
|
48
48
|
]
|
|
49
49
|
]
|
|
@@ -56,7 +56,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
56
56
|
with_test_platform_for_remote_testing(
|
|
57
57
|
expected_cmds: [
|
|
58
58
|
[
|
|
59
|
-
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+src.file \| \/.+\/ssh\s+
|
|
59
|
+
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+src.file \| \/.+\/ssh\s+hpc\.node\s+"tar\s+--extract\s+--gunzip\s+--file -\s+--directory \/remote_path\/to\/dst.dir\s+--owner root\s+"/,
|
|
60
60
|
proc 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|
|
|
61
61
|
expect(timeout).to eq 5
|
|
62
62
|
[0, '', '']
|
|
@@ -76,7 +76,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
76
76
|
[
|
|
77
77
|
/.+\/hpc_temp_cmds_.+\.sh$/,
|
|
78
78
|
proc do |received_cmd|
|
|
79
|
-
expect(File.read(received_cmd)).to match /.+\/ssh
|
|
79
|
+
expect(File.read(received_cmd)).to match /.+\/ssh hpc\.node \/bin\/bash <<'EOF'\n#{Regexp.escape(cmd)}\nEOF/
|
|
80
80
|
[0, 'Bash commands executed on node', '']
|
|
81
81
|
end
|
|
82
82
|
]
|
|
@@ -92,7 +92,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
92
92
|
with_test_platform_for_remote_testing(
|
|
93
93
|
expected_cmds: [
|
|
94
94
|
[
|
|
95
|
-
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+src.file \| \/.+\/ssh\s+
|
|
95
|
+
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+src.file \| \/.+\/ssh\s+hpc\.node\s+"sudo -u root tar\s+--extract\s+--gunzip\s+--file -\s+--directory \/remote_path\/to\/dst.dir\s+--owner root\s+"/,
|
|
96
96
|
proc { [0, '', ''] }
|
|
97
97
|
]
|
|
98
98
|
]
|
|
@@ -101,11 +101,25 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
it 'copies files remotely with a different sudo' do
|
|
105
|
+
with_test_platform_for_remote_testing(
|
|
106
|
+
expected_cmds: [
|
|
107
|
+
[
|
|
108
|
+
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+src.file \| \/.+\/ssh\s+hpc\.node\s+"other_sudo --user root tar\s+--extract\s+--gunzip\s+--file -\s+--directory \/remote_path\/to\/dst.dir\s+--owner root\s+"/,
|
|
109
|
+
proc { [0, '', ''] }
|
|
110
|
+
]
|
|
111
|
+
],
|
|
112
|
+
additional_config: 'sudo_for { |user| "other_sudo --user #{user}" }'
|
|
113
|
+
) do
|
|
114
|
+
test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir', sudo: true)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
104
118
|
it 'copies files remotely with a different owner' do
|
|
105
119
|
with_test_platform_for_remote_testing(
|
|
106
120
|
expected_cmds: [
|
|
107
121
|
[
|
|
108
|
-
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+--owner remote_user\s+src.file \| \/.+\/ssh\s+
|
|
122
|
+
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+--owner remote_user\s+src.file \| \/.+\/ssh\s+hpc\.node\s+"tar\s+--extract\s+--gunzip\s+--file -\s+--directory \/remote_path\/to\/dst.dir\s+--owner root\s+"/,
|
|
109
123
|
proc { [0, '', ''] }
|
|
110
124
|
]
|
|
111
125
|
]
|
|
@@ -118,7 +132,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
118
132
|
with_test_platform_for_remote_testing(
|
|
119
133
|
expected_cmds: [
|
|
120
134
|
[
|
|
121
|
-
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+--group remote_group\s+src.file \| \/.+\/ssh\s+
|
|
135
|
+
/cd \/path\/to && tar\s+--create\s+--gzip\s+--file -\s+--group remote_group\s+src.file \| \/.+\/ssh\s+hpc\.node\s+"tar\s+--extract\s+--gunzip\s+--file -\s+--directory \/remote_path\/to\/dst.dir\s+--owner root\s+"/,
|
|
122
136
|
proc { [0, '', ''] }
|
|
123
137
|
]
|
|
124
138
|
]
|
|
@@ -127,6 +141,30 @@ describe HybridPlatformsConductor::ActionsExecutor do
|
|
|
127
141
|
end
|
|
128
142
|
end
|
|
129
143
|
|
|
144
|
+
it 'executes bash commands remotely without Session Exec capabilities' do
|
|
145
|
+
with_test_platform_for_remote_testing(
|
|
146
|
+
expected_cmds: [[/^\{ cat \| .+\/ssh hpc\.node -T; } <<'EOF'\nbash_cmd.bash\nEOF$/, proc { [0, 'Bash commands executed on node', ''] }]],
|
|
147
|
+
expected_stdout: 'Bash commands executed on node',
|
|
148
|
+
session_exec: false
|
|
149
|
+
) do
|
|
150
|
+
test_connector.remote_bash('bash_cmd.bash')
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'copies files remotely without Session Exec capabilities' do
|
|
155
|
+
with_test_platform_for_remote_testing(
|
|
156
|
+
expected_cmds: [
|
|
157
|
+
[
|
|
158
|
+
/^scp -S .+\/ssh \/path\/to\/src.file hpc\.node:\/remote_path\/to\/dst.dir$/,
|
|
159
|
+
proc { [0, '', ''] }
|
|
160
|
+
]
|
|
161
|
+
],
|
|
162
|
+
session_exec: false
|
|
163
|
+
) do
|
|
164
|
+
test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir')
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
130
168
|
end
|
|
131
169
|
|
|
132
170
|
end
|
|
@@ -124,4 +124,32 @@ describe HybridPlatformsConductor::NodesHandler do
|
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
+
it 'computes the correct sudo for different nodes' do
|
|
128
|
+
with_test_platform(
|
|
129
|
+
{
|
|
130
|
+
nodes: {
|
|
131
|
+
'node1' => {},
|
|
132
|
+
'node2' => {},
|
|
133
|
+
'node3' => {}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
false,
|
|
137
|
+
'
|
|
138
|
+
for_nodes(%w[node1 node2]) do
|
|
139
|
+
sudo_for { |user| "alt_sudo1 -p #{user}" }
|
|
140
|
+
end
|
|
141
|
+
for_nodes(\'node2\') do
|
|
142
|
+
sudo_for { |user| "alt_sudo2 -q #{user}" }
|
|
143
|
+
end
|
|
144
|
+
'
|
|
145
|
+
) do
|
|
146
|
+
expect(test_nodes_handler.sudo_on('node1')).to eq 'alt_sudo1 -p root'
|
|
147
|
+
expect(test_nodes_handler.sudo_on('node1', 'test_user')).to eq 'alt_sudo1 -p test_user'
|
|
148
|
+
expect(test_nodes_handler.sudo_on('node2')).to eq 'alt_sudo2 -q root'
|
|
149
|
+
expect(test_nodes_handler.sudo_on('node2', 'test_user')).to eq 'alt_sudo2 -q test_user'
|
|
150
|
+
expect(test_nodes_handler.sudo_on('node3')).to eq 'sudo -u root'
|
|
151
|
+
expect(test_nodes_handler.sudo_on('node3', 'test_user')).to eq 'sudo -u test_user'
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
127
155
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
describe HybridPlatformsConductor::NodesHandler do
|
|
2
|
+
|
|
3
|
+
context 'checking config DSL' do
|
|
4
|
+
|
|
5
|
+
it 'adds helpers for master cmdbs' do
|
|
6
|
+
with_test_platform(
|
|
7
|
+
{
|
|
8
|
+
nodes: {
|
|
9
|
+
'node1' => {},
|
|
10
|
+
'node2' => {},
|
|
11
|
+
'node3' => {}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
false,
|
|
15
|
+
'
|
|
16
|
+
master_cmdbs(
|
|
17
|
+
test_cmdb: :property1,
|
|
18
|
+
test_cmdb2: :property2
|
|
19
|
+
)
|
|
20
|
+
for_nodes(\'node2\') do
|
|
21
|
+
master_cmdbs(test_cmdb: :property3)
|
|
22
|
+
end
|
|
23
|
+
'
|
|
24
|
+
) do
|
|
25
|
+
register_test_cmdb(%i[test_cmdb test_cmdb2])
|
|
26
|
+
expect(test_config.cmdb_masters).to eq [
|
|
27
|
+
{
|
|
28
|
+
nodes_selectors_stack: [],
|
|
29
|
+
cmdb_masters: {
|
|
30
|
+
test_cmdb: [:property1],
|
|
31
|
+
test_cmdb2: [:property2]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
nodes_selectors_stack: ['node2'],
|
|
36
|
+
cmdb_masters: {
|
|
37
|
+
test_cmdb: [:property3]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'adds helpers for configurable sudo' do
|
|
45
|
+
with_test_platform(
|
|
46
|
+
{
|
|
47
|
+
nodes: {
|
|
48
|
+
'node1' => {},
|
|
49
|
+
'node2' => {},
|
|
50
|
+
'node3' => {}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
false,
|
|
54
|
+
'
|
|
55
|
+
sudo_for { |user| "alt_sudo1 -p #{user}" }
|
|
56
|
+
for_nodes(\'node2\') do
|
|
57
|
+
sudo_for { |user| "alt_sudo2 -q #{user}" }
|
|
58
|
+
end
|
|
59
|
+
'
|
|
60
|
+
) do
|
|
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'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
@@ -33,6 +33,16 @@ describe HybridPlatformsConductor::NodesHandler do
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
it 'fails when the commit id is invalid' do
|
|
37
|
+
with_test_platform({}, true) do
|
|
38
|
+
with_cmd_runner_mocked([
|
|
39
|
+
[/cd .+\/my_remote_platform && git --no-pager diff --no-color invalid_id/, proc { raise HybridPlatformsConductor::CmdRunner::UnexpectedExitCodeError, 'Mocked git error due to an invalid commit id' }]
|
|
40
|
+
]) do
|
|
41
|
+
expect { test_nodes_handler.impacted_nodes_from_git_diff('my_remote_platform', from_commit: 'invalid_id') }.to raise_error HybridPlatformsConductor::NodesHandler::GitError, 'Mocked git error due to an invalid commit id'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
36
46
|
it 'diffs to another commit if asked' do
|
|
37
47
|
with_test_platform({}, true) do
|
|
38
48
|
with_cmd_runner_mocked([
|
|
@@ -106,6 +106,31 @@ describe 'nodes_to_deploy executable' do
|
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
it 'considers nodes having invalid commit ids in their logs to be deployed' do
|
|
110
|
+
with_test_platform_for_nodes_to_deploy do
|
|
111
|
+
expect_actions_executor_runs([proc do |actions|
|
|
112
|
+
expect(actions).to eq(
|
|
113
|
+
'node1' => { remote_bash: "cd /var/log/deployments && ls -t | head -1 | xargs sed '/===== STDOUT =====/q'" },
|
|
114
|
+
'node2' => { remote_bash: "cd /var/log/deployments && ls -t | head -1 | xargs sed '/===== STDOUT =====/q'" }
|
|
115
|
+
)
|
|
116
|
+
{
|
|
117
|
+
'node1' => [0, "repo_name_0: platform\nexit_status: 0\ncommit_id_0: abcdef1", ''],
|
|
118
|
+
'node2' => [0, "repo_name_0: platform\nexit_status: 0\ncommit_id_0: abcdef2", '']
|
|
119
|
+
}
|
|
120
|
+
end])
|
|
121
|
+
expect(test_nodes_handler).to receive(:impacted_nodes_from_git_diff).with('platform', from_commit: 'abcdef1', to_commit: 'master') do
|
|
122
|
+
raise HybridPlatformsConductor::NodesHandler::GitError, 'Mocked git error due to an invalid commit id'
|
|
123
|
+
end
|
|
124
|
+
expect(test_nodes_handler).to receive(:impacted_nodes_from_git_diff).with('platform', from_commit: 'abcdef2', to_commit: 'master') { [%w[], [], [], false] }
|
|
125
|
+
exit_code, stdout, stderr = run 'nodes_to_deploy'
|
|
126
|
+
expect(exit_code).to eq 0
|
|
127
|
+
expect(stdout).to eq <<~EOS
|
|
128
|
+
===== Nodes to deploy =====
|
|
129
|
+
node1
|
|
130
|
+
EOS
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
109
134
|
it 'ignores impacts if asked' do
|
|
110
135
|
with_test_platform_for_nodes_to_deploy do
|
|
111
136
|
exit_code, stdout, stderr = run 'nodes_to_deploy', '--ignore-deployed-info'
|
|
@@ -14,7 +14,6 @@ module HybridPlatformsConductorTest
|
|
|
14
14
|
# * *cmd_runner* (CmdRunner): The CmdRunner to mock [default: test_cmd_runner]
|
|
15
15
|
# * Proc: Code called with the command runner mocked
|
|
16
16
|
def with_cmd_runner_mocked(commands, cmd_runner: test_cmd_runner)
|
|
17
|
-
unexpected_commands = []
|
|
18
17
|
remaining_expected_commands = commands.clone
|
|
19
18
|
# We need to protect the access to this array as the mocked commands can be called by competing threads
|
|
20
19
|
remaining_expected_commands_mutex = Mutex.new
|
|
@@ -64,13 +63,10 @@ module HybridPlatformsConductorTest
|
|
|
64
63
|
log_stderr_to_io << mocked_stderr if !mocked_stderr.empty? && !log_stderr_to_io.nil?
|
|
65
64
|
[mocked_exit_status, mocked_stdout, mocked_stderr]
|
|
66
65
|
else
|
|
67
|
-
|
|
68
|
-
unexpected_commands << cmd
|
|
69
|
-
[:unexpected_command_to_mock, '', "Could not mock unexpected command #{cmd}"]
|
|
66
|
+
raise "Unexpected command run: #{cmd}"
|
|
70
67
|
end
|
|
71
68
|
end
|
|
72
69
|
yield
|
|
73
|
-
expect(unexpected_commands).to eq []
|
|
74
70
|
expect(remaining_expected_commands).to eq([]), "Expected CmdRunner commands were not run:\n#{remaining_expected_commands.map(&:first).join("\n")}"
|
|
75
71
|
# Un-mock the command runner
|
|
76
72
|
allow(cmd_runner).to receive(:run_cmd).and_call_original
|
|
@@ -10,6 +10,7 @@ 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]
|
|
@@ -18,6 +19,7 @@ module HybridPlatformsConductorTest
|
|
|
18
19
|
# * *with_control_master_destroy* (Boolean): Do we destroy the control master? [default: true]
|
|
19
20
|
# * *with_strict_host_key_checking* (Boolean): Do we use strict host key checking? [default: true]
|
|
20
21
|
# * *with_batch_mode* (Boolean): Do we use BatchMode when creating the control master? [default: true]
|
|
22
|
+
# * *with_session_exec* (Boolean): Do we use Sessien Exec capabilities when creating the control master? [default: true]
|
|
21
23
|
# Result::
|
|
22
24
|
# * Array< [String or Regexp, Proc] >: The expected commands that should be used, and their corresponding mocked code
|
|
23
25
|
def ssh_expected_commands_for(
|
|
@@ -26,23 +28,31 @@ module HybridPlatformsConductorTest
|
|
|
26
28
|
with_control_master_check: false,
|
|
27
29
|
with_control_master_destroy: true,
|
|
28
30
|
with_strict_host_key_checking: true,
|
|
29
|
-
with_batch_mode: true
|
|
31
|
+
with_batch_mode: true,
|
|
32
|
+
with_session_exec: true
|
|
30
33
|
)
|
|
31
34
|
nodes_connections.map do |node, node_connection_info|
|
|
32
35
|
node_connection_info[:times] = 1 unless node_connection_info.key?(:times)
|
|
33
36
|
ssh_commands_once = []
|
|
34
37
|
ssh_commands_per_connection = []
|
|
35
38
|
if with_strict_host_key_checking
|
|
39
|
+
ip = node_connection_info[:ip] || node_connection_info[:connection]
|
|
36
40
|
ssh_commands_once.concat([
|
|
37
41
|
[
|
|
38
|
-
"ssh-keyscan #{
|
|
39
|
-
proc { [0, "#{
|
|
42
|
+
"ssh-keyscan #{ip}",
|
|
43
|
+
proc { [0, "#{ip} ssh-rsa fake_host_key_for_#{ip}", ''] }
|
|
40
44
|
]
|
|
41
45
|
])
|
|
42
46
|
end
|
|
43
47
|
if with_control_master_create
|
|
44
48
|
ssh_commands_per_connection << [
|
|
45
|
-
|
|
49
|
+
if with_session_exec
|
|
50
|
+
/^.+\/ssh #{with_batch_mode ? '-o BatchMode=yes ' : ''}-o ControlMaster=yes -o ControlPersist=yes hpc\.#{Regexp.escape(node)} true$/
|
|
51
|
+
else
|
|
52
|
+
# Mock the user hitting enter as the Control MAster will be created in another thread and the main thread waits for user input.
|
|
53
|
+
expect($stdin).to receive(:gets) { "\n" }
|
|
54
|
+
/^xterm -e '.+\/ssh -o ControlMaster=yes -o ControlPersist=yes hpc\.#{Regexp.escape(node)}'$/
|
|
55
|
+
end,
|
|
46
56
|
proc do
|
|
47
57
|
control_file = test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], '22', node_connection_info[:user])
|
|
48
58
|
# Fail if the ControlMaster file already exists, as would SSH do if the file is stalled
|
|
@@ -51,6 +61,9 @@ module HybridPlatformsConductorTest
|
|
|
51
61
|
elsif node_connection_info[:control_master_create_error].nil?
|
|
52
62
|
# Really touch a fake control file, as ssh connector checks for its existence
|
|
53
63
|
File.write(control_file, '')
|
|
64
|
+
# If there is no Session Exec, this is done in a separate thread.
|
|
65
|
+
# So keep it alive until the user wants to stop it (which is done using an ssh -O exit command).
|
|
66
|
+
loop { sleep 0.1 } unless with_session_exec
|
|
54
67
|
[0, '', '']
|
|
55
68
|
else
|
|
56
69
|
[255, '', node_connection_info[:control_master_create_error]]
|
|
@@ -60,13 +73,13 @@ module HybridPlatformsConductorTest
|
|
|
60
73
|
end
|
|
61
74
|
if with_control_master_check
|
|
62
75
|
ssh_commands_per_connection << [
|
|
63
|
-
/^.+\/ssh -O check
|
|
76
|
+
/^.+\/ssh -O check hpc\.#{Regexp.escape(node)}$/,
|
|
64
77
|
proc { [0, '', ''] }
|
|
65
78
|
]
|
|
66
79
|
end
|
|
67
80
|
if with_control_master_destroy
|
|
68
81
|
ssh_commands_per_connection << [
|
|
69
|
-
/^.+\/ssh -O exit
|
|
82
|
+
/^.+\/ssh -O exit hpc\.#{Regexp.escape(node)} 2>&1 \| grep -v 'Exit request sent\.'$/,
|
|
70
83
|
proc do
|
|
71
84
|
# Really mock the control file deletion
|
|
72
85
|
File.unlink(test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], '22', node_connection_info[:user]))
|
|
@@ -94,9 +107,23 @@ module HybridPlatformsConductorTest
|
|
|
94
107
|
# * *expected_stderr* (String): Expected stderr after client code execution [default: '']
|
|
95
108
|
# * *timeout* (Integer or nil): Timeout to prepare the connector for [default: nil]
|
|
96
109
|
# * *password* (String or nil): Password to set for the node, or nil for none [default: nil]
|
|
110
|
+
# * *additional_config* (String): Additional config [default: '']
|
|
111
|
+
# * *session_exec* (Boolean): Do we mock a node having an SSH connection accepting Session Exec? [default: true]
|
|
97
112
|
# * Proc: Client code to execute testing
|
|
98
|
-
def with_test_platform_for_remote_testing(
|
|
99
|
-
|
|
113
|
+
def with_test_platform_for_remote_testing(
|
|
114
|
+
expected_cmds: [],
|
|
115
|
+
expected_stdout: '',
|
|
116
|
+
expected_stderr: '',
|
|
117
|
+
timeout: nil,
|
|
118
|
+
password: nil,
|
|
119
|
+
additional_config: '',
|
|
120
|
+
session_exec: true
|
|
121
|
+
)
|
|
122
|
+
with_test_platform(
|
|
123
|
+
{ nodes: { 'node' => { meta: { host_ip: '192.168.42.42', ssh_session_exec: session_exec ? 'true' : 'false' } } } },
|
|
124
|
+
false,
|
|
125
|
+
additional_config
|
|
126
|
+
) do
|
|
100
127
|
with_cmd_runner_mocked(
|
|
101
128
|
[
|
|
102
129
|
['which env', proc { [0, "/usr/bin/env\n", ''] }],
|
|
@@ -105,7 +132,8 @@ module HybridPlatformsConductorTest
|
|
|
105
132
|
(password ? [['sshpass -V', proc { [0, "sshpass 1.06\n", ''] }]] : []) +
|
|
106
133
|
ssh_expected_commands_for(
|
|
107
134
|
{ 'node' => { connection: '192.168.42.42', user: 'test_user' } },
|
|
108
|
-
with_batch_mode: password.nil
|
|
135
|
+
with_batch_mode: password.nil?,
|
|
136
|
+
with_session_exec: session_exec
|
|
109
137
|
) +
|
|
110
138
|
expected_cmds
|
|
111
139
|
) do
|
|
@@ -9,12 +9,12 @@ module HybridPlatformsConductorTest
|
|
|
9
9
|
# Parameters::
|
|
10
10
|
# * *action* (Hash<Symbol,Object>): The action to check
|
|
11
11
|
# * *node* (String): The concerned node
|
|
12
|
-
# * *sudo* (
|
|
13
|
-
def expect_action_to_lock_node(action, node, sudo:
|
|
12
|
+
# * *sudo* (String or nil): sudo supposed to be used, or nil if none [default: 'sudo -u root']
|
|
13
|
+
def expect_action_to_lock_node(action, node, sudo: 'sudo -u root')
|
|
14
14
|
expect(action[:scp].size).to eq 1
|
|
15
15
|
expect(action[:scp].first[0]).to match /^.+\/mutex_dir$/
|
|
16
16
|
expect(action[:scp].first[1]).to eq '.'
|
|
17
|
-
expect(action[:remote_bash]).to eq "while ! #{sudo ?
|
|
17
|
+
expect(action[:remote_bash]).to eq "while ! #{sudo ? "#{sudo} " : ''}./mutex_dir lock /tmp/hybrid_platforms_conductor_deploy_lock \"$(ps -o ppid= -p $$)\"; do echo -e 'Another deployment is running on #{node}. Waiting for it to finish to continue...' ; sleep 5 ; done"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Expect a given action to be releasing the mutex on a given node
|
|
@@ -22,9 +22,9 @@ module HybridPlatformsConductorTest
|
|
|
22
22
|
# Parameters::
|
|
23
23
|
# * *action* (Hash<Symbol,Object>): The action to check
|
|
24
24
|
# * *node* (String): The concerned node
|
|
25
|
-
# * *sudo* (
|
|
26
|
-
def expect_action_to_unlock_node(action, node, sudo:
|
|
27
|
-
expect(action).to eq(remote_bash: "#{sudo ?
|
|
25
|
+
# * *sudo* (String or nil): sudo supposed to be used, or nil if none [default: 'sudo -u root']
|
|
26
|
+
def expect_action_to_unlock_node(action, node, sudo: 'sudo -u root')
|
|
27
|
+
expect(action).to eq(remote_bash: "#{sudo ? "#{sudo} " : ''}./mutex_dir unlock /tmp/hybrid_platforms_conductor_deploy_lock")
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Expect a given set of actions to be a deployment
|
|
@@ -33,12 +33,12 @@ module HybridPlatformsConductorTest
|
|
|
33
33
|
# * *actions* (Object): Actions
|
|
34
34
|
# * *nodes* (String or Array<String>): Node (or list of nodes) that should be checked
|
|
35
35
|
# * *check* (Boolean): Is the deploy only a check? [default: false]
|
|
36
|
-
# * *sudo* (
|
|
36
|
+
# * *sudo* (String or nil): sudo supposed to be used, or nil if none [default: 'sudo -u root']
|
|
37
37
|
# * *expected_actions* (Array<Object>): Additional expected actions [default: []]
|
|
38
38
|
# * *mocked_result* (Hash<String, [Object, String, String]>): Expected result of the actions, per node, or nil for success [default: nil]
|
|
39
39
|
# Result::
|
|
40
40
|
# * Hash<String, [Integer or Symbol, String, String] >: Expected result of those expected actions
|
|
41
|
-
def expect_actions_to_deploy_on(actions, nodes, check: false, sudo:
|
|
41
|
+
def expect_actions_to_deploy_on(actions, nodes, check: false, sudo: 'sudo -u root', expected_actions: [], mocked_result: nil)
|
|
42
42
|
nodes = [nodes] if nodes.is_a?(String)
|
|
43
43
|
mocked_result = Hash[nodes.map { |node| [node, [0, "#{check ? 'Check' : 'Deploy'} successful", '']] }] if mocked_result.nil?
|
|
44
44
|
expect(actions.size).to eq nodes.size
|
|
@@ -57,8 +57,8 @@ module HybridPlatformsConductorTest
|
|
|
57
57
|
# Parameters::
|
|
58
58
|
# * *actions* (Object): Actions
|
|
59
59
|
# * *nodes* (String or Array<String>): Node (or list of nodes) that should be checked
|
|
60
|
-
# * *sudo* (
|
|
61
|
-
def expect_actions_to_unlock(actions, nodes, sudo:
|
|
60
|
+
# * *sudo* (String or nil): sudo supposed to be used, or nil if none [default: 'sudo -u root']
|
|
61
|
+
def expect_actions_to_unlock(actions, nodes, sudo: 'sudo -u root')
|
|
62
62
|
nodes = [nodes] if nodes.is_a?(String)
|
|
63
63
|
expect(actions.size).to eq nodes.size
|
|
64
64
|
nodes.each do |node|
|
|
@@ -73,17 +73,17 @@ module HybridPlatformsConductorTest
|
|
|
73
73
|
# Parameters::
|
|
74
74
|
# * *actions* (Object): Actions
|
|
75
75
|
# * *nodes* (String or Array<String>): Node (or list of nodes) that should be checked
|
|
76
|
-
# * *sudo* (
|
|
77
|
-
def expect_actions_to_upload_logs(actions, nodes, sudo:
|
|
76
|
+
# * *sudo* (String or nil): sudo supposed to be used, or nil if none [default: 'sudo -u root']
|
|
77
|
+
def expect_actions_to_upload_logs(actions, nodes, sudo: 'sudo -u root')
|
|
78
78
|
nodes = [nodes] if nodes.is_a?(String)
|
|
79
79
|
expect(actions.size).to eq nodes.size
|
|
80
80
|
nodes.each do |node|
|
|
81
81
|
expect(actions.key?(node)).to eq true
|
|
82
|
-
expect(actions[node][:remote_bash]).to eq "#{sudo ?
|
|
82
|
+
expect(actions[node][:remote_bash]).to eq "#{sudo ? "#{sudo} " : ''}mkdir -p /var/log/deployments"
|
|
83
83
|
expect(actions[node][:scp].first[1]).to eq '/var/log/deployments'
|
|
84
84
|
expect(actions[node][:scp][:group]).to eq 'root'
|
|
85
85
|
expect(actions[node][:scp][:owner]).to eq 'root'
|
|
86
|
-
expect(actions[node][:scp][:sudo]).to eq sudo
|
|
86
|
+
expect(actions[node][:scp][:sudo]).to eq (!sudo.nil?)
|
|
87
87
|
end
|
|
88
88
|
Hash[nodes.map { |node| [node, [0, 'Logs uploaded', '']] }]
|
|
89
89
|
end
|