hybrid_platforms_conductor 33.3.0 → 33.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +31 -2
  4. data/docs/config_dsl.md +45 -0
  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 +134 -90
  9. data/lib/hybrid_platforms_conductor/cmd_runner.rb +4 -4
  10. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +12 -44
  11. data/lib/hybrid_platforms_conductor/common_config_dsl/github.rb +9 -31
  12. data/lib/hybrid_platforms_conductor/config.rb +2 -0
  13. data/lib/hybrid_platforms_conductor/confluence.rb +93 -88
  14. data/lib/hybrid_platforms_conductor/connector.rb +5 -2
  15. data/lib/hybrid_platforms_conductor/credentials.rb +122 -97
  16. data/lib/hybrid_platforms_conductor/deployer.rb +7 -9
  17. data/lib/hybrid_platforms_conductor/github.rb +39 -0
  18. data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +1 -1
  19. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +27 -17
  20. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +13 -12
  21. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +6 -4
  22. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +1 -1
  23. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +37 -25
  24. data/lib/hybrid_platforms_conductor/hpc_plugins/log/remote_fs.rb +5 -6
  25. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +1 -1
  26. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +1 -1
  27. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +7 -4
  28. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +3 -1
  29. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +3 -2
  30. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/thycotic.rb +3 -1
  31. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -1
  32. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +17 -3
  33. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +30 -10
  34. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +1 -1
  35. data/lib/hybrid_platforms_conductor/hpc_plugins/test/github_ci.rb +4 -1
  36. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +1 -2
  37. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +1 -1
  38. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +1 -2
  39. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +7 -3
  40. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +8 -4
  41. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +1 -2
  42. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +1 -2
  43. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +1 -2
  44. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +1 -1
  45. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +1 -2
  46. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +3 -1
  47. data/lib/hybrid_platforms_conductor/logger_helpers.rb +24 -1
  48. data/lib/hybrid_platforms_conductor/test.rb +21 -7
  49. data/lib/hybrid_platforms_conductor/tests_runner.rb +7 -6
  50. data/lib/hybrid_platforms_conductor/thycotic.rb +80 -75
  51. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  52. data/spec/hybrid_platforms_conductor_test.rb +6 -0
  53. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +15 -0
  54. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +32 -0
  55. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +87 -0
  56. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +30 -0
  57. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +10 -0
  58. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +38 -0
  59. data/spec/hybrid_platforms_conductor_test/api/actions_executor/helpers_spec.rb +195 -0
  60. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +14 -0
  61. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +11 -0
  62. data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +251 -0
  63. data/spec/hybrid_platforms_conductor_test/api/deployer/log_plugins/remote_fs_spec.rb +215 -0
  64. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +280 -319
  65. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/thycotic_spec.rb +2 -2
  66. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +49 -10
  67. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +38 -0
  68. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +49 -69
  69. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/github_ci_spec.rb +29 -39
  70. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +5 -3
  71. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/chef_versions.yml +3 -0
  72. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/nodes/node.json +15 -0
  73. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/policyfiles/test_policy.rb +3 -0
  74. data/spec/hybrid_platforms_conductor_test/shared_examples/deployer.rb +134 -0
  75. data/spec/hybrid_platforms_conductor_test/test_connector.rb +2 -2
  76. metadata +36 -2
@@ -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
@@ -57,12 +57,9 @@ describe HybridPlatformsConductor::Deployer do
57
57
  # * *additional_config* (String): Additional config
58
58
  # * *platform_info* (Hash): Platform configuration [default: 1 node having 1 service]
59
59
  # * *mock_keepass_password* (String): Password to be returned by credentials [default: 'test_keepass_password']
60
- # * *mock_xml* (String): XML to be mocked [default: xml_single_entry]
61
60
  # * *expect_key_file* (String or nil): Key file to be expected, or nil if none [default: nil]
62
61
  # * *expect_password_enc* (String or nil): Encrypted password to be expected, or nil if none [default: nil]
