hybrid_platforms_conductor 33.4.0 → 33.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/README.md +5 -5
  4. data/docs/config_dsl.md +7 -5
  5. data/docs/plugins/cmdb/host_keys.md +3 -1
  6. data/docs/plugins/connector/ssh.md +1 -0
  7. data/lib/hybrid_platforms_conductor/actions_executor.rb +29 -1
  8. data/lib/hybrid_platforms_conductor/bitbucket.rb +2 -2
  9. data/lib/hybrid_platforms_conductor/cmd_runner.rb +4 -4
  10. data/lib/hybrid_platforms_conductor/config.rb +2 -0
  11. data/lib/hybrid_platforms_conductor/confluence.rb +2 -2
  12. data/lib/hybrid_platforms_conductor/connector.rb +5 -2
  13. data/lib/hybrid_platforms_conductor/credentials.rb +20 -12
  14. data/lib/hybrid_platforms_conductor/deployer.rb +5 -7
  15. data/lib/hybrid_platforms_conductor/github.rb +1 -1
  16. data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +1 -1
  17. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +27 -17
  18. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +13 -12
  19. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +6 -4
  20. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +1 -1
  21. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +37 -25
  22. data/lib/hybrid_platforms_conductor/hpc_plugins/log/remote_fs.rb +5 -6
  23. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +23 -14
  24. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +1 -1
  25. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +3 -2
  26. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +1 -1
  27. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +17 -3
  28. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +30 -10
  29. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +1 -1
  30. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +1 -2
  31. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +1 -1
  32. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +1 -2
  33. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +1 -1
  34. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +2 -2
  35. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +1 -2
  36. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +1 -2
  37. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +1 -2
  38. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +1 -1
  39. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +1 -2
  40. data/lib/hybrid_platforms_conductor/logger_helpers.rb +17 -0
  41. data/lib/hybrid_platforms_conductor/test.rb +21 -7
  42. data/lib/hybrid_platforms_conductor/tests_runner.rb +7 -6
  43. data/lib/hybrid_platforms_conductor/thycotic.rb +2 -2
  44. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  45. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +15 -0
  46. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +32 -0
  47. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +87 -0
  48. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +30 -0
  49. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +10 -0
  50. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +38 -0
  51. data/spec/hybrid_platforms_conductor_test/api/actions_executor/helpers_spec.rb +195 -0
  52. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +14 -0
  53. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +11 -0
  54. data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +8 -4
  55. data/spec/hybrid_platforms_conductor_test/api/deployer/log_plugins/remote_fs_spec.rb +215 -0
  56. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +49 -10
  57. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +64 -16
  58. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +5 -3
  59. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/chef_versions.yml +3 -0
  60. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/nodes/node.json +15 -0
  61. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/policyfiles/test_policy.rb +3 -0
  62. data/spec/hybrid_platforms_conductor_test/shared_examples/deployer.rb +134 -0
  63. data/spec/hybrid_platforms_conductor_test/test_connector.rb +2 -2
  64. metadata +20 -2
@@ -175,6 +175,16 @@ describe HybridPlatformsConductor::ActionsExecutor do
175
175
  end
176
176
  end
177
177
 
178
+ it 'generates a simple config for a node with host_ip and a given SSH port' do
179
+ with_test_platform({ nodes: { 'node' => { meta: { host_ip: '192.168.42.42', ssh_port: 666 } } } }) do
180
+ expect(ssh_config_for('node')).to eq <<~EO_SSH_CONFIG
181
+ Host hpc.node
182
+ Hostname 192.168.42.42
183
+ Port 666
184
+ EO_SSH_CONFIG
185
+ end
186
+ end
187
+
178
188
  it 'generates a simple config for several nodes' do
