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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e0dfe56d0297ba7c9c90dfbf4737cd6b4754b20a36f7dec0962d62a19b5bf2b
4
- data.tar.gz: 3a0363efcb7dc771c9cf41911d3012fae0a764a465e4bd925f241a422d5f11b0
3
+ metadata.gz: 04d2e323c123bfaa35c0fb61f95dcc7a9ade67b19f184eff75c61419e249ac8e
4
+ data.tar.gz: 25afb265a466103db73b891c62ec9a9ee32e5529bf8d9f0df71df379bab5ec7a
5
5
  SHA512:
6
- metadata.gz: e1dae403085c924a043dfee0185b46478eff9c99fa1d69848f5e92b571e4dfb1dc62ab1f80792d3b1928323eecf72a8f0904317b17facf3b92b6a07b82381112
7
- data.tar.gz: 6156b57048a690c5bc6e91ffd61a8276b84b2a2196206a8b77779ee25fe559aa4a4ec21bf741f015736cb3796b9bf4c33d9ec0203b4e4ff7c44a77c1d06fbe83
6
+ metadata.gz: d8112983393928123c7f8f35395506c4ec5e401e84577e3ab1e24380ab0cd92affd90b6be0cb75645314d06cc8350cd8c478e1d1b9e3c600143296bdec73af47
7
+ data.tar.gz: 6177c45b5489b077ee2d3e6d7535d6c07e94715c755c282ea8dc424e54d35ac1603c4be9d2e2d9b4cbfc4f450463c792a3e449f6dbbc5eb6bba4a0161c695c65
@@ -480,6 +480,7 @@ module HybridPlatformsConductor
480
480
  outputs = @actions_executor.execute_actions(
481
481
  Hash[services.map do |node, node_services|
482
482
  image_id = @nodes_handler.get_image_of(node)
483
+ sudo = (ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} ")
483
484
  # Install My_company corporate certificates if present
484
485
  certificate_actions =
485
486
  if @local_environment && ENV['hpc_certificates']
@@ -489,20 +490,20 @@ module HybridPlatformsConductor
489
490
  when 'debian_9', 'debian_10'
490
491
  [
491
492
  {
492
- remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}apt update && #{ssh_user == 'root' ? '' : 'sudo '}apt install -y ca-certificates"
493
+ remote_bash: "#{sudo}apt update && #{sudo}apt install -y ca-certificates"
493
494
  },
494
495
  {
495
496
  scp: {
496
497
  ENV['hpc_certificates'] => '/usr/local/share/ca-certificates',
497
498
  :sudo => ssh_user != 'root'
498
499
  },
499
- remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}update-ca-certificates"
500
+ remote_bash: "#{sudo}update-ca-certificates"
500
501
  }
501
502
  ]
502
503
  when 'centos_7'
503
504
  [
504
505
  {
505
- remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}yum install -y ca-certificates"
506
+ remote_bash: "#{sudo}yum install -y ca-certificates"
506
507
  },
507
508
  {
508
509
  scp: Hash[Dir.glob("#{ENV['hpc_certificates']}/*.crt").map do |cert_file|
@@ -512,8 +513,8 @@ module HybridPlatformsConductor
512
513
  ]
513
514
  end].merge(sudo: ssh_user != 'root'),
514
515
  remote_bash: [
515
- "#{ssh_user == 'root' ? '' : 'sudo '}update-ca-trust enable",
516
- "#{ssh_user == 'root' ? '' : 'sudo '}update-ca-trust extract"
516
+ "#{sudo}update-ca-trust enable",
517
+ "#{sudo}update-ca-trust extract"
517
518
  ]
518
519
  }
519
520
  ]
@@ -532,7 +533,7 @@ module HybridPlatformsConductor
532
533
  # Install the mutex lock and acquire it