63
62
  # * *expect_kpscript_calls* (Boolean): Should we expect calls to KPScript? [default: true]
64
- # * *expect_nbr_credentials_calls* (Integer): How many calls to the credentials are expected? [default: 1]
65
- # * *block* (Proc): Code called when the platform is setup
66
63
  def with_test_platform_for_keepass_test(
67
64
  additional_config,
68
65
  platform_info: {
@@ -70,33 +67,35 @@ describe HybridPlatformsConductor::Deployer do
70
67
  deployable_services: %w[service]
71
68
  },
72
69
  mock_keepass_password: 'test_keepass_password',
73
- mock_xml: xml_single_entry,
70
+ mock_databases: { '/path/to/database.kdbx' => xml_single_entry },
74
71
  expect_key_file: nil,
75
72
  expect_password_enc: nil,
76
- expect_kpscript_calls: true,
77
- expect_nbr_credentials_calls: 1,
78
- &block
73
+ expect_kpscript_calls: true
79
74
  )
80
- expect(HybridPlatformsConductor::Credentials).to receive(:with_credentials_for).exactly(expect_nbr_credentials_calls).times do |id, _logger, _logger_stderr, url: nil, &client_code|
81
- expect(id).to eq :keepass
82
- client_code.call nil, mock_keepass_password
83
- end
84
- if expect_kpscript_calls
85
- expect_calls_to_kpscript [
86
- [
87
- %r{/path/to/kpscript "/path/to/database.kdbx"#{mock_keepass_password.nil? ? '' : " -pw:\"#{Regexp.escape(mock_keepass_password)}\""}#{expect_password_enc.nil? ? '' : " -pw-enc:\"#{Regexp.escape(expect_password_enc)}\""}#{expect_key_file.nil? ? '' : " -keyfile:\"#{Regexp.escape(expect_key_file)}\""} -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
88
- {
89
- stdout: 'OK: Operation completed successfully.',
90
- xml: mock_xml
91
- }
92
- ]
93
- ]
94
- end
95
75
  with_test_platform(
96
76
  platform_info,
97
- additional_config: "read_secrets_from :keepass\n#{additional_config}",
98
- &block
99
- )
77
+ additional_config: "read_secrets_from :keepass\n#{additional_config}"
78
+ ) do
79
+ mock_databases.each do |database, _xml|
80
+ expect(test_deployer.instance_variable_get(:@secrets_readers)[:keepass]).to receive(:with_credentials_for).with(:keepass, resource: database) do |_id, resource: nil, &client_code|
81
+ client_code.call nil, mock_keepass_password
82
+ end
83
+ end
84
+ if expect_kpscript_calls
85
+ expect_calls_to_kpscript(
86
+ mock_databases.map do |database, xml|
87
+ [
88
+ %r{/path/to/kpscript "#{Regexp.escape(database)}"#{mock_keepass_password.nil? ? '' : " -pw:\"#{Regexp.escape(mock_keepass_password)}\""}#{expect_password_enc.nil? ? '' : " -pw-enc:\"#{Regexp.escape(expect_password_enc)}\""}#{expect_key_file.nil? ? '' : " -keyfile:\"#{Regexp.escape(expect_key_file)}\""} -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
89
+ {
90
+ stdout: 'OK: Operation completed successfully.',
91
+ xml: xml
92
+ }
93
+ ]
94
+ end
95
+ )
96
+ end
97
+ yield
98
+ end
100
99
  end
101
100
 
102
101
  # Expect secrets to be set to given values
@@ -224,7 +223,7 @@ describe HybridPlatformsConductor::Deployer do
224
223
  <<~EO_CONFIG,
225
224
  secrets_from_keepass(database: '/path/to/database.kdbx')
226
225
  EO_CONFIG
227
- expect_nbr_credentials_calls: 0,
226
+ mock_databases: {},
228
227
  expect_kpscript_calls: false
229
228
  ) do
230
229
  expect { test_deployer.deploy_on(%w[node]) }.to raise_error 'Missing KPScript configuration. Please use use_kpscript_from to set it.'