179
189
  with_test_platform(
180
190
  {
@@ -13,6 +13,15 @@ describe HybridPlatformsConductor::ActionsExecutor do
13
13
  end
14
14
  end
15
15
 
16
+ it 'executes bash commands remotely from a SecretString' do
17
+ with_test_platform_for_remote_testing(
18
+ expected_cmds: [[%r{.+/ssh hpc\.node /bin/bash <<'HPC_EOF'\nbash_cmd.bash\nHPC_EOF}, proc { [0, 'Bash commands executed on node', ''] }]],
19
+ expected_stdout: 'Bash commands executed on node'
20
+ ) do
21
+ test_connector.remote_bash(SecretString.new('bash_cmd.bash', silenced_str: '__INVALID_BASH__'))
22
+ end
23
+ end
24
+
16
25
  it 'executes bash commands remotely with timeout' do
17
26
  with_test_platform_for_remote_testing(
18
27
  expected_cmds: [
@@ -88,6 +97,25 @@ describe HybridPlatformsConductor::ActionsExecutor do
88
97
  end
89
98
  end
90
99
 
100
+ it 'executes really big bash commands remotely using a SecretString' do
101
+ cmd = "echo #{'1' * 131_060}"
102
+ with_test_platform_for_remote_testing(
103
+ expected_cmds: [
104
+ [
105
+ %r{.+/hpc_temp_cmds_.+\.sh$},
106
+ proc do |received_cmd|
107
+ expect(File.read(received_cmd)).to match(%r{.+/ssh hpc\.node /bin/bash <<'HPC_EOF'\n#{Regexp.escape(cmd)}\nHPC_EOF})
108
+ [0, 'Bash commands executed on node', '']
109
+ end
110
+ ]
111
+ ],
112
+ expected_stdout: 'Bash commands executed on node'
113
+ ) do
114
+ # Use an argument that exceeds the max arg length limit
115
+ test_connector.remote_bash(SecretString.new(cmd, silenced_str: '__INVALID_BASH__'))
116
+ end
117
+ end
118
+
91
119
  it 'copies files remotely with sudo' do
92
120
  with_test_platform_for_remote_testing(
93
121
  expected_cmds: [
@@ -153,6 +181,16 @@ describe HybridPlatformsConductor::ActionsExecutor do
153
181
  end
154
182
  end
155
183
 
184
+ it 'executes bash commands remotely without Session Exec capabilities using a SecretString' do
185
+ with_test_platform_for_remote_testing(
186
+ expected_cmds: [[%r{^\{ cat \| .+/ssh hpc\.node -T; \} <<'HPC_EOF'\nbash_cmd.bash\nHPC_EOF$}, proc { [0, 'Bash commands executed on node', ''] }]],
187
+ expected_stdout: 'Bash commands executed on node',
188
+ session_exec: false
189
+ ) do
190
+ test_connector.remote_bash(SecretString.new('bash_cmd.bash', silenced_str: '__INVALID_BASH__'))
191
+ end
192
+ end
193
+
156
194
  it 'copies files remotely without Session Exec capabilities' do
157
195
  with_test_platform_for_remote_testing(
158
196
  expected_cmds: [
@@ -0,0 +1,195 @@
1
+ describe HybridPlatformsConductor::ActionsExecutor do
2
+
3
+ context 'when checking helpers' do
4
+
5
+ it 'gives access to connectors' do
6
+ with_test_platform({}) do
7
+ expect(test_actions_executor.connector(:ssh)).not_to be_nil
8
+ end
9
+ end
10
+
11
+ it 'returns if a user has privileged access on a node' do
12
+ with_test_platform({ nodes: { 'node' => {} } }) do
13
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
14
+ expect(test_actions_executor.privileged_access?('node')).to eq false
15
+ end
16
+ end
17
+
18
+ it 'returns if a user has privileged access on a node when connecting with root' do
19
+ with_test_platform({ nodes: { 'node' => {} } }) do
20
+ test_actions_executor.connector(:ssh).ssh_user = 'root'
21
+ expect(test_actions_executor.privileged_access?('node')).to eq true
22
+ end
23
+ end
24
+
25
+ it 'returns if a user has privileged access on a local node' do
26
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true } } } }) do
27
+ with_cmd_runner_mocked [
28
+ ['whoami', proc { [0, 'test_user', ''] }]
29
+ ] do
30
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
31
+ expect(test_actions_executor.privileged_access?('node')).to eq false
32
+ end
33
+ end
34
+ end
35
+
36
+ it 'returns if a user has privileged access on a local node when local user is root' do
37
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true } } } }) do
38
+ with_cmd_runner_mocked [
39
+ ['whoami', proc { [0, 'root', ''] }]
40
+ ] do
41
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
42
+ expect(test_actions_executor.privileged_access?('node')).to eq true
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'with connection on a remote node' do
48
+
49
+ it 'returns the correct sudo prefix' do
50
+ with_test_platform({ nodes: { 'node' => {} } }) do
51
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
52
+ expect(test_actions_executor.sudo_prefix('node')).to eq 'sudo -u root '
53
+ end
54
+ end
55
+
56
+ it 'returns the correct sudo prefix with env forwarding' do
57
+ with_test_platform({ nodes: { 'node' => {} } }) do
58
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
59
+ expect(test_actions_executor.sudo_prefix('node', forward_env: true)).to eq 'sudo -u root -E '
60
+ end
61
+ end
62
+
63
+ it 'returns the correct sudo prefix when connecting as root' do
64
+ with_test_platform({ nodes: { 'node' => {} } }) do
65
+ test_actions_executor.connector(:ssh).ssh_user = 'root'
66
+ expect(test_actions_executor.sudo_prefix('node')).to eq ''
67
+ end
68
+ end
69
+
70
+ it 'returns the correct sudo prefix with a different sudo' do
71
+ with_test_platform(
72
+ { nodes: { 'node' => {} } },
73
+ additional_config: <<~'EO_CONFIG'
74
+ sudo_for { |user| "other_sudo --user #{user}" }
75
+ EO_CONFIG
76
+ ) do
77
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
78
+ expect(test_actions_executor.sudo_prefix('node')).to eq 'other_sudo --user root '
79
+ end
80
+ end
81
+
82
+ it 'returns the correct sudo prefix with a different sudo and env forwarding' do
83
+ with_test_platform(
84
+ { nodes: { 'node' => {} } },
85
+ additional_config: <<~'EO_CONFIG'
86
+ sudo_for { |user| "other_sudo --user #{user}" }
87
+ EO_CONFIG
88
+ ) do
89
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
90
+ expect(test_actions_executor.sudo_prefix('node', forward_env: true)).to eq 'other_sudo --user root -E '
91
+ end
92
+ end
93
+
94
+ it 'returns the correct sudo prefix with a different sudo when connecting as root' do
95
+ with_test_platform(
96
+ { nodes: { 'node' => {} } },
97
+ additional_config: <<~'EO_CONFIG'
98
+ sudo_for { |user| "other_sudo --user #{user}" }
99
+ EO_CONFIG
100
+ ) do
101
+ test_actions_executor.connector(:ssh).ssh_user = 'root'
102
+ expect(test_actions_executor.sudo_prefix('node')).to eq ''
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ context 'with connection on a local node' do
109
+
110
+ it 'returns the correct sudo prefix' do
111
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true } } } }) do
112
+ with_cmd_runner_mocked [
113
+ ['whoami', proc { [0, 'test_user', ''] }]
114
+ ] do
115
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
116
+ expect(test_actions_executor.sudo_prefix('node')).to eq 'sudo -u root '
117
+ end
118
+ end
119
+ end
120
+
121
+ it 'returns the correct sudo prefix with env forwarding' do
122
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true } } } }) do
123
+ with_cmd_runner_mocked [
124
+ ['whoami', proc { [0, 'test_user', ''] }]
125
+ ] do
126
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
127
+ expect(test_actions_executor.sudo_prefix('node', forward_env: true)).to eq 'sudo -u root -E '
128
+ end
129
+ end
130
+ end
131
+
132
+ it 'returns the correct sudo prefix when connecting as root' do
133
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true } } } }) do
134
+ with_cmd_runner_mocked [
135
+ ['whoami', proc { [0, 'root', ''] }]
136
+ ] do
137
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
138
+ expect(test_actions_executor.sudo_prefix('node')).to eq ''
139
+ end
140
+ end
141
+ end
142
+
143
+ it 'returns the correct sudo prefix with a different sudo' do
144
+ with_test_platform(
145
+ { nodes: { 'node' => { meta: { local_node: true } } } },
146
+ additional_config: <<~'EO_CONFIG'
147
+ sudo_for { |user| "other_sudo --user #{user}" }
148
+ EO_CONFIG
149
+ ) do
150
+ with_cmd_runner_mocked [
151
+ ['whoami', proc { [0, 'test_user', ''] }]
152
+ ] do
153
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
154
+ expect(test_actions_executor.sudo_prefix('node')).to eq 'other_sudo --user root '
155
+ end
156
+ end
157
+ end
158
+
159
+ it 'returns the correct sudo prefix with a different sudo and env forwarding' do
160
+ with_test_platform(
161
+ { nodes: { 'node' => { meta: { local_node: true } } } },
162
+ additional_config: <<~'EO_CONFIG'
163
+ sudo_for { |user| "other_sudo --user #{user}" }
164
+ EO_CONFIG
165
+ ) do
166
+ with_cmd_runner_mocked [
167
+ ['whoami', proc { [0, 'test_user', ''] }]
168
+ ] do
169
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
170
+ expect(test_actions_executor.sudo_prefix('node', forward_env: true)).to eq 'other_sudo --user root -E '
171
+ end
172
+ end
173
+ end
174
+
175
+ it 'returns the correct sudo prefix with a different sudo when connecting as root' do
176
+ with_test_platform(
177
+ { nodes: { 'node' => { meta: { local_node: true } } } },
178
+ additional_config: <<~'EO_CONFIG'
179
+ sudo_for { |user| "other_sudo --user #{user}" }
180
+ EO_CONFIG
181
+ ) do
182
+ with_cmd_runner_mocked [
183
+ ['whoami', proc { [0, 'root', ''] }]
184
+ ] do
185
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
186
+ expect(test_actions_executor.sudo_prefix('node')).to eq ''
187
+ end
188
+ end
189
+ end
190
+
191
+ end
192
+
193
+ end
194
+
195
+ end
@@ -7,6 +7,13 @@ describe HybridPlatformsConductor::CmdRunner do
7
7
  end
