hybrid_platforms_conductor 32.4.1 → 32.7.1

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hybrid_platforms_conductor/deployer.rb +9 -8
  3. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +169 -85
  4. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +1 -1
  5. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +1 -1
  6. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +1 -1
  7. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +1 -1
  8. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +1 -1
  9. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +1 -1
  10. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +1 -1
  11. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +1 -1
  12. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +1 -1
  13. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +6 -7
  14. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +7 -6
  15. data/lib/hybrid_platforms_conductor/nodes_handler.rb +37 -0
  16. data/lib/hybrid_platforms_conductor/services_handler.rb +9 -13
  17. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  18. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +35 -0
  19. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +65 -5
  20. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +68 -12
  21. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +1 -1
  22. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +47 -9
  23. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +28 -0
  24. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/config_dsl_spec.rb +71 -0
  25. data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +2 -1
  26. data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +25 -11
  27. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +50 -11
  28. data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +14 -14
  29. data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +70 -11
  30. data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +1 -1
  31. data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +2 -2
  32. metadata +12 -11
@@ -404,7 +404,7 @@ module HybridPlatformsConductor
404
404
  {
405
405
  proxmox_test_info[:sync_node] => {
406
406
  remote_bash: {
407
- commands: "#{@actions_executor.connector(:ssh).ssh_user == 'root' ? '' : 'sudo -E '}./proxmox/#{cmd}",
407
+ commands: "#{@actions_executor.connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(proxmox_test_info[:sync_node])} -E "}./proxmox/#{cmd}",
408
408
  env: {
409
409
  'hpc_user_for_proxmox' => user,
410
410
  'hpc_password_for_proxmox' => password,
@@ -15,7 +15,7 @@ module HybridPlatformsConductor
15
15
  def test_on_node
16
16
  now = Time.now
17
17
  {
18
- 'sudo ls -t /var/log/deployments' => proc do |stdout|
18
+ "#{@nodes_handler.sudo_on(@node)} ls -t /var/log/deployments" => proc do |stdout|
19
19
  if stdout.empty?
20
20
  error 'Node has never been deployed using deploy (/var/log/deployments is empty)'
21
21
  elsif stdout.first =~ /No such file or directory/
@@ -17,7 +17,7 @@ module HybridPlatformsConductor
17
17
  Hash[
18
18
  @config.aggregate_files_rules(@nodes_handler, @node).map do |path, rule_info|
19
19
  [
20
- "if sudo /bin/bash -c '[[ -d \"#{path}\" ]]' ; then echo 1 ; else echo 0 ; fi",
20
+ "if #{@nodes_handler.sudo_on(@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
@@ -10,7 +10,7 @@ module HybridPlatformsConductor
10
10
  # Check my_test_plugin.rb.sample documentation for signature details.
11
11
  def test_on_node
12
12
  {
13
- 'sudo hostname -s' => proc do |stdout|
13
+ "#{@nodes_handler.sudo_on(@node)} hostname -s" => proc do |stdout|
14
14
  assert_equal stdout.first, @node, "Expected hostname to be #{@node}, but got #{stdout.first} instead."
15
15
  end
16
16
  }
@@ -10,7 +10,7 @@ module HybridPlatformsConductor
10
10
  # Check my_test_plugin.rb.sample documentation for signature details.
11
11
  def test_on_node
12
12
  {
13
- 'sudo hostname -I' => proc do |stdout|
13
+ "#{@nodes_handler.sudo_on(@node)} hostname -I" => proc do |stdout|
14
14
  if stdout.first.nil?
15
15
  error 'No IP returned by "hostname -I"'
16
16
  else
@@ -57,7 +57,7 @@ module HybridPlatformsConductor
57
57
  # Check my_test_plugin.rb.sample documentation for signature details.
58
58
  def test_on_node
59
59
  {
60
- "sudo cat /etc/passwd" => proc do |stdout|
60
+ "#{@nodes_handler.sudo_on(@node)} cat /etc/passwd" => proc do |stdout|
61
61
  passwd_users = stdout.map { |passwd_line| passwd_line.split(':').first }
62
62
  missing_users = @nodes_handler.
63
63
  select_confs_for_node(@node, @config.users_that_should_be_present).
@@ -61,7 +61,7 @@ module HybridPlatformsConductor
61
61
  # Check my_test_plugin.rb.sample documentation for signature details.
62
62
  def test_on_node
63
63
  {
64
- 'sudo mount' => proc do |stdout|
64
+ "#{@nodes_handler.sudo_on(@node)} mount" => proc do |stdout|
65
65
  mounts_info = stdout.map do |line|
66
66
  fields = line.split
67
67
  {
@@ -50,7 +50,7 @@ module HybridPlatformsConductor
50
50
  # Check my_test_plugin.rb.sample documentation for signature details.
51
51
  def test_on_node
52
52
  {
53
- "sudo /usr/bin/find / \\( #{@nodes_handler.
53
+ "#{@nodes_handler.sudo_on(@node)} /usr/bin/find / \\( #{@nodes_handler.
54
54
  select_confs_for_node(@node, @config.ignored_orphan_files_paths).
55
55
  inject(DIRECTORIES_TO_ALWAYS_IGNORE) { |merged_paths, paths_to_ignore_info| merged_paths + paths_to_ignore_info[:ignored_paths] }.
56
56
  uniq.
@@ -13,16 +13,15 @@ module HybridPlatformsConductor
13
13
  'CVE-2017-5754' => 'Meltdown'
14
14
  }
15
15
 
16
- SPECTRE_CMD = <<~EOS
17
- sudo /bin/bash <<'EOAction'
18
- #{File.read("#{__dir__}/spectre-meltdown-checker.sh")}
19
- EOAction
20
- EOS
21
-
22
16
  # Check my_test_plugin.rb.sample documentation for signature details.
23
17
  def test_on_node
18
+ spectre_cmd = <<~EOS
19
+ #{@nodes_handler.sudo_on(@node)} /bin/bash <<'EOAction'
20
+ #{File.read("#{__dir__}/spectre-meltdown-checker.sh")}
21
+ EOAction
22
+ EOS
24
23
  {
25
- SPECTRE_CMD => {
24
+ spectre_cmd => {
26
25
  validator: proc do |stdout|
27
26
  VULNERABILITIES_TO_CHECK.each do |id, name|
28
27
  id_regexp = /#{Regexp.escape(id)}/
@@ -54,6 +54,7 @@ module HybridPlatformsConductor
54
54
  current_url
55
55
  end
56
56
  )
57
+ sudo = @nodes_handler.sudo_on(@node)
57
58
  Hash[urls.map do |url|
58
59
  # 1. Get the OVAL file on the node to be tested (uncompress it if needed)
59
60
  # 2. Make sure oscap is installed
@@ -74,9 +75,9 @@ module HybridPlatformsConductor
74
75
  #{
75
76
  case image
76
77
  when :centos_7
77
- "sudo yum install -y wget openscap-scanner #{packages_to_install.join(' ')}"
78
+ "#{sudo} yum install -y wget openscap-scanner #{packages_to_install.join(' ')}"
78
79
  when :debian_9
79
- "sudo apt install -y wget libopenscap8 #{packages_to_install.join(' ')}"
80
+ "#{sudo} apt install -y wget libopenscap8 #{packages_to_install.join(' ')}"
80
81
  when :debian_10
81
82
  # On Debian 10 we have to compile it from sources, as the packaged official version has core dumps.
82
83
  # cf https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1688223.html
@@ -86,13 +87,13 @@ module HybridPlatformsConductor
86
87
  rm -rf openscap
87
88
  git clone --recurse-submodules https://github.com/OpenSCAP/openscap.git
88
89
  cd openscap
89
- sudo apt install -y cmake libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev libgcrypt20-dev libselinux1-dev libxslt1-dev libgconf2-dev libacl1-dev libblkid-dev libcap-dev libxml2-dev libldap2-dev libpcre3-dev python-dev swig libxml-parser-perl libxml-xpath-perl libperl-dev libbz2-dev librpm-dev g++ libapt-pkg-dev libyaml-dev
90
+ #{sudo} apt install -y cmake libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev libgcrypt20-dev libselinux1-dev libxslt1-dev libgconf2-dev libacl1-dev libblkid-dev libcap-dev libxml2-dev libldap2-dev libpcre3-dev python-dev swig libxml-parser-perl libxml-xpath-perl libperl-dev libbz2-dev librpm-dev g++ libapt-pkg-dev libyaml-dev
90
91
  cd build
91
92
  cmake ../
92
93
  make
93
- sudo make install
94
+ #{sudo} make install
94
95
  fi
95
- sudo apt install -y wget #{packages_to_install.join(' ')}
96
+ #{sudo} apt install -y wget #{packages_to_install.join(' ')}
96
97
  EOS2
97
98
  else
98
99
  raise "Non supported image: #{image}. Please adapt this test's code."
@@ -103,7 +104,7 @@ module HybridPlatformsConductor
103
104
  cd hpc_vulnerabilities_test
104
105
  wget -N #{url}
105
106
  #{uncompress_cmds.join("\n")}
106
- sudo oscap oval eval --skip-valid --results "#{local_oval_file}.results.xml" "#{local_oval_file}"
107
+ #{sudo} oscap oval eval --skip-valid --results "#{local_oval_file}.results.xml" "#{local_oval_file}"
107
108
  echo "===== RESULTS ====="
108
109
  cat "#{local_oval_file}.results.xml"
109
110
  cd ..
@@ -18,9 +18,20 @@ module HybridPlatformsConductor
18
18
  # Array< Hash<Symbol, Object> >
19
19
  attr_reader :cmdb_masters
20
20
 
21
+ # List of sudo methods. Each info has the following properties:
22
+ # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
23
+ # * *sudo_proc* (Proc): Code giving the sudo line for a given user
24
+ # Parameters::
25
+ # * *user* (String): User for which we want sudo
26
+ # Result::
27
+ # * String: Corresponding sudo string
28
+ # Array< Hash<Symbol, Object> >
29
+ attr_reader :sudo_procs
30
+
21
31
  # Mixin initializer
22
32
  def init_nodes_handler_config
23
33
  @cmdb_masters = []
34
+ @sudo_procs = []
24
35
  end
25
36
 
26
37
  # Set CMDB masters
@@ -34,6 +45,17 @@ module HybridPlatformsConductor
34
45
  }
35
46
  end
36
47
 
48
+ # Set a sudo proc
49
+ #
50
+ # Parameters::
51
+ # * *sudo_proc* (Proc): The sudo proc (see #sudo_procs doc to know the signature)
52
+ def sudo_for(&sudo_proc)
53
+ @sudo_procs << {
54
+ nodes_selectors_stack: current_nodes_selectors_stack,
55
+ sudo_proc: sudo_proc
56
+ }
57
+ end
58
+
37
59
  end
38
60
 
39
61
  Config.extend_config_dsl_with ConfigDSLExtension, :init_nodes_handler_config
@@ -568,6 +590,21 @@ module HybridPlatformsConductor
568
590
  nodes_selector_stack.inject(known_nodes) { |selected_nodes, nodes_selector| selected_nodes & select_nodes(nodes_selector) }
569
591
  end
570
592
 
593
+ # Get the sudo command for a given user on a given node
594
+ #
595
+ # Parameters::
596
+ # * *node* (String): Node on which we need sudo
597
+ # * *user* (String): User for which we need sudo [default = 'root']
598
+ # Result::
599
+ # * String: The corresponding sudo string
600
+ def sudo_on(node, user = 'root')
601
+ sudo = nil
602
+ select_confs_for_node(node, @config.sudo_procs).each do |sudo_proc_info|
603
+ sudo = sudo_proc_info[:sudo_proc].call(user)
604
+ end
605
+ sudo.nil? ? "sudo -u #{user}" : sudo
606
+ end
607
+
571
608
  private
572
609
 
573
610
  # Get the CMDB master for a given property.
@@ -12,14 +12,9 @@ module HybridPlatformsConductor
12
12
 
13
13
  class << self
14
14
 
15
- # List of deployments that have been packaged.
16
- # Each deployment has the following info:
17
- # * *platform_name* (String): The platform name
18
- # * *services* (Hash< String, Array<String> >): Services to be deployed, per node
19
- # * *secrets* (Hash): Secrets for which this has been packaged
20
- # * *local_environment* (Boolean): Has it been packaged for local environment?
15
+ # List of package IDs that have been packaged.
21
16
  # Make this at class level as several Deployer instances can be used in a multi-thread environmnent.
22
- # Array< Hash<Symbol, Object> >
17
+ # Array<Object>
23
18
  attr_reader :packaged_deployments
24
19
 
25
20
  end
@@ -114,21 +109,22 @@ module HybridPlatformsConductor
114
109
  )
115
110
  platforms_for(services).each do |platform, platform_services|
116
111
  platform_name = platform.name
117
- deployment_info = {
112
+ # Compute the package ID that is unique to this packaging, so that we don't mix it with others if needed.
113
+ package_id = {
118
114
  platform_name: platform_name,
119
- services: platform_services,
120
- secrets: secrets,
115
+ services: Hash[platform_services.map { |node, node_services| [node, node_services.sort] }].sort,
116
+ secrets: secrets.sort,
121
117
  local_environment: local_environment
122
118
  }
123
- if ServicesHandler.packaged_deployments.include?(deployment_info)
124
- log_debug "Platform #{platform_name} has already been packaged for this deployment. Won't package it another time."
119
+ if ServicesHandler.packaged_deployments.include?(package_id)
120
+ log_debug "Platform #{platform_name} has already been packaged for this deployment (package ID #{package_id}). Won't package it another time."
125
121
  else
126
122
  platform.package(
127
123
  services: platform_services,
128
124
  secrets: secrets,
129
125
  local_environment: local_environment
130
126
  )
131
- ServicesHandler.packaged_deployments << deployment_info
127
+ ServicesHandler.packaged_deployments << package_id
132
128
  end
133
129
  end
134
130
  end
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '32.4.1'
3
+ VERSION = '32.7.1'
4
4
 
5
5
  end
@@ -43,6 +43,41 @@ describe HybridPlatformsConductor::ActionsExecutor do
43
43
  end
44
44
  end
45
45
 
46
+ it 'returns ssh transformation procs' do
47
+ with_test_platform(
48
+ {
49
+ nodes: {
50
+ 'node1' => {},
51
+ 'node2' => {},
52
+ 'node3' => {}
53
+ },
54
+ },
55
+ false,
56
+ '
57
+ for_nodes(%w[node1 node3]) do
58
+ transform_ssh_connection do |node, connection, connection_user, gateway, gateway_user|
59
+ ["#{connection}_#{node}_13", "#{connection_user}_#{node}_13", "#{gateway}_#{node}_13", "#{gateway_user}_#{node}_13"]
60
+ end
61
+ end
62
+ for_nodes(\'node1\') do
63
+ transform_ssh_connection do |node, connection, connection_user, gateway, gateway_user|
64
+ ["#{connection}_#{node}_1", "#{connection_user}_#{node}_1", "#{gateway}_#{node}_1", "#{gateway_user}_#{node}_1"]
65
+ end
66
+ end
67
+ '
68
+ ) do
69
+ expect(test_config.ssh_connection_transforms.size).to eq 2
70
+ expect(test_config.ssh_connection_transforms[0][:nodes_selectors_stack]).to eq [%w[node1 node3]]
71
+ expect(test_config.ssh_connection_transforms[0][:transform].call('node1', 'test_host', 'test_user', 'test_gateway', 'test_gateway_user')).to eq [
72
+ 'test_host_node1_13', 'test_user_node1_13', 'test_gateway_node1_13', 'test_gateway_user_node1_13'
73
+ ]
74
+ expect(test_config.ssh_connection_transforms[1][:nodes_selectors_stack]).to eq ['node1']
75
+ expect(test_config.ssh_connection_transforms[1][:transform].call('node1', 'test_host', 'test_user', 'test_gateway', 'test_gateway_user')).to eq [
76
+ 'test_host_node1_1', 'test_user_node1_1', 'test_gateway_node1_1', 'test_gateway_user_node1_1'
77
+ ]
78
+ end
79
+ end
80
+
46
81
  end
47
82
 
48
83
  end
@@ -28,6 +28,22 @@ describe HybridPlatformsConductor::ActionsExecutor do
28
28
  end
29
29
  end
30
30
 
31
+ it 'creates an SSH master to 1 node not having Session Exec capabilities' do
32
+ with_test_platform(nodes: { 'node' => { meta: { host_ip: '192.168.42.42', ssh_session_exec: 'false' } } }) do
33
+ with_cmd_runner_mocked(
34
+ [
35
+ ['which env', proc { [0, "/usr/bin/env\n", ''] }],
36
+ ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }]
37
+ ] + ssh_expected_commands_for({ 'node' => { connection: '192.168.42.42', user: 'test_user' } }, with_session_exec: false)
38
+ ) do
39
+ test_connector.ssh_user = 'test_user'
40
+ test_connector.with_connection_to(['node']) do |connected_nodes|
41
+ expect(connected_nodes).to eq ['node']
42
+ end
43
+ end
44
+ end
45
+ end
46
+
31
47
  it 'creates SSH master to several nodes' do
32
48
  with_test_platform(nodes: {
33
49
  'node1' => { meta: { host_ip: '192.168.42.1' } },
@@ -52,6 +68,45 @@ describe HybridPlatformsConductor::ActionsExecutor do
52
68
  end
53
69
  end
54
70
 
71
+ it 'creates SSH master to several nodes with ssh connections transformed' do
72
+ with_test_platform(
73
+ { nodes: {
74
+ 'node1' => { meta: { host_ip: '192.168.42.1' } },
75
+ 'node2' => { meta: { host_ip: '192.168.42.2' } },
76
+ 'node3' => { meta: { host_ip: '192.168.42.3' } }
77
+ } },
78
+ false,
79
+ '
80
+ for_nodes(%w[node1 node3]) do
81
+ transform_ssh_connection do |node, connection, connection_user, gateway, gateway_user|
82
+ ["#{connection}_#{node}_13", "#{connection_user}_#{node}_13", "#{gateway}_#{node}_13", "#{gateway_user}_#{node}_13"]
83
+ end
84
+ end
85
+ for_nodes(\'node1\') do
86
+ transform_ssh_connection do |node, connection, connection_user, gateway, gateway_user|
87
+ ["#{connection}_#{node}_1", "#{connection_user}_#{node}_1", "#{gateway}_#{node}_1", "#{gateway_user}_#{node}_1"]
88
+ end
89
+ end
90
+ '
91
+ ) do
92
+ with_cmd_runner_mocked(
93
+ [
94
+ ['which env', proc { [0, "/usr/bin/env\n", ''] }],
95
+ ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }]
96
+ ] + ssh_expected_commands_for(
97
+ 'node1' => { ip: '192.168.42.1', connection: '192.168.42.1_node1_13_node1_1', user: 'test_user_node1_13_node1_1' },
98
+ 'node2' => { ip: '192.168.42.2', connection: '192.168.42.2', user: 'test_user' },
99
+ 'node3' => { ip: '192.168.42.3', connection: '192.168.42.3_node3_13', user: 'test_user_node3_13' }
100
+ )
101
+ ) do
102
+ test_connector.ssh_user = 'test_user'
103
+ test_connector.with_connection_to(%w[node1 node2 node3]) do |connected_nodes|
104
+ expect(connected_nodes.sort).to eq %w[node1 node2 node3].sort
105
+ end
106
+ end
107
+ end
108
+ end
109
+
55
110
  it 'fails when an SSH master can\'t be created' do
56
111
  with_test_platform(nodes: {
57
112
  'node1' => { meta: { host_ip: '192.168.42.1' } },
@@ -63,8 +118,12 @@ describe HybridPlatformsConductor::ActionsExecutor do
63
118
  ['which env', proc { [0, "/usr/bin/env\n", ''] }],
64
119
  ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }]
65
120
  ] + ssh_expected_commands_for(
66
- 'node1' => { connection: '192.168.42.1', user: 'test_user' },
67
- 'node3' => { connection: '192.168.42.3', user: 'test_user' }
121
+ {
122
+ 'node1' => { connection: '192.168.42.1', user: 'test_user' },
123
+ 'node3' => { connection: '192.168.42.3', user: 'test_user' }
124
+ },
125
+ # Here the threads for node1's and node3's ControlMasters might not trigger before the one for node2, so they will not destroy it.
126
+ with_control_master_destroy_optional: true
68
127
  ) + ssh_expected_commands_for(
69
128
  {
70
129
  'node2' => { connection: '192.168.42.2', user: 'test_user', control_master_create_error: 'Can\'t connect to 192.168.42.2' }
@@ -73,7 +132,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
73
132
  )
74
133
  ) do
75
134
  test_connector.ssh_user = 'test_user'
76
- expect { test_connector.with_connection_to(%w[node1 node2 node3]) }.to raise_error(/^Error while starting SSH Control Master with .+\/ssh -o BatchMode=yes -o ControlMaster=yes -o ControlPersist=yes test_user@hpc.node2 true: Can't connect to 192.168.42.2$/)
135
+ expect { test_connector.with_connection_to(%w[node1 node2 node3]) }.to raise_error(/^Error while starting SSH Control Master with .+\/ssh -o BatchMode=yes -o ControlMaster=yes -o ControlPersist=yes hpc.node2 true: Can't connect to 192.168.42.2$/)
77
136
  end
78
137
  end
79
138
  end
@@ -239,7 +298,8 @@ describe HybridPlatformsConductor::ActionsExecutor do
239
298
  ) do
240
299
  test_connector.ssh_use_control_master = false
241
300
  test_connector.ssh_user = 'test_user'
242
- test_connector.with_connection_to(['node']) do
301
+ test_connector.with_connection_to(['node']) do |connected_nodes|
302
+ expect(connected_nodes).to eq %w[node]
243
303
  end
244
304
  end
245
305
  end
@@ -416,7 +476,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
416
476
  ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }],
417
477
  ] +
418
478
  [[
419
- /^.+\/ssh -o BatchMode=yes -o ControlMaster=yes -o ControlPersist=yes test_user@hpc\.node true$/,
479
+ /^.+\/ssh -o BatchMode=yes -o ControlMaster=yes -o ControlPersist=yes hpc\.node true$/,
420
480
  proc do
421
481
  nbr_boot_messages += 1
422
482
  [255, '', "System is booting up. See pam_nologin(8)\nAuthentication failed.\n"]
@@ -35,8 +35,11 @@ describe HybridPlatformsConductor::ActionsExecutor do
35
35
  begin_marker = node.nil? ? /^Host \*$/ : /^# #{Regexp.escape(node)} - .+$/
36
36
  start_idx = ssh_config_lines.index { |line| line =~ begin_marker }
37
37
  return nil if start_idx.nil?
38
- end_marker = /^# \w+ - .+$/
39
- end_idx = ssh_config_lines[start_idx + 1..-1].index { |line| line =~ end_marker }
38
+ end_markers = [
39
+ /^\# \w+ - .+$/,
40
+ /^\#+$/
41
+ ]
42
+ end_idx = ssh_config_lines[start_idx + 1..-1].index { |line| end_markers.any? { |end_marker| line =~ end_marker } }
40
43
  end_idx = end_idx.nil? ? -1 : start_idx + end_idx
41
44
  ssh_config_lines[start_idx..end_idx].select do |line|
42
45
  stripped_line = line.strip
@@ -50,7 +53,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
50
53
  expect(ssh_config_for(nil)).to eq <<~EOS
51
54
  Host *
52
55
  User test_user
53
- ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_actions_executor_mux_%h_%p_%r
56
+ ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_ssh_mux_%h_%p_%r
54
57
  PubkeyAcceptedKeyTypes +ssh-dss
55
58
  EOS
56
59
  end
@@ -62,7 +65,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
62
65
  expect(ssh_config_for(nil)).to eq <<~EOS
63
66
  Host *
64
67
  User test_user
65
- ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_actions_executor_mux_%h_%p_%r
68
+ ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_ssh_mux_%h_%p_%r
66
69
  PubkeyAcceptedKeyTypes +ssh-dss
67
70
  EOS
68
71
  end
@@ -74,7 +77,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
74
77
  expect(ssh_config_for(nil, known_hosts_file: '/path/to/known_hosts')).to eq <<~EOS
75
78
  Host *
76
79
  User test_user
77
- ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_actions_executor_mux_%h_%p_%r
80
+ ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_ssh_mux_%h_%p_%r
78
81
  PubkeyAcceptedKeyTypes +ssh-dss
79
82
  UserKnownHostsFile /path/to/known_hosts
80
83
  EOS
@@ -88,7 +91,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
88
91
  expect(ssh_config_for(nil)).to eq <<~EOS
89
92
  Host *
90
93
  User test_user
91
- ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_actions_executor_mux_%h_%p_%r
94
+ ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_ssh_mux_%h_%p_%r
92
95
  PubkeyAcceptedKeyTypes +ssh-dss
93
96
  StrictHostKeyChecking no
94
97
  EOS
@@ -153,6 +156,24 @@ describe HybridPlatformsConductor::ActionsExecutor do
153
156
  end
154
157
  end
155
158
 
159
+ it 'generates a simple config for several nodes even when some of them can\'t be connected' do
160
+ with_test_platform(nodes: {
161
+ 'node1' => { meta: { host_ip: '192.168.42.1' } },
162
+ 'node2' => { meta: {} },
163
+ 'node3' => { meta: { host_ip: '192.168.42.3' } }
164
+ }) do
165
+ expect(ssh_config_for('node1')).to eq <<~EOS
166
+ Host hpc.node1
167
+ Hostname 192.168.42.1
168
+ EOS
169
+ expect(ssh_config_for('node2')).to eq "\n"
170
+ expect(ssh_config_for('node3')).to eq <<~EOS
171
+ Host hpc.node3
172
+ Hostname 192.168.42.3
173
+ EOS
174
+ end
175
+ end
176
+
156
177
  it 'selects nodes when generating the config' do
157
178
  with_test_platform(nodes: {
158
179
  'node1' => { meta: { host_ip: '192.168.42.1' } },
@@ -167,12 +188,6 @@ describe HybridPlatformsConductor::ActionsExecutor do
167
188
  end
168
189
  end
169
190
 
170
- it 'fails if a node can\'t be connected to' do
171
- with_test_platform(nodes: { 'node' => {} }) do
172
- expect { ssh_config_for('node') }.to raise_error(/No connection possible to node/)
173
- end
174
- end
175
-
176
191
  it 'generates an alias if the node has a hostname' do
177
192
  with_test_platform(nodes: { 'node' => { meta: { host_ip: '192.168.42.42', hostname: 'my_hostname.my_domain' } } }) do
178
193
  expect(ssh_config_for('node')).to eq <<~EOS
@@ -262,6 +277,47 @@ describe HybridPlatformsConductor::ActionsExecutor do
262
277
  end
263
278
  end
264
279
 
280
+ it 'uses node transformed SSH connection' do
281
+ with_test_platform(
282
+ { nodes: {
283
+ 'node1' => { meta: { host_ip: '192.168.42.1', gateway: 'test_gateway1', gateway_user: 'test_gateway1_user' } },
284
+ 'node2' => { meta: { host_ip: '192.168.42.2', gateway: 'test_gateway2', gateway_user: 'test_gateway2_user' } },
285
+ 'node3' => { meta: { host_ip: '192.168.42.3', gateway: 'test_gateway3', gateway_user: 'test_gateway3_user' } }
286
+ } },
287
+ false,
288
+ '
289
+ for_nodes(%w[node1 node3]) do
290
+ transform_ssh_connection do |node, connection, connection_user, gateway, gateway_user|
291
+ ["#{connection}_#{node}_13", "#{connection_user}_#{node}_13", "#{gateway}_#{node}_13", "#{gateway_user}_#{node}_13"]
292
+ end
293
+ end
294
+ for_nodes(\'node1\') do
295
+ transform_ssh_connection do |node, connection, connection_user, gateway, gateway_user|
296
+ ["#{connection}_#{node}_1", "#{connection_user}_#{node}_1", "#{gateway}_#{node}_1", "#{gateway_user}_#{node}_1"]
297
+ end
298
+ end
299
+ ') do
300
+ test_connector.ssh_user = 'test_user'
301
+ expect(ssh_config_for('node1')).to eq <<~EOS
302
+ Host hpc.node1
303
+ Hostname 192.168.42.1_node1_13_node1_1
304
+ User "test_user_node1_13_node1_1"
305
+ ProxyCommand ssh -q -W %h:%p test_gateway1_user_node1_13_node1_1@test_gateway1_node1_13_node1_1
306
+ EOS
307
+ expect(ssh_config_for('node2')).to eq <<~EOS
308
+ Host hpc.node2
309
+ Hostname 192.168.42.2
310
+ ProxyCommand ssh -q -W %h:%p test_gateway2_user@test_gateway2
311
+ EOS
312
+ expect(ssh_config_for('node3')).to eq <<~EOS
313
+ Host hpc.node3
314
+ Hostname 192.168.42.3_node3_13
315
+ User "test_user_node3_13"
316
+ ProxyCommand ssh -q -W %h:%p test_gateway3_user_node3_13@test_gateway3_node3_13
317
+ EOS
318
+ end
319
+ end
320
+
265
321
  it 'generates a config compatible for passwords authentication' do
266
322
  with_test_platform(nodes: { 'node' => { meta: { host_ip: '192.168.42.42' } } }) do
267
323
  test_connector.passwords['node'] = 'PaSsWoRd'