@@ -237,63 +236,65 @@ describe HybridPlatformsConductor::Deployer do
237
236
  use_kpscript_from '/path/to/kpscript'
238
237
  secrets_from_keepass(database: '/path/to/database.kdbx')
239
238
  EO_CONFIG
240
- mock_xml: <<~EO_XML
241
- <KeePassFile>
242
- <Root>
243
- <Group>
244
- <Entry>
245
- <String>
246
- <Key>Password</Key>
247
- <Value ProtectInMemory="True">TestPassword0</Value>
248
- </String>
249
- <String>
250
- <Key>Title</Key>
251
- <Value>Secret 0</Value>
252
- </String>
253
- </Entry>
239
+ mock_databases: {
240
+ '/path/to/database.kdbx' => <<~EO_XML
241
+ <KeePassFile>
242
+ <Root>
254
243
  <Group>
255
- <Name>Group1</UUID>
256
244
  <Entry>
257
245
  <String>
258
246
  <Key>Password</Key>
259
- <Value ProtectInMemory="True">TestPassword1</Value>
247
+ <Value ProtectInMemory="True">TestPassword0</Value>
260
248
  </String>
261
249
  <String>
262
250
  <Key>Title</Key>
263
- <Value>Secret 1</Value>
251
+ <Value>Secret 0</Value>
264
252
  </String>
265
253
  </Entry>
266
254
  <Group>
267
- <Name>Group2</UUID>
255
+ <Name>Group1</UUID>
268
256
  <Entry>
269
257
  <String>
270
258
  <Key>Password</Key>
271
- <Value ProtectInMemory="True">TestPassword2</Value>
259
+ <Value ProtectInMemory="True">TestPassword1</Value>
272
260
  </String>
273
261
  <String>
274
262
  <Key>Title</Key>
275
- <Value>Secret 2</Value>
276
- </String>
277
- </Entry>
278
- </Group>
279
- <Group>
280
- <Name>Group3</UUID>
281
- <Entry>
282
- <String>
283
- <Key>Password</Key>
284
- <Value ProtectInMemory="True">TestPassword3</Value>
285
- </String>
286
- <String>
287
- <Key>Title</Key>
288
- <Value>Secret 3</Value>
263
+ <Value>Secret 1</Value>
289
264
  </String>
290
265
  </Entry>
266
+ <Group>
267
+ <Name>Group2</UUID>
268
+ <Entry>
269
+ <String>
270
+ <Key>Password</Key>
271
+ <Value ProtectInMemory="True">TestPassword2</Value>
272
+ </String>
273
+ <String>
274
+ <Key>Title</Key>
275
+ <Value>Secret 2</Value>
276
+ </String>
277
+ </Entry>
278
+ </Group>
279
+ <Group>
280
+ <Name>Group3</UUID>
281
+ <Entry>
282
+ <String>
283
+ <Key>Password</Key>
284
+ <Value ProtectInMemory="True">TestPassword3</Value>
285
+ </String>
286
+ <String>
287
+ <Key>Title</Key>
288
+ <Value>Secret 3</Value>
289
+ </String>
290
+ </Entry>
291
+ </Group>
291
292
  </Group>
292
293
  </Group>
293
- </Group>
294
- </Root>
295
- </KeePassFile>
296
- EO_XML
294
+ </Root>
295
+ </KeePassFile>
296
+ EO_XML
297
+ }
297
298
  ) do