8
8
  end
9
9
 
10
+ it 'runs a simple bash command in a SecretString' do
11
+ with_repository do |repository|
12
+ test_cmd_runner.run_cmd SecretString.new("echo TestContent >#{repository}/test_file", silenced_str: '__INVALID_BASH__')
13
+ expect(File.read("#{repository}/test_file")).to eq "TestContent\n"
14
+ end
15
+ end
16
+
10
17
  it 'runs a simple bash command and returns exit code, stdout and stderr correctly' do
11
18
  with_repository do
12
19
  expect(test_cmd_runner.run_cmd('echo TestStderr 1>&2 ; echo TestStdout')).to eq [0, "TestStdout\n", "TestStderr\n"]
@@ -20,6 +27,13 @@ describe HybridPlatformsConductor::CmdRunner do
20
27
  end
21
28
  end
22
29
 
30
+ it 'runs a simple bash command and forces usage of bash in a SecretString' do
31
+ with_repository do
32
+ # Use set -o pipefail that does not work in /bin/sh
33
+ expect(test_cmd_runner.run_cmd(SecretString.new('set -o pipefail ; echo TestStderr 1>&2 ; echo TestStdout', silenced_str: '__INVALID_BASH__'), force_bash: true)).to eq [0, "TestStdout\n", "TestStderr\n"]
34
+ end
35
+ end
36
+
23
37
  it 'runs a simple bash command and logs stdout and stderr to a file' do