533
534
  {
534
535
  scp: { "#{__dir__}/mutex_dir" => '.' },
535
- remote_bash: "while ! #{ssh_user == 'root' ? '' : 'sudo '}./mutex_dir lock /tmp/hybrid_platforms_conductor_deploy_lock \"$(ps -o ppid= -p $$)\"; do echo -e 'Another deployment is running on #{node}. Waiting for it to finish to continue...' ; sleep 5 ; done"
536
+ remote_bash: "while ! #{sudo}./mutex_dir lock /tmp/hybrid_platforms_conductor_deploy_lock \"$(ps -o ppid= -p $$)\"; do echo -e 'Another deployment is running on #{node}. Waiting for it to finish to continue...' ; sleep 5 ; done"
536
537
  }
537
538
  ] +
538
539
  certificate_actions +
@@ -548,7 +549,7 @@ module HybridPlatformsConductor
548
549
  Hash[services.keys.map do |node|
549
550
  [
550
551
  node,
551
- { remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}./mutex_dir unlock /tmp/hybrid_platforms_conductor_deploy_lock" }
552
+ { remote_bash: "#{ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} "}./mutex_dir unlock /tmp/hybrid_platforms_conductor_deploy_lock" }
552
553
  ]
553
554
  end],
554
555
  timeout: 10,
