hybrid_platforms_conductor 32.4.1 → 32.7.1

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