hybrid_platforms_conductor 33.4.0 → 33.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/README.md +5 -5
  4. data/docs/config_dsl.md +7 -5
  5. data/docs/plugins/cmdb/host_keys.md +3 -1
  6. data/docs/plugins/connector/ssh.md +1 -0
  7. data/lib/hybrid_platforms_conductor/actions_executor.rb +29 -1
  8. data/lib/hybrid_platforms_conductor/bitbucket.rb +2 -2
  9. data/lib/hybrid_platforms_conductor/cmd_runner.rb +4 -4
  10. data/lib/hybrid_platforms_conductor/config.rb +2 -0
  11. data/lib/hybrid_platforms_conductor/confluence.rb +2 -2
  12. data/lib/hybrid_platforms_conductor/connector.rb +5 -2
  13. data/lib/hybrid_platforms_conductor/credentials.rb +20 -12
  14. data/lib/hybrid_platforms_conductor/deployer.rb +5 -7
  15. data/lib/hybrid_platforms_conductor/github.rb +1 -1
  16. data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +1 -1
  17. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +27 -17
  18. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +13 -12
  19. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +6 -4
  20. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +1 -1
  21. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +37 -25
  22. data/lib/hybrid_platforms_conductor/hpc_plugins/log/remote_fs.rb +5 -6
  23. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +23 -14
  24. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +1 -1
  25. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +3 -2
  26. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +1 -1
  27. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +17 -3
  28. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +30 -10
  29. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +1 -1
  30. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +1 -2
  31. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +1 -1
  32. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +1 -2
  33. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +1 -1
  34. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +2 -2
  35. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +1 -2
  36. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +1 -2
  37. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +1 -2
  38. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +1 -1
  39. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +1 -2
  40. data/lib/hybrid_platforms_conductor/logger_helpers.rb +17 -0
  41. data/lib/hybrid_platforms_conductor/test.rb +21 -7
  42. data/lib/hybrid_platforms_conductor/tests_runner.rb +7 -6
  43. data/lib/hybrid_platforms_conductor/thycotic.rb +2 -2
  44. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  45. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +15 -0
  46. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +32 -0
  47. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +87 -0
  48. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +30 -0
  49. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +10 -0
  50. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +38 -0
  51. data/spec/hybrid_platforms_conductor_test/api/actions_executor/helpers_spec.rb +195 -0
  52. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +14 -0
  53. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +11 -0
  54. data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +8 -4
  55. data/spec/hybrid_platforms_conductor_test/api/deployer/log_plugins/remote_fs_spec.rb +215 -0
  56. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +49 -10
  57. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +64 -16
  58. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +5 -3
  59. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/chef_versions.yml +3 -0
  60. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/nodes/node.json +15 -0
  61. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/policyfiles/test_policy.rb +3 -0
  62. data/spec/hybrid_platforms_conductor_test/shared_examples/deployer.rb +134 -0
  63. data/spec/hybrid_platforms_conductor_test/test_connector.rb +2 -2
  64. metadata +20 -2
@@ -17,7 +17,7 @@ module HybridPlatformsConductor
17
17
  # Flatten the paths rules so that we can spot inconsistencies in configuration
18
18
  @config.aggregate_files_rules(@nodes_handler, @node).map do |path, rule_info|