24
38
  with_repository do |repository|
25
39
  test_cmd_runner.run_cmd 'echo TestStderr 1>&2 ; sleep 1 ; echo TestStdout', log_to_file: "#{repository}/test_file"
@@ -29,6 +29,17 @@ describe HybridPlatformsConductor::Config do
29
29
  end
30
30
  end
31
31
 
32
+ it 'can check if we are in debug mode' do
33
+ with_platforms(
34
+ <<~EO_CONFIG
35
+ os_image :image_1, '/path/to/image_1' if log_debug?
36
+ os_image :image_2, '/path/to/image_2'
37
+ EO_CONFIG
38
+ ) do
39
+ expect(test_config.known_os_images.sort).to eq %i[image_2].sort
40
+ end
41
+ end
42
+
32
43
  it 'returns the tests provisioner correctly' do
33
44
  with_platforms 'tests_provisioner :test_provisioner' do
34
45
  expect(test_config.tests_provisioner_id).to eq :test_provisioner
@@ -15,15 +15,19 @@ describe HybridPlatformsConductor::Credentials do
15
15
  # * *resource* (String or nil): The resource for which we query the credentials, or nil if none [default: nil]
16
16
  def expect_credentials_to_be(expected_user, expected_password, resource: nil)
17
17
  creds = {}
18
+ password_class = nil
18
19
  credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
19
20
  with_credentials_for(:test_credential, resource: resource) do |user, password|
21
+ password_class = password.class
20
22
  creds = {
21
23
  user: user,
22
24
  # We clone the value as for security reasons it is removed when exiting the block
23
- password: password.clone
25
+ password: password&.to_unprotected.clone
24
26
  }
25
27
  end