298
299
  expect_secrets_to_be(
299
300
  'Secret 0' => { 'password' => 'TestPassword0' },
@@ -316,57 +317,59 @@ describe HybridPlatformsConductor::Deployer do
316
317
  use_kpscript_from '/path/to/kpscript'
317
318
  secrets_from_keepass(database: '/path/to/database.kdbx')
318
319
  EO_CONFIG
319
- mock_xml: <<~EO_XML
320
- <KeePassFile>
321
- <Meta>
322
- <Binaries>
323
- <Binary ID="0" Compressed="True">#{
324
- str = StringIO.new
325
- gz = Zlib::GzipWriter.new(str)
326
- gz.write('File 0 Content')
327
- gz.close
328
- Base64.encode64(str.string).strip
329
- }</Binary>
330
- <Binary ID="1">#{Base64.encode64('File 1 Content').strip}</Binary>
331
- </Binaries>
332
- </Meta>
333
- <Root>
334
- <Group>
335
- <Entry>
336
- <String>
337
- <Key>Password</Key>
338
- <Value ProtectInMemory="True">TestPassword0</Value>
339
- </String>
340
- <String>
341
- <Key>Title</Key>
342
- <Value>Secret 0</Value>
343
- </String>
344
- <Binary>
345
- <Key>file0.txt</Key>
346
- <Value Ref="0" />
347
- </Binary>
348
- </Entry>
320
+ mock_databases: {
321
+ '/path/to/database.kdbx' => <<~EO_XML
322
+ <KeePassFile>
323
+ <Meta>
324
+ <Binaries>
325
+ <Binary ID="0" Compressed="True">#{
326
+ str = StringIO.new
327
+ gz = Zlib::GzipWriter.new(str)
328
+ gz.write('File 0 Content')
329
+ gz.close
330
+ Base64.encode64(str.string).strip
331
+ }</Binary>
332
+ <Binary ID="1">#{Base64.encode64('File 1 Content').strip}</Binary>
333
+ </Binaries>
334
+ </Meta>
335
+ <Root>
349
336
  <Group>
350
- <Name>Group1</UUID>
351
337
  <Entry>
352
338
  <String>
353
339
  <Key>Password</Key>
354
- <Value ProtectInMemory="True">TestPassword1</Value>
340
+ <Value ProtectInMemory="True">TestPassword0</Value>
355
341
  </String>
356
342
  <String>
357
343
  <Key>Title</Key>
358
- <Value>Secret 1</Value>
344
+ <Value>Secret 0</Value>
359
345
  </String>
360
346
  <Binary>
361
- <Key>file1.txt</Key>
362
- <Value Ref="1" />
347
+ <Key>file0.txt</Key>
348
+ <Value Ref="0" />
363
349
  </Binary>
364
350
  </Entry>
351
+ <Group>
352
+ <Name>Group1</UUID>
353
+ <Entry>
354
+ <String>
355
+ <Key>Password</Key>
356
+ <Value ProtectInMemory="True">TestPassword1</Value>
357
+ </String>
358
+ <String>
359
+ <Key>Title</Key>
360
+ <Value>Secret 1</Value>
361
+ </String>
362
+ <Binary>
363
+ <Key>file1.txt</Key>
364
+ <Value Ref="1" />
365
+ </Binary>
366
+ </Entry>
367
+ </Group>
365
368
  </Group>
366
- </Group>
367
- </Root>
368
- </KeePassFile>
369
- EO_XML
369
+ </Root>
370
+ </KeePassFile>
371
+ EO_XML
372
+ }
370
373
  ) do
371
374
  expect_secrets_to_be(
372
375
  'Secret 0' => { 'file0.txt' => 'File 0 Content', 'password' => 'TestPassword0' },
@@ -410,47 +413,33 @@ describe HybridPlatformsConductor::Deployer do
410
413
  nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
411
414
  deployable_services: %w[service1 service2]
412
415
  },
413
- expect_kpscript_calls: false,
414
- expect_nbr_credentials_calls: 2
416
+ mock_databases: {
417
+ '/path/to/database1.kdbx' => xml_single_entry,
418
+ '/path/to/database2.kdbx' => <<~EO_XML
419
+ <KeePassFile>
420
+ <Root>
421
+ <Group>
422
+ <Entry>
423
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
424
+ <String>
425
+ <Key>Password</Key>
426
+ <Value ProtectInMemory="True">TestPassword2</Value>
427
+ </String>
428
+ <String>
429
+ <Key>Title</Key>
430
+ <Value>Test Secret 2</Value>
431
+ </String>
432
+ <String>
433
+ <Key>UserName</Key>
434
+ <Value>Test User Name 2</Value>
435
+ </String>
436
+ </Entry>
437
+ </Group>
438
+ </Root>
439
+ </KeePassFile>
440
+ EO_XML
441
+ }
415
442
  ) do
