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
@@ -132,6 +132,36 @@ describe HybridPlatformsConductor::ActionsExecutor do
132
132
  end
133
133
  end
134
134
 
135
+ it 'creates SSH master to several nodes differing only by the SSH port' do
136
+ with_test_platform(
137
+ {
138
+ nodes: {
139
+ 'node1' => { meta: { host_ip: '192.168.42.1', ssh_port: 6661 } },
140
+ 'node2' => { meta: { host_ip: '192.168.42.1', ssh_port: 6662 } },
141
+ 'node3' => { meta: { host_ip: '192.168.42.1', ssh_port: 6663 } }
142
+ }
143
+ }
144
+ ) do
145
+ with_cmd_runner_mocked(
146
+ [
147
+ ['which env', proc { [0, "/usr/bin/env\n", ''] }],
148
+ ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }]
149
+ ] + ssh_expected_commands_for(
150
+ {
151
+ 'node1' => { connection: '192.168.42.1', user: 'test_user', port: 6661 },
152
+ 'node2' => { connection: '192.168.42.1', user: 'test_user', port: 6662 },
153
+ 'node3' => { connection: '192.168.42.1', user: 'test_user', port: 6663 }
154
+ }
155
+ )
156
+ ) do
157
+ test_connector.ssh_user = 'test_user'
158
+ test_connector.with_connection_to(%w[node1 node2 node3]) do |connected_nodes|
159
+ expect(connected_nodes.sort).to eq %w[node1 node2 node3].sort
160
+ end
161
+ end
162
+ end
163
+ end
164
+
135
165
  it 'creates SSH master to several nodes with ssh connections transformed' do