19
19
  [
20
- "if #{@nodes_handler.sudo_on(@node)} /bin/bash -c '[[ -d \"#{path}\" ]]' ; then echo 1 ; else echo 0 ; fi",
20
+ "if #{@actions_executor.sudo_prefix(@node)}/bin/bash -c '[[ -d \"#{path}\" ]]' ; then echo 1 ; else echo 0 ; fi",
21
21
  {
22
22
  validator: proc do |stdout, stderr|
23
23
  case stdout.last
@@ -12,8 +12,7 @@ module HybridPlatformsConductor
12
12
  # Check my_test_plugin.rb.sample documentation for signature details.
13
13
  def test_on_node
14
14
  {
15
- # TODO: Access the user correctly when the user notion will be moved out of the ssh connector
16
- "#{@deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(@node)} "}hostname -s" => proc do |stdout|
15
+ "#{@actions_executor.sudo_prefix(@node)}hostname -s" => proc do |stdout|
17
16
  assert_equal stdout.first, @node, "Expected hostname to be #{@node}, but got #{stdout.first} instead."
18
17
  end
19
18
  }
@@ -27,7 +27,7 @@ module HybridPlatformsConductor
27
27
  # System is booting up. See pam_nologin(8)
28
28
  # Authentication failed.
29
29
  instance.stop
30
- instance.with_running_instance(port: 22) do
30
+ instance.with_running_instance(port: @nodes_handler.get_ssh_port_of(@node) || 22) do
31
31
  # Now that the node has been deployed, use the a_testadmin user for the check-node (as root has no more access)
32
32
  deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user = 'a_testadmin'
33
33
  deployer.instance_variable_get(:@actions_executor).connector(:ssh).passwords.delete(@node)
@@ -12,8 +12,7 @@ module HybridPlatformsConductor
12
12
  # Check my_test_plugin.rb.sample documentation for signature details.
13
13
  def test_on_node
14
14
  {
15
- # TODO: Access the user correctly when the user notion will be moved out of the ssh connector
16
- "#{@deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(@node)} "}hostname -I" => proc do |stdout|
15
+ "#{@actions_executor.sudo_prefix(@node)}hostname -I" => proc do |stdout|
17
16
  if stdout.first.nil?
18
17
  error 'No IP returned by "hostname -I"'
19
18
  else
@@ -26,7 +26,7 @@ module HybridPlatformsConductor
26
26
  else
27
27
  with_credentials_for(:jenkins_ci, resource: repo_info[:jenkins_ci_url]) do |jenkins_user, jenkins_password|
28
28
  # Get its config
29
- doc = Nokogiri::XML(URI.parse("#{repo_info[:jenkins_ci_url]}/config.xml").open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
29
+ doc = Nokogiri::XML(URI.parse("#{repo_info[:jenkins_ci_url]}/config.xml").open(http_basic_authentication: [jenkins_user, jenkins_password&.to_unprotected]).read)
30
30
  # Check that this job builds the correct Bitbucket repository
31
31
  assert_equal(
32
32
  doc.xpath('/org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject/sources/data/jenkins.branch.BranchSource/source/serverUrl').text,
@@ -34,10 +34,10 @@ module HybridPlatformsConductor
34
34
  master_info_url = "#{repo_info[:jenkins_ci_url]}/job/master/api/json"
35
35
  with_credentials_for(:jenkins_ci, resource: master_info_url) do |jenkins_user, jenkins_password|
36
36
  # Get the master branch info from the API
37
- master_info = JSON.parse(URI.parse(master_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
37
+ master_info = JSON.parse(URI.parse(master_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password&.to_unprotected]).read)
38
38
  # Get the last build's URL
39
39
  last_build_info_url = "#{master_info['lastBuild']['url']}/api/json"
40
- last_build_info = JSON.parse(URI.parse(last_build_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
40
+ last_build_info = JSON.parse(URI.parse(last_build_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password&.to_unprotected]).read)
41
41
  log_debug "Build info for #{master_info_url}:\n#{JSON.pretty_generate(last_build_info)}"
42
42
  error "Last build for job #{repo_info[:project]}/#{repo_info[:name]} is in status #{last_build_info['result']}: #{master_info['lastBuild']['url']}" unless SUCCESS_STATUSES.include?(last_build_info['result'])
43
43
  rescue
@@ -59,8 +59,7 @@ module HybridPlatformsConductor
59
59
  # Check my_test_plugin.rb.sample documentation for signature details.
60
60
  def test_on_node
61
61
  {
62
- # TODO: Access the user correctly when the user notion will be moved out of the ssh connector
63
- "#{@deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(@node)} "}cat /etc/passwd" => proc do |stdout|
62
+ "#{@actions_executor.sudo_prefix(@node)}cat /etc/passwd" => proc do |stdout|
64
63
  passwd_users = stdout.map { |passwd_line| passwd_line.split(':').first }
65
64
  missing_users = @nodes_handler.
66
65
  select_confs_for_node(@node, @config.users_that_should_be_present).
@@ -63,8 +63,7 @@ module HybridPlatformsConductor
63
63
  # Check my_test_plugin.rb.sample documentation for signature details.
64
64
  def test_on_node
65
65
  {
66
- # TODO: Access the user correctly when the user notion will be moved out of the ssh connector
67
- "#{@deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(@node)} "}mount" => proc do |stdout|
66
+ "#{@actions_executor.sudo_prefix(@node)}mount" => proc do |stdout|
68
67
  mounts_info = stdout.map do |line|
69
68
  fields = line.split
70
69
  {
@@ -52,8 +52,7 @@ module HybridPlatformsConductor
52
52
  # Check my_test_plugin.rb.sample documentation for signature details.
53
53
  def test_on_node
54
54
  {
55
- # TODO: Access the user correctly when the user notion will be moved out of the ssh connector
56
- "#{@deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(@node)} "}/usr/bin/find / \\( #{
55
+ "#{@actions_executor.sudo_prefix(@node)}/usr/bin/find / \\( #{
57
56
  @nodes_handler.
58
57
  select_confs_for_node(@node, @config.ignored_orphan_files_paths).
59
58
  inject(DIRECTORIES_TO_ALWAYS_IGNORE) { |merged_paths, paths_to_ignore_info| merged_paths + paths_to_ignore_info[:ignored_paths] }.
@@ -18,7 +18,7 @@ module HybridPlatformsConductor
18
18
  # Check my_test_plugin.rb.sample documentation for signature details.
19
19
  def test_on_node
20
20
  spectre_cmd = <<~EO_BASH
21
- #{@deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(@node)} "}/bin/bash <<'EOAction'
21
+ #{@actions_executor.sudo_prefix(@node)}/bin/bash <<'EOAction'
22
22
  #{File.read("#{__dir__}/spectre-meltdown-checker.sh")}
23
23
  EOAction
24
24
  EO_BASH
@@ -56,8 +56,7 @@ module HybridPlatformsConductor
56
56
  current_url
57
57
  end
58
58
  )
59
- # TODO: Access the user correctly when the user notion will be moved out of the ssh connector
60
- sudo = @deployer.instance_variable_get(:@actions_executor).connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(@node)} "
59
+ sudo = @actions_executor.sudo_prefix(@node)
61
60
  urls.map do |url|
62
61
  # 1. Get the OVAL file on the node to be tested (uncompress it if needed)
63
62
  # 2. Make sure oscap is installed
@@ -1,6 +1,23 @@
1
1
  require 'colorize'
2
2
  require 'logger'
3
3
  require 'ruby-progressbar'
4
+ require 'secret_string'
5
+
6
+ # Add colorization methods to SecretString, but always directed to the silenced string as we NEVER want to modiy/clone a secret
7
+ class SecretString
8
+
9
+ extend Colorize::ClassMethods
10
+
11
+ def_delegators :@silenced_str, *%i[
12
+ colorize
13
+ uncolorize
14
+ colorized?
15
+ ]
16
+
17
+ color_methods
18
+ modes_methods
19
+
20
+ end
4
21
 
5
22
  module HybridPlatformsConductor
6
23
 
@@ -36,20 +36,34 @@ module HybridPlatformsConductor
36
36
  # Constructor
37
37
  #
38
38
  # Parameters::
39
- # * *logger* (Logger): Logger to be used
40
- # * *logger_stderr* (Logger): Logger to be used for stderr
41
- # * *config* (Config): Config to be used.
42
- # * *cmd_runner* (CmdRunner): CmdRunner that can be used by tests
43
- # * *nodes_handler* (NodesHandler): Nodes handler that can be used by tests
44
- # * *deployer* (Deployer): Deployer that can be used by tests
39
+ # * *logger* (Logger): Logger to be used [default: Logger.new($stdout)]
40
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new($stderr)]
41
+ # * *config* (Config): Config to be used. [default: Config.new]
42
+ # * *cmd_runner* (CmdRunner): Command executor to be used. [default: CmdRunner.new]
43
+ # * *nodes_handler* (NodesHandler): Nodes handler to be used. [default: NodesHandler.new]
44
+ # * *actions_executor* (ActionsExecutor): Actions Executor to be used. [default: ActionsExecutor.new]
45
+ # * *deployer* (Deployer): Deployer that can be used by tests [default: Deployer.new]
45
46
  # * *name* (String): Name of the test being instantiated [default: 'unknown_test']
46
47
  # * *platform* (PlatformHandler): Platform handler for which the test is instantiated, or nil if global or node specific [default: nil]
47
48
  # * *node* (String): Node name for which the test is instantiated, or nil if global or platform specific [default: nil]
48
49
  # * *expected_failure* (String or nil): Expected failure, or nil if not expected to fail [default: nil]
49
- def initialize(logger, logger_stderr, config, cmd_runner, nodes_handler, deployer, name: 'unknown_test', platform: nil, node: nil, expected_failure: nil)
50
+ def initialize(
51
+ logger: Logger.new($stdout),
52
+ logger_stderr: Logger.new($stderr),
53
+ config: Config.new,
54
+ cmd_runner: CmdRunner.new,
55
+ nodes_handler: NodesHandler.new,
56
+ actions_executor: ActionsExecutor.new,
57
+ deployer: Deployer.new,
58
+ name: 'unknown_test',
59
+ platform: nil,
60
+ node: nil,
61
+ expected_failure: nil
62
+ )
50
63
  super(logger: logger, logger_stderr: logger_stderr, config: config)
51
64
  @cmd_runner = cmd_runner
52
65
  @nodes_handler = nodes_handler
66
+ @actions_executor = actions_executor
53
67
  @deployer = deployer
54
68
  @name = name
55
69
  @platform = platform
@@ -274,12 +274,13 @@ module HybridPlatformsConductor
274
274
  # * Test: Corresponding test
275
275
  def new_test(test_name, platform: nil, node: nil, ignore_expected_failure: false)
276
276
  (test_name.nil? ? Test : @tests_plugins[test_name]).new(
277
- @logger,
278
- @logger_stderr,
279
- @config,
280
- @cmd_runner,
281
- @nodes_handler,
282
- @deployer,
277
+ logger: @logger,
278
+ logger_stderr: @logger_stderr,
279
+ config: @config,
280
+ cmd_runner: @cmd_runner,
281
+ nodes_handler: @nodes_handler,
282
+ actions_executor: @actions_executor,
283
+ deployer: @deployer,
283
284
  name: test_name.nil? ? :global : test_name,
284
285
  platform: platform,
285
286
  node: node,
@@ -33,7 +33,7 @@ module HybridPlatformsConductor
33
33
  # Parameters::
34
34
  # * *url* (String): URL of the Thycotic Secret Server
35
35
  # * *user* (String): User name to be used to connect to Thycotic
36
- # * *password* (String): Password to be used to connect to Thycotic
36
+ # * *password* (SecretString): Password to be used to connect to Thycotic
37
37
  # * *domain* (String): Domain to use for authentication to Thycotic [default: ENV['hpc_domain_for_thycotic']]
38
38
  # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
39
39
  # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
@@ -57,7 +57,7 @@ module HybridPlatformsConductor
57
57
  :authenticate,
58
58
  message: {
59
59
  username: user,
60
- password: password,
60
+ password: password&.to_unprotected,
61
61
  domain: domain
62
62
  }
63
63
  ).to_hash.dig(:authenticate_response, :authenticate_result, :token)
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '33.4.0'
3
+ VERSION = '33.7.1'
4
4
 
5
5
  end
@@ -17,6 +17,21 @@ describe HybridPlatformsConductor::ActionsExecutor do
17
17
  end
18
18
  end
19
19
 
20
+ it 'executes local Bash code from a SecretString' do
21
+ with_test_platform_for_action_plugins do |repository|
22
+ expect(
23
+ test_actions_executor.execute_actions(
24
+ {
25
+ 'node' => {
26
+ bash: SecretString.new("echo TestContent >#{repository}/test_file ; echo TestStdout ; echo TestStderr 1>&2", silenced_str: '__INVALID_BASH__')
27
+ }
28
+ }
29
+ )['node']
30
+ ).to eq [0, "TestStdout\n", "TestStderr\n"]
31
+ expect(File.read("#{repository}/test_file")).to eq "TestContent\n"
32
+ end
33
+ end
34
+
20
35
  it 'executes local Bash code with timeout' do
21
36
  with_test_platform_for_action_plugins do
22
37
  expect(test_actions_executor.execute_actions(
@@ -13,6 +13,17 @@ describe HybridPlatformsConductor::ActionsExecutor do
13
13
  end
14
14
  end
15
15
 
16
+ it 'executes remote Bash code from a SecretString' do
17
+ with_test_platform_for_action_plugins do
18
+ test_actions_executor.execute_actions({ 'node' => { remote_bash: SecretString.new('remote_bash_cmd.bash', silenced_str: '__INVALID_BASH__') } })
19
+ expect(test_actions_executor.connector(:test_connector).calls).to eq [
20
+ [:connectable_nodes_from, ['node']],
21
+ [:with_connection_to, ['node'], { no_exception: true }],
22
+ [:remote_bash, 'remote_bash_cmd.bash']
23
+ ]
24
+ end
25
+ end
26
+
16
27
  it 'executes remote Bash code with timeout' do
17
28
  with_test_platform_for_action_plugins do
18
29
  test_actions_executor.connector(:test_connector).remote_bash_code = proc do |_stdout, _stderr, connector|
@@ -124,6 +135,27 @@ describe HybridPlatformsConductor::ActionsExecutor do
124
135
  end
125
136
  end
126
137
 
138
+ it 'executes remote Bash code with environment variables set using SecretStrings' do
139
+ with_test_platform_for_action_plugins do
140
+ test_actions_executor.execute_actions(
141
+ {
142
+ 'node' => { remote_bash: {
143
+ commands: 'bash_cmd.bash',
144
+ env: {
145
+ 'var1' => SecretString.new('value1', silenced_str: 'SILENCED_VALUE'),
146
+ 'var2' => 'value2'
147
+ }
148
+ } }
149
+ }
150
+ )
151
+ expect(test_actions_executor.connector(:test_connector).calls).to eq [
152
+ [:connectable_nodes_from, ['node']],
153
+ [:with_connection_to, ['node'], { no_exception: true }],
154
+ [:remote_bash, "export var1='value1'\nexport var2='value2'\nbash_cmd.bash"]
155
+ ]
156
+ end
157
+ end
158
+
127
159
  end
128
160
 
129
161
  end
@@ -54,6 +54,15 @@ describe HybridPlatformsConductor::ActionsExecutor do
54
54
  end
55
55
  end
56
56
 
57
+ it 'executes bash commands remotely from a SecretString' do
58
+ with_test_platform_for_remote_testing(
59
+ expected_cmds: [['cd /tmp/hpc_local_workspaces/node ; bash_cmd.bash', proc { [0, 'Bash commands executed on node', ''] }]],
60
+ expected_stdout: 'Bash commands executed on node'
61
+ ) do
62
+ test_connector.remote_bash(SecretString.new('bash_cmd.bash', silenced_str: '__INVALID_BASH__'))
63
+ end
64
+ end
65
+
57
66
  it 'executes bash commands remotely with timeout' do
58
67
  with_test_platform_for_remote_testing(
59
68
  expected_cmds: [
@@ -98,6 +107,10 @@ describe HybridPlatformsConductor::ActionsExecutor do
98
107
  it 'copies files remotely with sudo' do
99
108
  with_test_platform_for_remote_testing(
100
109
  expected_cmds: [
110
+ [
111
+ 'whoami',
112
+ proc { [0, 'test_user', ''] }
113
+ ],
101
114
  [
102
115
  'sudo -u root cp -r "/path/to/src.file" "/remote_path/to/dst.dir"',
103
116
  proc { [0, '', ''] }
@@ -108,9 +121,27 @@ describe HybridPlatformsConductor::ActionsExecutor do
108
121
  end
109
122
  end
110
123
 
124
+ it 'copies files remotely with sudo when being root' do
125
+ with_test_platform_for_remote_testing(
126
+ expected_cmds: [
127
+ [
128
+ 'whoami',
129
+ proc { [0, 'root', ''] }
130
+ ]
131
+ ]
132
+ ) do
133
+ expect(FileUtils).to receive(:cp_r).with('/path/to/src.file', '/remote_path/to/dst.dir')
134
+ test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir', sudo: true)
135
+ end
136
+ end
137
+
111
138
  it 'copies files remotely with a different sudo' do
112
139
  with_test_platform_for_remote_testing(
113
140
  expected_cmds: [
141
+ [
142
+ 'whoami',
143
+ proc { [0, 'test_user', ''] }
144
+ ],
114
145
  [
115
146
  'other_sudo --user root cp -r "/path/to/src.file" "/remote_path/to/dst.dir"',
116
147
  proc { [0, '', ''] }
@@ -124,6 +155,23 @@ describe HybridPlatformsConductor::ActionsExecutor do
124
155
  end
125
156
  end
126
157
 
158
+ it 'copies files remotely with a different sudo when being root' do
159
+ with_test_platform_for_remote_testing(
160
+ expected_cmds: [
161
+ [
162
+ 'whoami',
163
+ proc { [0, 'root', ''] }
164
+ ]
165
+ ],
166
+ additional_config: <<~'EO_CONFIG'
167
+ sudo_for { |user| "other_sudo --user #{user}" }
168
+ EO_CONFIG
169
+ ) do
170
+ expect(FileUtils).to receive(:cp_r).with('/path/to/src.file', '/remote_path/to/dst.dir')
171
+ test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir', sudo: true)
172
+ end
173
+ end
174
+
127
175
  it 'copies files remotely with timeout' do
128
176
  with_test_platform_for_remote_testing(
129
177
  timeout: 5
@@ -143,6 +191,10 @@ describe HybridPlatformsConductor::ActionsExecutor do
143
191
  it 'copies relative files remotely with sudo' do
144
192
  with_test_platform_for_remote_testing(
145
193
  expected_cmds: [
194
+ [
195
+ 'whoami',
196
+ proc { [0, 'test_user', ''] }
197
+ ],
146
198
  [
147
199
  'sudo -u root cp -r "/path/to/src.file" "/tmp/hpc_local_workspaces/node/to/dst.dir"',
148
200
  proc { [0, '', ''] }
@@ -153,9 +205,27 @@ describe HybridPlatformsConductor::ActionsExecutor do
153
205
  end
154
206
  end
155
207
 
208
+ it 'copies relative files remotely with sudo when being root' do
209
+ with_test_platform_for_remote_testing(
210
+ expected_cmds: [
211
+ [
212
+ 'whoami',
213
+ proc { [0, 'root', ''] }
214
+ ]
215
+ ]
216
+ ) do
217
+ expect(FileUtils).to receive(:cp_r).with('/path/to/src.file', '/tmp/hpc_local_workspaces/node/to/dst.dir')
218
+ test_connector.remote_copy('/path/to/src.file', 'to/dst.dir', sudo: true)
219
+ end
220
+ end
221
+
156
222
  it 'copies relative files remotely with a different sudo' do
157
223
  with_test_platform_for_remote_testing(
158
224
  expected_cmds: [
225
+ [
226
+ 'whoami',
227
+ proc { [0, 'test_user', ''] }
228
+ ],
159
229
  [
160
230
  'other_sudo --user root cp -r "/path/to/src.file" "/tmp/hpc_local_workspaces/node/to/dst.dir"',
161
231
  proc { [0, '', ''] }
@@ -169,6 +239,23 @@ describe HybridPlatformsConductor::ActionsExecutor do
169
239
  end
170
240
  end
171
241
 
242
+ it 'copies relative files remotely with a different sudo when being root' do
243
+ with_test_platform_for_remote_testing(
244
+ expected_cmds: [
245
+ [
246
+ 'whoami',
247
+ proc { [0, 'root', ''] }
248
+ ]
249
+ ],
250
+ additional_config: <<~'EO_CONFIG'
251
+ sudo_for { |user| "other_sudo --user #{user}" }
252
+ EO_CONFIG
253
+ ) do
254
+ expect(FileUtils).to receive(:cp_r).with('/path/to/src.file', '/tmp/hpc_local_workspaces/node/to/dst.dir')
255
+ test_connector.remote_copy('/path/to/src.file', 'to/dst.dir', sudo: true)
256
+ end
257
+ end
258
+
172
259
  end
173
260
 
174
261
  end
@@ -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: {