hybrid_platforms_conductor 33.3.0 → 33.7.0

Sign up to get free protection for your applications and to get access to all the features.
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