@@ -595,7 +596,7 @@ module HybridPlatformsConductor
595
596
  [
596
597
  node,
597
598
  {
598
- remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}mkdir -p /var/log/deployments",
599
+ remote_bash: "#{ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} "}mkdir -p /var/log/deployments",
599
600
  scp: {
600
601
  log_file => '/var/log/deployments',
601
602
  :sudo => ssh_user != 'root',
@@ -10,13 +10,46 @@ module HybridPlatformsConductor
10
10
  # Connect to node using SSH
11
11
  class Ssh < HybridPlatformsConductor::Connector
12
12
 
13
+ # Exception raise when a node is not connectable using SSH
14
+ class NotConnectableError < RuntimeError
15
+ end
16
+
13
17
  module PlatformsDslSsh
14
18
 
19
+ # List of SSH connection transformations:
20
+ # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
21
+ # * *transform* (Proc): Code called to transform SSH connection info:
22
+ # Parameters::
23
+ # * *node* (String): Node for which we transform the SSH connection
24
+ # * *connection* (String or nil): The connection host or IP, or nil if none
25
+ # * *connection_user* (String): The connection user
26
+ # * *gateway* (String or nil): The gateway name, or nil if none
27
+ # * *gateway_user* (String or nil): The gateway user, or nil if none
28
+ # Result::
29
+ # * String: The transformed connection host or IP, or nil if none
30
+ # * String: The transformed connection user
31
+ # * String or nil: The transformed gateway name, or nil if none
32
+ # * String or nil: The transformed gateway user, or nil if none
33
+ # Array< Hash<Symbol, Object> >
34
+ attr_reader :ssh_connection_transforms
35
+
15
36
  # Initialize the DSL
16
37
  def init_ssh
17
38
  # List of gateway configurations, per gateway config name
18
39
  # Hash<Symbol, String>
19
40
  @gateways = {}
41
+ @ssh_connection_transforms = []
42
+ end
43
+
44
+ # Define a transformation of SSH connection.
45
+ #
46
+ # Parameters::
47
+ # * *transform* (Proc): Code to be called to transform an SSH connection (see ssh_connection_transforms signature for details)
48
+ def transform_ssh_connection(&transform)
49
+ @ssh_connection_transforms << {
50
+ nodes_selectors_stack: current_nodes_selectors_stack,
51
+ transform: transform
52
+ }
20
53
  end
21
54
 
22
55
  # Register a new gateway configuration
@@ -200,7 +233,13 @@ module HybridPlatformsConductor
200
233
  # Parameters::
201
234
  # * *bash_cmds* (String): Bash commands to execute
202
235
  def remote_bash(bash_cmds)
203
- ssh_cmd = "#{ssh_exec} #{ssh_url} /bin/bash <<'EOF'\n#{bash_cmds}\nEOF"
236
+ ssh_cmd =
237
+ if @nodes_handler.get_ssh_session_exec_of(@node) == 'false'
238
+ # When ExecSession is disabled we need to use stdin directly
239
+ "{ cat | #{ssh_exec} #{ssh_url} -T; } <<'EOF'\n#{bash_cmds}\nEOF"
240
+ else
241
+ "#{ssh_exec} #{ssh_url} /bin/bash <<'EOF'\n#{bash_cmds}\nEOF"
242
+ end
204
243
  # Due to a limitation of Process.spawn, each individual argument is limited to 128KB of size.
205
244
  # Therefore we need to make sure that if bash_cmds exceeds MAX_CMD_ARG_LENGTH bytes (considering EOF chars) then we use an intermediary shell script to store the commands.
206
245
  if bash_cmds.size > MAX_CMD_ARG_LENGTH
@@ -257,25 +296,30 @@ module HybridPlatformsConductor
257
296
  # * *owner* (String or nil): Owner to be used when copying the files, or nil for current one [default: nil]
258
297
  # * *group* (String or nil): Group to be used when copying the files, or nil for current one [default: nil]
259
298
  def remote_copy(from, to, sudo: false, owner: nil, group: nil)
260
- run_cmd <<~EOS
261
- cd #{File.dirname(from)} && \
262
- tar \
263
- --create \
264
- --gzip \
265
- --file - \
266
- #{owner.nil? ? '' : "--owner #{owner}"} \
267
- #{group.nil? ? '' : "--group #{group}"} \
268
- #{File.basename(from)} | \
269
- #{ssh_exec} \
270
- #{ssh_url} \
271
- \"#{sudo ? 'sudo ' : ''}tar \
272
- --extract \
273
- --gunzip \
299
+ if @nodes_handler.get_ssh_session_exec_of(@node) == 'false'
300
+ # We don't have ExecSession, so don't use ssh, but scp instead.
301
+ run_cmd "scp -S #{ssh_exec} #{from} #{ssh_url}:#{to}"
302
+ else
303
+ run_cmd <<~EOS
304
+ cd #{File.dirname(from)} && \
305
+ tar \
306
+ --create \
307
+ --gzip \
274
308
  --file - \
275
- --directory #{to} \
276
- --owner root \
277
- \"
278
- EOS
309
+ #{owner.nil? ? '' : "--owner #{owner}"} \
310
+ #{group.nil? ? '' : "--group #{group}"} \
311
+ #{File.basename(from)} | \
312
+ #{ssh_exec} \
313
+ #{ssh_url} \
314
+ \"#{sudo ? "#{@nodes_handler.sudo_on(@node)} " : ''}tar \
315
+ --extract \
316
+ --gunzip \
317
+ --file - \
318
+ --directory #{to} \
319
+ --owner root \
320
+ \"
321
+ EOS
322
+ end
279
323
  end
280
324
 
281
325
  # Get the ssh executable to be used when connecting to the current node
@@ -291,7 +335,7 @@ module HybridPlatformsConductor
291
335
  # Result::
292
336
  # * String: The ssh URL connecting to the current node
293
337
  def ssh_url
294
- "#{@ssh_user}@hpc.#{@node}"
338
+ "hpc.#{@node}"
295
339
  end
296
340
 
297
341
  # Get an SSH configuration content giving access to nodes of the platforms with the current configuration
@@ -314,14 +358,6 @@ module HybridPlatformsConductor
314
358
  # ENDPOINTS #
315
359
  #############
316
360
 
317
- Host *
318
- User #{@ssh_user}
319
- # Default control socket path to be used when multiplexing SSH connections
320
- ControlPath #{control_master_file('%h', '%p', '%r')}
321
- #{open_ssh_major_version >= 7 ? 'PubkeyAcceptedKeyTypes +ssh-dss' : ''}
322
- #{known_hosts_file.nil? ? '' : "UserKnownHostsFile #{known_hosts_file}"}
323
- #{@ssh_strict_host_key_checking ? '' : 'StrictHostKeyChecking no'}
324
-
325
361
  EOS
326
362
 
327
363
  # Add each node
@@ -329,17 +365,37 @@ module HybridPlatformsConductor
329
365
  @nodes_handler.prefetch_metadata_of nodes, %i[private_ips hostname host_ip description]
330
366
  nodes.sort.each do |node|
331
367
  # Generate the conf for the node
332
- connection, gateway, gateway_user = connection_info_for(node)
333
- config_content << "# #{node} - #{connection} - #{@nodes_handler.get_description_of(node) || ''}\n"
334
- config_content << "Host #{ssh_aliases_for(node).join(' ')}\n"
335
- config_content << " Hostname #{connection}\n"
336
- config_content << " ProxyCommand #{ssh_exec} -q -W %h:%p #{gateway_user}@#{gateway}\n" unless gateway.nil?
337
- if @passwords.key?(node)
338
- config_content << " PreferredAuthentications password\n"
339
- config_content << " PubkeyAuthentication no\n"
368
+ connection, connection_user, gateway, gateway_user = connection_info_for(node, no_exception: true)
369
+ if connection.nil?
370
+ config_content << "# #{node} - Not connectable using SSH - #{@nodes_handler.get_description_of(node) || ''}\n"
371
+ else
372
+ config_content << "# #{node} - #{connection} - #{@nodes_handler.get_description_of(node) || ''}\n"
373
+ config_content << "Host #{ssh_aliases_for(node).join(' ')}\n"
374
+ config_content << " Hostname #{connection}\n"
375
+ config_content << " User \"#{connection_user}\"\n" if connection_user != @ssh_user
376
+ config_content << " ProxyCommand #{ssh_exec} -q -W %h:%p #{gateway_user}@#{gateway}\n" unless gateway.nil?
377
+ if @passwords.key?(node)
378
+ config_content << " PreferredAuthentications password\n"
379
+ config_content << " PubkeyAuthentication no\n"
380
+ end
340
381
  end
341
382
  config_content << "\n"
342
383
  end
384
+ # Add global definitions at the end of the SSH config, as they might be overriden by previous ones, and first match wins.
385
+ config_content << <<~EOS
386
+ ###########
387
+ # GLOBALS #
388
+ ###########
389
+
390
+ Host *
391
+ User #{@ssh_user}
392
+ # Default control socket path to be used when multiplexing SSH connections
393
+ ControlPath #{control_master_file('%h', '%p', '%r')}
394
+ #{open_ssh_major_version >= 7 ? 'PubkeyAcceptedKeyTypes +ssh-dss' : ''}
395
+ #{known_hosts_file.nil? ? '' : "UserKnownHostsFile #{known_hosts_file}"}
396
+ #{@ssh_strict_host_key_checking ? '' : 'StrictHostKeyChecking no'}
397
+
398
+ EOS
343
399
  config_content
344
400
  end
345
401
 
@@ -442,34 +498,48 @@ module HybridPlatformsConductor
442
498
  with_lock_on_control_master_for(node) do |current_users, user_id|
443
499
  working_master = false
444
500
  ssh_exec = ssh_exec_for(node)
445
- ssh_url = "#{@ssh_user}@hpc.#{node}"
501
+ ssh_url = "hpc.#{node}"
446
502
  if current_users.empty?
447
503
  log_debug "[ ControlMaster - #{ssh_url} ] - Creating SSH ControlMaster..."
448
- # Create the control master
449
- ssh_control_master_start_cmd = "#{ssh_exec}#{@passwords.key?(node) || @auth_password ? '' : ' -o BatchMode=yes'} -o ControlMaster=yes -o ControlPersist=yes #{ssh_url} true"
450
504
  exit_status = nil
451
- idx_try = 0
452
- loop do
453
- stderr = nil
454
- exit_status, _stdout, stderr = @cmd_runner.run_cmd ssh_control_master_start_cmd, log_to_stdout: log_debug?, no_exception: true, timeout: timeout
455
- if exit_status == 0
456
- break
457
- elsif stderr =~ /System is booting up/
458
- if idx_try == MAX_RETRIES_FOR_BOOT
459
- if no_exception
460
- break
461
- else
462
- raise ActionsExecutor::ConnectionError, "Tried #{idx_try} times to create SSH Control Master with #{ssh_control_master_start_cmd} but system says it's booting up."
505
+ if @nodes_handler.get_ssh_session_exec_of(node) == 'false'
506
+ # Here we have to create a ControlMaster using an interactive session, as the SSH server prohibits ExecSession, and so command executions.
507
+ # We'll do that using another terminal spawned in the background.
508
+ Thread.new do
509
+ log_debug "[ ControlMaster - #{ssh_url} ] - Spawn interactive ControlMaster in separate terminal"
510
+ @cmd_runner.run_cmd "xterm -e '#{ssh_exec} -o ControlMaster=yes -o ControlPersist=yes #{ssh_url}'", log_to_stdout: log_debug?
511
+ log_debug "[ ControlMaster - #{ssh_url} ] - Separate interactive ControlMaster closed"
512
+ end
513
+ out 'External ControlMaster has been spawned.'
514
+ out 'Please login into it, keep its session opened and press enter here when done...'
515
+ $stdin.gets
516
+ exit_status = 0
517
+ else
518
+ # Create the control master
519
+ ssh_control_master_start_cmd = "#{ssh_exec}#{@passwords.key?(node) || @auth_password ? '' : ' -o BatchMode=yes'} -o ControlMaster=yes -o ControlPersist=yes #{ssh_url} true"
520
+ idx_try = 0
521
+ loop do
522
+ stderr = nil
523
+ exit_status, _stdout, stderr = @cmd_runner.run_cmd ssh_control_master_start_cmd, log_to_stdout: log_debug?, no_exception: true, timeout: timeout
524
+ if exit_status == 0
525
+ break
526
+ elsif stderr =~ /System is booting up/
527
+ if idx_try == MAX_RETRIES_FOR_BOOT
528
+ if no_exception
529
+ break
530
+ else
531
+ raise ActionsExecutor::ConnectionError, "Tried #{idx_try} times to create SSH Control Master with #{ssh_control_master_start_cmd} but system says it's booting up."
532
+ end
463
533
  end
534
+ # Wait a bit and try again
535
+ idx_try += 1
536
+ log_debug "[ ControlMaster - #{ssh_url} ] - System is booting up (try ##{idx_try}). Wait #{WAIT_TIME_FOR_BOOT} seconds before trying ControlMaster's creation again."
537
+ sleep WAIT_TIME_FOR_BOOT
538
+ elsif no_exception
539
+ break
540
+ else
541
+ raise ActionsExecutor::ConnectionError, "Error while starting SSH Control Master with #{ssh_control_master_start_cmd}: #{stderr.strip}"
464
542
  end
465
- # Wait a bit and try again
466
- idx_try += 1
467
- log_debug "[ ControlMaster - #{ssh_url} ] - System is booting up (try ##{idx_try}). Wait #{WAIT_TIME_FOR_BOOT} seconds before trying ControlMaster's creation again."
468
- sleep WAIT_TIME_FOR_BOOT
469
- elsif no_exception
470
- break
471
- else
472
- raise ActionsExecutor::ConnectionError, "Error while starting SSH Control Master with #{ssh_control_master_start_cmd}: #{stderr.strip}"
473
543
  end
474
544
  end
475
545
  if exit_status == 0
@@ -504,28 +574,33 @@ module HybridPlatformsConductor
504
574
  end
505
575
  end
506
576
  end
577
+ else
578
+ # We have not created any ControlMaster, but still consider the nodes to be ready to connect
579
+ user_locks = Hash[nodes.map { |node| [node, nil]} ]
507
580
  end
508
581
  yield user_locks.keys
509
582
  ensure
510
- user_locks_mutex.synchronize do
511
- user_locks.each do |node, user_id|
512
- with_lock_on_control_master_for(node, user_id: user_id) do |current_users, user_id|
513
- ssh_url = "#{@ssh_user}@hpc.#{node}"
514
- log_warn "[ ControlMaster - #{ssh_url} ] - Current process/thread was not part of the ControlMaster users anymore whereas it should have been" unless current_users.include?(user_id)
515
- remaining_users = current_users - [user_id]
516
- if remaining_users.empty?
517
- # Stop the ControlMaster
518
- log_debug "[ ControlMaster - #{ssh_url} ] - Stopping ControlMaster..."
519
- # Dumb verbose ssh! Tricky trick to just silence what is useless.
520
- # Don't fail if the connection close fails (but still log the error), as it can be seen as only a warning: it means the connection was closed anyway.
521
- @cmd_runner.run_cmd "#{ssh_exec_for(node)} -O exit #{ssh_url} 2>&1 | grep -v 'Exit request sent.'", log_to_stdout: log_debug?, expected_code: 1, timeout: timeout, no_exception: true
522
- log_debug "[ ControlMaster - #{ssh_url} ] - ControlMaster stopped"
523
- # Uncomment if you want to test that the connection has been closed
524
- # @cmd_runner.run_cmd "#{ssh_exec_for(node)} -O check #{ssh_url}", log_to_stdout: log_debug?, expected_code: 255, timeout: timeout
525
- else
526
- log_debug "[ ControlMaster - #{ssh_url} ] - Leaving ControlMaster started as #{remaining_users.size} processes/threads are still using it."
583
+ if @ssh_use_control_master
584
+ user_locks_mutex.synchronize do
585
+ user_locks.each do |node, user_id|
586
+ with_lock_on_control_master_for(node, user_id: user_id) do |current_users, user_id|
587
+ ssh_url = "hpc.#{node}"
588
+ log_warn "[ ControlMaster - #{ssh_url} ] - Current process/thread was not part of the ControlMaster users anymore whereas it should have been" unless current_users.include?(user_id)
589
+ remaining_users = current_users - [user_id]
590
+ if remaining_users.empty?
591
+ # Stop the ControlMaster
592
+ log_debug "[ ControlMaster - #{ssh_url} ] - Stopping ControlMaster..."
593
+ # Dumb verbose ssh! Tricky trick to just silence what is useless.
594
+ # Don't fail if the connection close fails (but still log the error), as it can be seen as only a warning: it means the connection was closed anyway.
595
+ @cmd_runner.run_cmd "#{ssh_exec_for(node)} -O exit #{ssh_url} 2>&1 | grep -v 'Exit request sent.'", log_to_stdout: log_debug?, expected_code: 1, timeout: timeout, no_exception: true
596
+ log_debug "[ ControlMaster - #{ssh_url} ] - ControlMaster stopped"
597
+ # Uncomment if you want to test that the connection has been closed
598
+ # @cmd_runner.run_cmd "#{ssh_exec_for(node)} -O check #{ssh_url}", log_to_stdout: log_debug?, expected_code: 255, timeout: timeout
599
+ else
600
+ log_debug "[ ControlMaster - #{ssh_url} ] - Leaving ControlMaster started as #{remaining_users.size} processes/threads are still using it."
601
+ end
602
+ false
527
603
  end
528
- false
529
604
  end
530
605
  end
531
606
  end
@@ -554,8 +629,9 @@ module HybridPlatformsConductor
554
629
  # TODO: Add test case when control file is missing ad when it is stale
555
630
  # Get the list of existing process/thread ids using this control master
556
631
  existing_users = File.exist?(control_master_users_file) ? File.read(control_master_users_file).split("\n") : []
557
- ssh_url = "#{@ssh_user}@hpc.#{node}"
558
- control_path_file = control_master_file(connection_info_for(node).first, '22', @ssh_user)
632
+ ssh_url = "hpc.#{node}"
633
+ connection, connection_user, _gateway, _gateway_user = connection_info_for(node)
634
+ control_path_file = control_master_file(connection, '22', connection_user)
559
635
  if existing_users.empty?
560
636
  # Make sure there is no stale one.
561
637
  if File.exist?(control_path_file)
@@ -587,7 +663,7 @@ module HybridPlatformsConductor
587
663
  # * *port* (String): The port. Can be a string as ssh config uses wildchars.
588
664
  # * *user* (String): The user
589
665
  def control_master_file(host, port, user)
590
- "#{@tmp_dir}/hpc_actions_executor_mux_#{host}_#{port}_#{user}"
666
+ "#{@tmp_dir}/hpc_ssh_mux_#{host}_#{port}_#{user}"
591
667
  end
592
668
 
593
669
  # Provide a bootstrapped ssh executable that includes an SSH config allowing access to nodes.
@@ -619,7 +695,7 @@ module HybridPlatformsConductor
619
695
  nodes.sort.each do |node|
620
696
  host_keys = @nodes_handler.get_host_keys_of(node)
621
697
  if host_keys && !host_keys.empty?
622
- connection, _gateway, _gateway_user = connection_info_for(node)
698
+ connection, _connection_user, _gateway, _gateway_user = connection_info_for(node)
623
699
  host_keys.each do |host_key|
624
700
  file.puts "#{connection} #{host_key}"
625
701
  end
@@ -652,11 +728,13 @@ module HybridPlatformsConductor
652
728
  #
653
729
  # Parameters::
654
730
  # * *node* (String): The node to access
731
+ # * *no_exception* (Boolean): Should we skip exceptions in case of no connection possible? [default: false]
655
732
  # Result::
656
- # * String: The real hostname or IP to be used to connect
733
+ # * String: The real hostname or IP to be used to connect, or nil if none and no_exception is true
734
+ # * String: The real user to be used to connect, or nil if none and no_exception is true
657
735
  # * String or nil: The gateway name to be used (should be defined by the gateways configurations), or nil if no gateway to be used.
658
736
  # * String or nil: The gateway user to be used, or nil if none.
659
- def connection_info_for(node)
737
+ def connection_info_for(node, no_exception: false)
660
738
  connection =
661
739
  if @nodes_handler.get_host_ip_of(node)
662
740
  @nodes_handler.get_host_ip_of(node)
@@ -665,12 +743,18 @@ module HybridPlatformsConductor
665
743
  elsif @nodes_handler.get_hostname_of(node)
666
744
  @nodes_handler.get_hostname_of(node)
667
745
  else
668
- raise "No connection possible to #{node}"
746
+ nil
669
747
  end
748
+ connection_user = @ssh_user
670
749
  gateway = @nodes_handler.get_gateway_of node
671
750
  gateway_user = @nodes_handler.get_gateway_user_of node
672
751
  gateway_user = @ssh_gateway_user if !gateway.nil? && gateway_user.nil?
673
- [connection, gateway, gateway_user]
752
+ # In case we want to transform the connection info, do it here.
753
+ @nodes_handler.select_confs_for_node(node, @config.ssh_connection_transforms).each do |transform_info|
754
+ connection, connection_user, gateway, gateway_user = transform_info[:transform].call(node, connection, connection_user, gateway, gateway_user)
755
+ end
756
+ raise NotConnectableError, "No connection possible to #{node}" if connection.nil? && !no_exception
757
+ [connection, connection_user, gateway, gateway_user]
674
758
  end
675
759
 
676
760
  # Get the possible SSH aliases for a given node.
@@ -173,7 +173,7 @@ module HybridPlatformsConductor
173
173
  # * *local_environment* (Boolean): Are we deploying to a local environment?
174
174
  def package(services:, secrets:, local_environment:)
175
175
  # This method should take all actions to prepare the repository to be deployed on nodes later.
176
- File.write('temporary_secrets_to_be_deployed.json', secrets)
176
+ File.write("#{@repository_path}/temporary_secrets_to_be_deployed.json", secrets.to_json)
177
177
  # Usually it is meant to package the deployment scripts.
178
178
  @cmd_runner.run_cmd "cd #{@repository_path} && ./scripts/package_in_debian_format.sh"
179
179
  end