136
166
  with_test_platform(
137
167
  { nodes: {
@@ -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
@@ -0,0 +1,251 @@
1
+ describe HybridPlatformsConductor::Credentials do
2
+
3
+ # Create a container class for the credential Mixin to be tested, as a plugin as credentials can be used in any plugin.
4
+ let(:credential_tester_class) do
5
+ Class.new(HybridPlatformsConductor::Plugin) do
6
+ include HybridPlatformsConductor::Credentials
7
+ end
8
+ end
9
+
10
+ # Expect credentials to be as a given user and password
11
+ #
12
+ # Parameters::
13
+ # * *expected_user* (String or nil): The expected user
14
+ # * *expected_password* (String or nil): The expected password
15
+ # * *resource* (String or nil): The resource for which we query the credentials, or nil if none [default: nil]
16
+ def expect_credentials_to_be(expected_user, expected_password, resource: nil)
17
+ creds = {}
18
+ password_class = nil
19
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
20
+ with_credentials_for(:test_credential, resource: resource) do |user, password|
21
+ password_class = password.class
22
+ creds = {
23
+ user: user,
24
+ # We clone the value as for security reasons it is removed when exiting the block
25
+ password: password&.to_unprotected.clone
26
+ }
27
+ end
28
+ end
29
+ # Make sure we always return a SecretString for the password
30
+ expect(password_class).to be SecretString unless password_class == NilClass
31
+ expect(creds).to eq(
32
+ user: expected_user,
33
+ password: expected_password
34
+ )
35
+ end
36
+
37
+ it 'returns no credentials when they are not set' do
38
+ with_platforms '' do
39
+ # Check that .netrc won't be read
40
+ expect(::Netrc).not_to receive(:read)
41
+ expect_credentials_to_be nil, nil
42
+ end
43
+ end
44
+
45
+ it 'returns credentials taken from environment variables' do
46
+ with_platforms '' do
47
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
48
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
49
+ begin
50
+ # Check that .netrc won't be read
51
+ expect(::Netrc).not_to receive(:read)
52
+ expect_credentials_to_be 'env_test_user', 'env_test_password'
53
+ ensure
54
+ ENV.delete('hpc_user_for_test_credential')
55
+ ENV.delete('hpc_password_for_test_credential')
56
+ end
57
+ end
58
+ end
59
+
60
+ it 'erases the value of the password taken from environment variable after usage' do
61
+ with_platforms '' do
62
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
63
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
64
+ begin
65
+ leaked_password = nil
66
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
67
+ with_credentials_for(:test_credential) do |_user, password|
68
+ leaked_password = password
69
+ end
70
+ end
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"
72
+ ensure
73
+ ENV.delete('hpc_user_for_test_credential')
74
+ ENV.delete('hpc_password_for_test_credential')
75
+ end
76
+ end
77
+ end
78
+
79
+ it 'returns credentials taken from .netrc when a resource is specified' do
80
+ with_platforms '' do
81
+ expect(::Netrc).to receive(:read) do
82
+ mocked_netrc = instance_double(::Netrc)
83
+ expect(mocked_netrc).to receive(:[]).with('my_domain.com').and_return %w[test_user test_password]
84
+ expect(mocked_netrc).to receive(:instance_variable_get).with(:@data).and_return []
85
+ mocked_netrc
86
+ end
87
+ expect_credentials_to_be 'test_user', 'test_password', resource: 'http://My_Domain.com/path/to/resource'
88
+ end
89
+ end
90
+
91
+ it 'returns credentials taken from .netrc when a non-URL resource is specified' do
92
+ with_platforms '' do
93
+ expect(::Netrc).to receive(:read) do
94
+ mocked_netrc = instance_double(::Netrc)
95
+ expect(mocked_netrc).to receive(:[]).with('This is:not/ a URL!').and_return %w[test_user test_password]
96
+ expect(mocked_netrc).to receive(:instance_variable_get).with(:@data).and_return []
97
+ mocked_netrc
98
+ end
99
+ expect_credentials_to_be 'test_user', 'test_password', resource: 'This is:not/ a URL!'
100
+ end
101
+ end
102
+
103
+ it 'erases the value of the password taken from netrc after usage' do
104
+ with_platforms '' do
105
+ netrc_data = [['mocked_data']]
106
+ expect(::Netrc).to receive(:read) do
107
+ mocked_netrc = instance_double(::Netrc)
108
+ expect(mocked_netrc).to receive(:[]).with('my_domain.com').and_return %w[test_user test_password]
109
+ expect(mocked_netrc).to receive(:instance_variable_get).with(:@data).and_return netrc_data
110
+ mocked_netrc
111
+ end
112
+ leaked_password = nil
113
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
114
+ with_credentials_for(:test_credential, resource: 'http://My_Domain.com/path/to/resource') do |_user, password|
115
+ leaked_password = password
116
+ end
117
+ end
118
+ expect(leaked_password).to eq "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
119
+ expect(netrc_data).to eq [['GotYou!!!' * 100]]
120
+ end
121
+ end
122
+
123
+ it 'returns credentials taken from config' do
124
+ with_platforms(
125
+ <<~'EO_CONFIG'
126
+ credentials_for(:test_credential) do |resource, requester|
127
+ requester.call "user_for_#{resource}", "password_for_#{resource}"
128
+ end
129
+ EO_CONFIG
130
+ ) do
131
+ # Check that netrc is not called when config is used, and that env vars are ignored
132
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
133
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
134
+ begin
135
+ # Check that .netrc won't be read
136
+ expect(::Netrc).not_to receive(:read)
137
+ expect_credentials_to_be 'user_for_', 'password_for_'
138
+ ensure
139
+ ENV.delete('hpc_user_for_test_credential')
140
+ ENV.delete('hpc_password_for_test_credential')
141
+ end
142
+ end
143
+ end
144
+
145
+ it 'returns credentials taken from config for a given resource' do
146
+ with_platforms(
147
+ <<~'EO_CONFIG'
148
+ credentials_for(:test_credential) do |resource, requester|
149
+ requester.call "user_for_#{resource}", "password_for_#{resource}"
150
+ end
151
+ EO_CONFIG
152
+ ) do
153
+ # Check that netrc is not called when config is used, and that env vars are ignored
154
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
155
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
156
+ begin
157
+ # Check that .netrc won't be read
158
+ expect(::Netrc).not_to receive(:read)
159
+ expect_credentials_to_be 'user_for_test_resource', 'password_for_test_resource', resource: 'test_resource'
160
+ ensure
161
+ ENV.delete('hpc_user_for_test_credential')
162
+ ENV.delete('hpc_password_for_test_credential')
163
+ end
164
+ end
165
+ end
166
+
167
+ it 'returns credentials taken from config for a given resource even when they are nil' do
168
+ with_platforms(
169
+ <<~'EO_CONFIG'
170
+ credentials_for(:test_credential) do |resource, requester|
171
+ requester.call nil, nil
172
+ end
173
+ EO_CONFIG
174
+ ) do
175
+ # Check that netrc is not called when config is used, and that env vars are ignored
176
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
177
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
178
+ begin
179
+ # Check that .netrc won't be read
180
+ expect(::Netrc).not_to receive(:read)
181
+ expect_credentials_to_be nil, nil, resource: 'test_resource'
182
+ ensure
183
+ ENV.delete('hpc_user_for_test_credential')
184
+ ENV.delete('hpc_password_for_test_credential')
185
+ end
186
+ end
187
+ end
188
+
189
+ it 'returns credentials taken from config after filtering the resource name' do
190
+ with_platforms(
191
+ <<~'EO_CONFIG'
192
+ credentials_for(:test_credential, resource: 'another_resource') do |resource, requester|
193
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
194
+ end
195
+ credentials_for(:test_credential, resource: /test_.*/) do |resource, requester|
196
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
197
+ end
198
+ credentials_for(:test_credential, resource: /_resource/) do |resource, requester|
199
+ requester.call "correct_user_for_#{resource}", "correct_password_for_#{resource}"
200
+ end
201
+ credentials_for(:test_credential, resource: 'test_resource2') do |resource, requester|
202
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
203
+ end
204
+ EO_CONFIG
205
+ ) do
206
+ expect_credentials_to_be 'correct_user_for_test_resource', 'correct_password_for_test_resource', resource: 'test_resource'
207
+ end
208
+ end
209
+
210
+ it 'returns credentials taken from config after filtering the resource name when no resource is given' do
211
+ with_platforms(
212
+ <<~'EO_CONFIG'
213
+ credentials_for(:test_credential, resource: 'another_resource') do |resource, requester|
214
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
215
+ end
216
+ credentials_for(:test_credential, resource: /test_.*/) do |resource, requester|
217
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
218
+ end
219
+ credentials_for(:test_credential) do |resource, requester|
220
+ requester.call "correct_user_for_#{resource}", "correct_password_for_#{resource}"
221
+ end
222
+ credentials_for(:test_credential, resource: /_resource/) do |resource, requester|
223
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
224
+ end
225
+ credentials_for(:test_credential, resource: 'test_resource2') do |resource, requester|
226
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
227
+ end
228
+ EO_CONFIG
229
+ ) do
230
+ expect_credentials_to_be 'correct_user_for_', 'correct_password_for_'
231
+ end
232
+ end
233
+
234
+ it 'fails if the requester is not called from config' do
235
+ with_platforms(
236
+ <<~'EO_CONFIG'
237
+ credentials_for(:test_credential) do |resource, requester|
238
+ end
239
+ EO_CONFIG
240
+ ) do
241
+ expect do
242
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
243
+ with_credentials_for(:test_credential) do |_user, _password|
244
+ nil
245
+ end
246
+ end
247
+ end.to raise_error 'Requester not called by the credentials provider for test_credential (resource: ) - Please check the credentials_for code in your configuration.'
248
+ end
249
+ end
250
+
251
+ end