26
28
  end
29
+ # Make sure we always return a SecretString for the password
30
+ expect(password_class).to be SecretString unless password_class == NilClass
27
31
  expect(creds).to eq(
28
32
  user: expected_user,
29
33
  password: expected_password
@@ -64,7 +68,7 @@ describe HybridPlatformsConductor::Credentials do
64
68
  leaked_password = password
65
69
  end
66
70
  end
67
- expect(leaked_password).to eq 'gotyou!' * 100
71
+ expect(leaked_password.to_unprotected).to eq "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
68
72
  ensure
69
73
  ENV.delete('hpc_user_for_test_credential')
70
74
  ENV.delete('hpc_password_for_test_credential')
@@ -96,7 +100,7 @@ describe HybridPlatformsConductor::Credentials do
96
100
  end
97
101
  end
98
102
 
99
- it 'erases the value of the password taken from netrc' do
103
+ it 'erases the value of the password taken from netrc after usage' do
100
104
  with_platforms '' do
101
105
  netrc_data = [['mocked_data']]
102
106
  expect(::Netrc).to receive(:read) do
@@ -111,7 +115,7 @@ describe HybridPlatformsConductor::Credentials do
111
115
  leaked_password = password
112
116
  end
113
117
  end
114
- expect(leaked_password).to eq 'gotyou!' * 100
118
+ expect(leaked_password).to eq "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
115
119
  expect(netrc_data).to eq [['GotYou!!!' * 100]]
116
120
  end
117
121
  end
@@ -129,6 +129,127 @@ describe HybridPlatformsConductor::Deployer do
129
129
  end
130
130
  end
131
131
 
132
+ it 'returns actions to save logs on a local node' do
133
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true }, services: %w[service1 service2] } } }, additional_config: 'send_logs_to :remote_fs') do
134
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
135
+ expect_services_handler_to_deploy('node' => %w[service1 service2])
136
+ expect_actions_executor_runs [
137
+ # First run, we expect the mutex to be setup, and the deployment actions to be run
138
+ proc do |actions_per_nodes|
139
+ expect_actions_to_deploy_on(
140
+ actions_per_nodes,
141
+ 'node',
142
+ mocked_result: { 'node' => [0, 'Deploy successful stdout', 'Deploy successful stderr'] }
143
+ )
144
+ end,
145
+ # Second run, we expect the mutex to be released
146
+ proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, 'node') },
147
+ # Third run, we expect logs to be uploaded on the node
148
+ proc do |actions_per_nodes|
149
+ expect(actions_per_nodes['node'].size).to eq 3
150
+ expect(actions_per_nodes['node'][0].keys.sort).to eq %i[ruby remote_bash].sort
151
+ expect(actions_per_nodes['node'][0][:remote_bash]).to eq 'sudo -u root mkdir -p /var/log/deployments && sudo -u root chmod 600 /var/log/deployments'
152
+ expect(actions_per_nodes['node'][1].keys.sort).to eq %i[scp].sort
153
+ expect(actions_per_nodes['node'][1][:scp].delete(:sudo)).to eq true
154
+ expect(actions_per_nodes['node'][1][:scp].delete(:owner)).to eq 'root'
155
+ expect(actions_per_nodes['node'][1][:scp].delete(:group)).to eq 'root'
156
+ expect(actions_per_nodes['node'][1][:scp].size).to eq 1
157
+ tmp_log_file = actions_per_nodes['node'][1][:scp].first[0]
158
+ expect(actions_per_nodes['node'][1][:scp].first[1]).to eq '/var/log/deployments'
159
+ expect(actions_per_nodes['node'][2].keys.sort).to eq %i[ruby remote_bash].sort
160
+ expect(actions_per_nodes['node'][2][:remote_bash]).to eq "sudo -u root chmod 600 /var/log/deployments/#{File.basename(tmp_log_file)}"
161
+ # Call the Ruby codes to be tested
162
+ actions_per_nodes['node'][0][:ruby].call
163
+ expect(File.exist?(tmp_log_file)).to eq true
164
+ file_content_regexp = Regexp.new <<~EOREGEXP
165
+ repo_name_0: platform
166
+ commit_id_0: 123456
167
+ commit_message_0: Test commit for node: service1, service2
168
+ date: \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}
169
+ user: test_user
170
+ debug: No
171
+ services: service1, service2
172
+ exit_status: 0
173
+ ===== STDOUT =====
174
+ Deploy successful stdout
175
+ ===== STDERR =====
176
+ Deploy successful stderr
177
+ EOREGEXP
178
+ expect(File.read(tmp_log_file)).to match file_content_regexp
179
+ actions_per_nodes['node'][2][:ruby].call
180
+ # Check temporary log file gets deleted for security reasons
181
+ expect(File.exist?(tmp_log_file)).to eq false
182
+ end
183
+ ]
184
+ with_cmd_runner_mocked [
185
+ ['whoami', proc { [0, 'test_user', ''] }]
186
+ ] do
187
+ expect(test_deployer.deploy_on('node')).to eq('node' => [0, 'Deploy successful stdout', 'Deploy successful stderr'])
188
+ end
189
+ end
190
+ end
191
+
192
+ it 'returns actions to save logs on a local node as root' do
193
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true }, services: %w[service1 service2] } } }, additional_config: 'send_logs_to :remote_fs') do
194
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
195
+ expect_services_handler_to_deploy('node' => %w[service1 service2])
196
+ expect_actions_executor_runs [
197
+ # First run, we expect the mutex to be setup, and the deployment actions to be run
198
+ proc do |actions_per_nodes|
199
+ expect_actions_to_deploy_on(
200
+ actions_per_nodes,
201
+ 'node',
202
+ sudo: nil,
203
+ mocked_result: { 'node' => [0, 'Deploy successful stdout', 'Deploy successful stderr'] }
204
+ )
205
+ end,
206
+ # Second run, we expect the mutex to be released
207
+ proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, 'node', sudo: nil) },
208
+ # Third run, we expect logs to be uploaded on the node
209
+ proc do |actions_per_nodes|
210
+ expect(actions_per_nodes['node'].size).to eq 3
211
+ expect(actions_per_nodes['node'][0].keys.sort).to eq %i[ruby remote_bash].sort
212
+ expect(actions_per_nodes['node'][0][:remote_bash]).to eq 'mkdir -p /var/log/deployments && chmod 600 /var/log/deployments'
213
+ expect(actions_per_nodes['node'][1].keys.sort).to eq %i[scp].sort
214
+ expect(actions_per_nodes['node'][1][:scp].delete(:sudo)).to eq false
215
+ expect(actions_per_nodes['node'][1][:scp].delete(:owner)).to eq 'root'
216
+ expect(actions_per_nodes['node'][1][:scp].delete(:group)).to eq 'root'
217
+ expect(actions_per_nodes['node'][1][:scp].size).to eq 1
218
+ tmp_log_file = actions_per_nodes['node'][1][:scp].first[0]
219
+ expect(actions_per_nodes['node'][1][:scp].first[1]).to eq '/var/log/deployments'
220
+ expect(actions_per_nodes['node'][2].keys.sort).to eq %i[ruby remote_bash].sort
221
+ expect(actions_per_nodes['node'][2][:remote_bash]).to eq "chmod 600 /var/log/deployments/#{File.basename(tmp_log_file)}"
222
+ # Call the Ruby codes to be tested
223
+ actions_per_nodes['node'][0][:ruby].call
224
+ expect(File.exist?(tmp_log_file)).to eq true
225
+ file_content_regexp = Regexp.new <<~EOREGEXP
226
+ repo_name_0: platform
227
+ commit_id_0: 123456
228
+ commit_message_0: Test commit for node: service1, service2
229
+ date: \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}
230
+ user: test_user
231
+ debug: No
232
+ services: service1, service2
233
+ exit_status: 0
234
+ ===== STDOUT =====
235
+ Deploy successful stdout
236
+ ===== STDERR =====
237
+ Deploy successful stderr
238
+ EOREGEXP
239
+ expect(File.read(tmp_log_file)).to match file_content_regexp
240
+ actions_per_nodes['node'][2][:ruby].call
241
+ # Check temporary log file gets deleted for security reasons
242
+ expect(File.exist?(tmp_log_file)).to eq false
243
+ end
244
+ ]
245
+ with_cmd_runner_mocked [
246
+ ['whoami', proc { [0, 'root', ''] }]
247
+ ] do
248
+ expect(test_deployer.deploy_on('node')).to eq('node' => [0, 'Deploy successful stdout', 'Deploy successful stderr'])
249
+ end
250
+ end
251
+ end
252
+
132
253
  it 'reads logs' do