416
- expect_calls_to_kpscript [
417
- [
418
- %r{/path/to/kpscript "/path/to/database1.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
419
- {
420
- stdout: 'OK: Operation completed successfully.',
421
- xml: xml_single_entry
422
- }
423
- ],
424
- [
425
- %r{/path/to/kpscript "/path/to/database2.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
426
- {
427
- stdout: 'OK: Operation completed successfully.',
428
- xml: <<~EO_XML
429
- <KeePassFile>
430
- <Root>
431
- <Group>
432
- <Entry>
433
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
434
- <String>
435
- <Key>Password</Key>
436
- <Value ProtectInMemory="True">TestPassword2</Value>
437
- </String>
438
- <String>
439
- <Key>Title</Key>
440
- <Value>Test Secret 2</Value>
441
- </String>
442
- <String>
443
- <Key>UserName</Key>
444
- <Value>Test User Name 2</Value>
445
- </String>
446
- </Entry>
447
- </Group>
448
- </Root>
449
- </KeePassFile>
450
- EO_XML
451
- }
452
- ]
453
- ]
454
443
  expect(test_services_handler).to receive(:package).with(
455
444
  services: { 'node1' => %w[service1], 'node2' => %w[service2] },
456
445
  secrets: {
@@ -500,122 +489,108 @@ describe HybridPlatformsConductor::Deployer do
500
489
  nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
501
490
  deployable_services: %w[service1 service2]
502
491
  },
503
- expect_kpscript_calls: false,
504
- expect_nbr_credentials_calls: 2
492
+ mock_databases: {
493
+ '/path/to/database1.kdbx' => <<~EO_XML,
494
+ <KeePassFile>
495
+ <Root>
496
+ <Group>
497
+ <Entry>
498
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
499
+ <String>
500
+ <Key>Password</Key>
501
+ <Value ProtectInMemory="True">TestPassword1</Value>
502
+ </String>
503
+ <String>
504
+ <Key>Title</Key>
505
+ <Value>Test Secret 1</Value>
506
+ </String>
507
+ <String>
508
+ <Key>UserName</Key>
509
+ <Value>Test User Name 1</Value>
510
+ </String>
511
+ </Entry>
512
+ <Group>
513
+ <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
514
+ <Name>Group1</Name>
515
+ <Entry>
516
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
517
+ <String>
518
+ <Key>Password</Key>
519
+ <Value ProtectInMemory="True">TestPassword3</Value>
520
+ </String>
521
+ <String>
522
+ <Key>Title</Key>
523
+ <Value>Test Secret 3</Value>
524
+ </String>
525
+ <String>
526
+ <Key>UserName</Key>
527
+ <Value>Test User Name 3</Value>
528
+ </String>
529
+ </Entry>
530
+ </Group>
531
+ </Group>
532
+ </Root>
533
+ </KeePassFile>
534
+ EO_XML
535
+ '/path/to/database2.kdbx' => <<~EO_XML
536
+ <KeePassFile>
537
+ <Root>
538
+ <Group>
539
+ <Entry>
540
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
541
+ <String>
542
+ <Key>Password</Key>
543
+ <Value ProtectInMemory="True">TestPassword2</Value>
544
+ </String>
545
+ <String>
546
+ <Key>Title</Key>
547
+ <Value>Test Secret 2</Value>
548
+ </String>
549
+ <String>
550
+ <Key>UserName</Key>
551
+ <Value>Test User Name 2</Value>
552
+ </String>
553
+ </Entry>
554
+ <Group>
555
+ <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
556
+ <Name>Group1</Name>
557
+ <Entry>
558
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
559
+ <String>
560
+ <Key>Password</Key>
561
+ <Value ProtectInMemory="True">TestPassword3</Value>
562
+ </String>
563
+ <String>
564
+ <Key>Title</Key>
565
+ <Value>Test Secret 3</Value>
566
+ </String>
567
+ <String>
568
+ <Key>Notes</Key>
569
+ <Value>Notes 3</Value>
570
+ </String>
571
+ </Entry>
572
+ <Entry>
573
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
574
+ <String>
575
+ <Key>Password</Key>
576
+ <Value ProtectInMemory="True">TestPassword4</Value>
577
+ </String>
578
+ <String>
579
+ <Key>Title</Key>
580
+ <Value>Test Secret 4</Value>
581
+ </String>
582
+ <String>
583
+ <Key>UserName</Key>
584
+ <Value>Test User Name 4</Value>
585
+ </String>
586
+ </Entry>
587
+ </Group>
588
+ </Group>
589
+ </Root>
590
+ </KeePassFile>
591
+ EO_XML
592
+ }
505
593
  ) do
506
- expect_calls_to_kpscript [
507
- [
508
- %r{/path/to/kpscript "/path/to/database1.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
509
- {
510
- stdout: 'OK: Operation completed successfully.',
511
- xml: <<~EO_XML
512
- <KeePassFile>
513
- <Root>
514
- <Group>
515
- <Entry>
516
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
517
- <String>
518
- <Key>Password</Key>
519
- <Value ProtectInMemory="True">TestPassword1</Value>
520
- </String>
521
- <String>
522
- <Key>Title</Key>
523
- <Value>Test Secret 1</Value>
524
- </String>
525
- <String>
526
- <Key>UserName</Key>
527
- <Value>Test User Name 1</Value>
528
- </String>
529
- </Entry>
530
- <Group>
531
- <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
532
- <Name>Group1</Name>
533
- <Entry>
534
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
535
- <String>
536
- <Key>Password</Key>
537
- <Value ProtectInMemory="True">TestPassword3</Value>
538
- </String>
539
- <String>
540
- <Key>Title</Key>
541
- <Value>Test Secret 3</Value>
542
- </String>
543
- <String>
544
- <Key>UserName</Key>
545
- <Value>Test User Name 3</Value>
546
- </String>
547
- </Entry>
548
- </Group>
549
- </Group>
550
- </Root>
551
- </KeePassFile>
552
- EO_XML
553
- }
554
- ],
555
- [
556
- %r{/path/to/kpscript "/path/to/database2.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
557
- {
558
- stdout: 'OK: Operation completed successfully.',
559
- xml: <<~EO_XML
560
- <KeePassFile>
561
- <Root>
562
- <Group>
563
- <Entry>
564
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
565
- <String>
566
- <Key>Password</Key>
567
- <Value ProtectInMemory="True">TestPassword2</Value>
568
- </String>
569
- <String>
570
- <Key>Title</Key>
571
- <Value>Test Secret 2</Value>
572
- </String>
573
- <String>
574
- <Key>UserName</Key>
575
- <Value>Test User Name 2</Value>
576
- </String>
577
- </Entry>
578
- <Group>
579
- <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
580
- <Name>Group1</Name>
581
- <Entry>
582
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
583
- <String>
584
- <Key>Password</Key>
585
- <Value ProtectInMemory="True">TestPassword3</Value>
586
- </String>
587
- <String>
588
- <Key>Title</Key>
589
- <Value>Test Secret 3</Value>
590
- </String>
591
- <String>
592
- <Key>Notes</Key>
593
- <Value>Notes 3</Value>
594
- </String>
595
- </Entry>
596
- <Entry>
597
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
598
- <String>
599
- <Key>Password</Key>
600
- <Value ProtectInMemory="True">TestPassword4</Value>
601
- </String>
602
- <String>
603
- <Key>Title</Key>
604
- <Value>Test Secret 4</Value>
605
- </String>
606
- <String>
607
- <Key>UserName</Key>
608
- <Value>Test User Name 4</Value>
609
- </String>
610
- </Entry>
611
- </Group>
612
- </Group>
613
- </Root>
614
- </KeePassFile>
615
- EO_XML
616
- }
617
- ]
618
- ]
619
594
  expect(test_services_handler).to receive(:package).with(
620
595
  services: { 'node1' => %w[service1], 'node2' => %w[service2] },
621
596
  secrets: {
@@ -645,69 +620,55 @@ describe HybridPlatformsConductor::Deployer do
645
620
  nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
646
621
  deployable_services: %w[service1 service2]
647
622
  },
648
- expect_kpscript_calls: false,
649
- expect_nbr_credentials_calls: 2
623
+ mock_databases: {
624
+ '/path/to/database1.kdbx' => <<~EO_XML,
625
+ <KeePassFile>
626
+ <Root>
627
+ <Group>
628
+ <Entry>
629
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
630
+ <String>
631
+ <Key>Password</Key>
632
+ <Value ProtectInMemory="True">TestPassword1</Value>
633
+ </String>
634
+ <String>
635
+ <Key>Title</Key>
636
+ <Value>Test Secret 1</Value>
637
+ </String>
638
+ <String>
639
+ <Key>UserName</Key>
640
+ <Value>Test User Name 1</Value>
641
+ </String>
642
+ </Entry>
643
+ </Group>
644
+ </Root>
645
+ </KeePassFile>
646
+ EO_XML
647
+ '/path/to/database2.kdbx' => <<~EO_XML
648
+ <KeePassFile>
649
+ <Root>
650
+ <Group>
651
+ <Entry>
652
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
653
+ <String>
654
+ <Key>Password</Key>
655
+ <Value ProtectInMemory="True">OtherTestPassword1</Value>
656
+ </String>
657
+ <String>
658
+ <Key>Title</Key>
659
+ <Value>Test Secret 1</Value>
660
+ </String>
661
+ <String>
662
+ <Key>UserName</Key>
663
+ <Value>Test User Name 1</Value>
664
+ </String>
665
+ </Entry>
666
+ </Group>
667
+ </Root>
668
+ </KeePassFile>
669
+ EO_XML
670
+ }
650
671
  ) do
651
- expect_calls_to_kpscript [
652
- [
653
- %r{/path/to/kpscript "/path/to/database1.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
654
- {
655
- stdout: 'OK: Operation completed successfully.',
656
- xml: <<~EO_XML
657
- <KeePassFile>
658
- <Root>
659
- <Group>
660
- <Entry>
661
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
662
- <String>
663
- <Key>Password</Key>
664
- <Value ProtectInMemory="True">TestPassword1</Value>
665
- </String>
666
- <String>
667
- <Key>Title</Key>
668
- <Value>Test Secret 1</Value>
669
- </String>
670
- <String>
671
- <Key>UserName</Key>
672
- <Value>Test User Name 1</Value>
673
- </String>
674
- </Entry>
675
- </Group>
676
- </Root>
677
- </KeePassFile>
678
- EO_XML
679
- }
680
- ],
681
- [
682
- %r{/path/to/kpscript "/path/to/database2.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
683
- {
684
- stdout: 'OK: Operation completed successfully.',
685
- xml: <<~EO_XML
686
- <KeePassFile>
687
- <Root>
688
- <Group>
689
- <Entry>
690
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
691
- <String>
692
- <Key>Password</Key>
693
- <Value ProtectInMemory="True">OtherTestPassword1</Value>
694
- </String>
695
- <String>
696
- <Key>Title</Key>
697
- <Value>Test Secret 1</Value>
698
- </String>
699
- <String>
700
- <Key>UserName</Key>
701
- <Value>Test User Name 1</Value>
702
- </String>
703
- </Entry>
704
- </Group>
705
- </Root>
706
- </KeePassFile>
707
- EO_XML
708
- }
709
- ]
710
- ]
711
672
  expect { test_deployer.deploy_on(%w[node1 node2]) }.to raise_error 'Secret set at path Test Secret 1->password by /path/to/database2.kdbx for service service2 on node node2 has conflicting values (set debug for value details).'
712
673
  end
713
674
  end