133
254
  with_test_platform_for_remote_fs do
134
255
  expect_actions_executor_runs [
@@ -216,6 +337,100 @@ describe HybridPlatformsConductor::Deployer do
216
337
  end
217
338
  end
218
339
 
340
+ it 'reads logs on a local node' do
341
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true }, services: %w[service1 service2] } } }, additional_config: 'send_logs_to :remote_fs') do
342
+ expect_actions_executor_runs [
343
+ # Expect the actions to get log files
344
+ proc do |actions_per_nodes|
345
+ expect(actions_per_nodes).to eq('node' => [{ remote_bash: 'sudo -u root cat /var/log/deployments/`sudo -u root ls -t /var/log/deployments/ | head -1`' }])
346
+ { 'node' => [0, <<~EO_STDOUT, ''] }
347
+ repo_name_0: platform
348
+ commit_id_0: 123456
349
+ commit_message_0: Test commit for node: service1, service2
350
+ diff_files_0: file1, file2, file3
351
+ date: 2017-11-23 18:43:01
352
+ user: test_user
353
+ debug: Yes
354
+ services: service1, service2, service3
355
+ exit_status: 0
356
+ ===== STDOUT =====
357
+ Deploy successful stdout
358
+ ===== STDERR =====
359
+ Deploy successful stderr
360
+ EO_STDOUT
361
+ end
362
+ ]
363
+ with_cmd_runner_mocked [
364
+ ['whoami', proc { [0, 'test_user', ''] }]
365
+ ] do
366
+ expect(test_deployer.deployment_info_from('node')).to eq(
367
+ 'node' => {
368
+ deployment_info: {
369
+ repo_name_0: 'platform',
370
+ commit_id_0: '123456',
371
+ commit_message_0: 'Test commit for node: service1, service2',
372
+ diff_files_0: %w[file1 file2 file3],
373
+ date: Time.parse('2017-11-23 18:43:01 UTC'),
374
+ debug: true,
375
+ user: 'test_user'
376
+ },
377
+ exit_status: 0,
378
+ services: %w[service1 service2 service3],
379
+ stderr: 'Deploy successful stderr',
380
+ stdout: 'Deploy successful stdout'
381
+ }
382
+ )
383
+ end
384
+ end
385
+ end
386
+
387
+ it 'reads logs on a local node as root' do
388
+ with_test_platform({ nodes: { 'node' => { meta: { local_node: true }, services: %w[service1 service2] } } }, additional_config: 'send_logs_to :remote_fs') do
389
+ expect_actions_executor_runs [
390
+ # Expect the actions to get log files
391
+ proc do |actions_per_nodes|
392
+ expect(actions_per_nodes).to eq('node' => [{ remote_bash: 'cat /var/log/deployments/`ls -t /var/log/deployments/ | head -1`' }])
393
+ { 'node' => [0, <<~EO_STDOUT, ''] }
394
+ repo_name_0: platform
395
+ commit_id_0: 123456
396
+ commit_message_0: Test commit for node: service1, service2
397
+ diff_files_0: file1, file2, file3
398
+ date: 2017-11-23 18:43:01
399
+ user: test_user
400
+ debug: Yes
401
+ services: service1, service2, service3
402
+ exit_status: 0
403
+ ===== STDOUT =====
404
+ Deploy successful stdout
405
+ ===== STDERR =====
406
+ Deploy successful stderr
407
+ EO_STDOUT
408
+ end
409
+ ]
410
+ with_cmd_runner_mocked [
411
+ ['whoami', proc { [0, 'root', ''] }]
412
+ ] do
413
+ expect(test_deployer.deployment_info_from('node')).to eq(
414
+ 'node' => {
415
+ deployment_info: {
416
+ repo_name_0: 'platform',
417
+ commit_id_0: '123456',
418
+ commit_message_0: 'Test commit for node: service1, service2',
419
+ diff_files_0: %w[file1 file2 file3],
420
+ date: Time.parse('2017-11-23 18:43:01 UTC'),
421
+ debug: true,
422
+ user: 'test_user'
423
+ },
424
+ exit_status: 0,
425
+ services: %w[service1 service2 service3],
426
+ stderr: 'Deploy successful stderr',
427
+ stdout: 'Deploy successful stdout'
428
+ }
429
+ )
430
+ end
431
+ end
432
+ end
433
+
219
434
  end
220
435
 
221
436
  end