hybrid_platforms_conductor 33.5.1 → 33.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (22) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/README.md +2 -0
  4. data/docs/plugins/cmdb/host_keys.md +3 -1
  5. data/docs/plugins/connector/ssh.md +1 -0
  6. data/lib/hybrid_platforms_conductor/config.rb +2 -0
  7. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +13 -12
  8. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +4 -2
  9. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +23 -13
  10. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +2 -2
  11. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +1 -0
  12. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +17 -3
  13. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +30 -10
  14. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +1 -1
  15. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  16. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +30 -0
  17. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +10 -0
  18. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +11 -0
  19. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +49 -10
  20. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +26 -16
  21. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +5 -3
  22. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1e882e48d05f919ad5818c9251e045f941d78a9eacc16824f3fd49e787d91b5
4
- data.tar.gz: b685a6a1ca1f6f40714da2443ad7de95d78e90489f15a4b8499e153f4d84945a
3
+ metadata.gz: 143014298b9351f34fdfa3d4e69e0c91a918c3f6c6ed8faa9348cc36380e43e2
4
+ data.tar.gz: aa78731557e9650d06780a4b1505b28c206476b111d9f42171e4cab4cc46b829
5
5
  SHA512:
6
- metadata.gz: f9386d953b2e652fd453656a21a935470099ba3f935a82229829d5777408e93149833883d7acd277da6cf2f08ba5ca49c8c62d43d3207b34113c347299335e5f
7
- data.tar.gz: 9066d78f77fed46560b1884bade1074055da61547bd196c2a6479cf9412ba30f9d7518f91e05244307b703ebd241a620985e59f93a7911cd353543af26ae3699
6
+ metadata.gz: b13deb407182b0ef9bbb3ea5cb513938acf57b9773aa28c4c43088d972b1b668f1ded6c46067bfe8c9544c67bb2ce596750c7f211389bd1ffc32039aef1e5511
7
+ data.tar.gz: f5f11ed1bc6d675478778c6fb66af5a90e7e06e0a61f73b6683782f960492339002f93f3e6ce9e37ad4bc2f690a31e2529044fda6a8787a8db15e81611323518
data/CHANGELOG.md CHANGED
@@ -1,3 +1,50 @@
1
+ # [v33.7.2](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.7.1...v33.7.2) (2021-07-15 14:37:59)
2
+
3
+ ## Global changes
4
+ ### Patches
5
+
6
+ * [[Hotfix(provisioner_docker)] Increase Futex timeout to cope with image building time](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/52a03adbfc176f49800793acb3ab28026eb56289)
7
+
8
+ ## Changes for provisioner_docker
9
+ ### Patches
10
+
11
+ * [[Hotfix(provisioner_docker)] Increase Futex timeout to cope with image building time](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/52a03adbfc176f49800793acb3ab28026eb56289)
12
+
13
+ # [v33.7.1](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.7.0...v33.7.1) (2021-07-09 17:17:18)
14
+
15
+ ## Global changes
16
+ ### Patches
17
+
18
+ * [[Bugfix(platform_handler_serverless_chef)] [#93] Make sure chef runs use colors in their output](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/14352425115cf46b92e4c747b2e34e3734314288)
19
+
20
+ ## Changes for platform_handler_serverless_chef
21
+ ### Patches
22
+
23
+ * [[Bugfix(platform_handler_serverless_chef)] [#93] Make sure chef runs use colors in their output](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/14352425115cf46b92e4c747b2e34e3734314288)
24
+
25
+ # [v33.7.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.6.0...v33.7.0) (2021-07-09 16:32:25)
26
+
27
+ ### Features
28
+
29
+ * [[Feature] [#91] Expose log_debug? method to the config DSL to adapt configuration in case we are in debug mode](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/87f6f51df2d20c7861f9ddd59a8d7f68c27cdd74)
30
+
31
+ # [v33.6.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.5.1...v33.6.0) (2021-07-07 15:45:12)
32
+
33
+ ## Global changes
34
+ ### Patches
35
+
36
+ * [[Feature(connector_ssh)] [Feature(cmdb_host_keys)] [#78] Way to configure the SSH port being used with the ssh_port metadata](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/f1be319eae30eedcf7f901d3ce0a14f6f4d1f2ea)
37
+
38
+ ## Changes for cmdb_host_keys
39
+ ### Features
40
+
41
+ * [[Feature(connector_ssh)] [Feature(cmdb_host_keys)] [#78] Way to configure the SSH port being used with the ssh_port metadata](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/f1be319eae30eedcf7f901d3ce0a14f6f4d1f2ea)
42
+
43
+ ## Changes for connector_ssh
44
+ ### Features
45
+
46
+ * [[Feature(connector_ssh)] [Feature(cmdb_host_keys)] [#78] Way to configure the SSH port being used with the ssh_port metadata](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/f1be319eae30eedcf7f901d3ce0a14f6f4d1f2ea)
47
+
1
48
  # [v33.5.1](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.5.0...v33.5.1) (2021-07-07 12:01:32)
2
49
 
3
50
  ### Patches
data/README.md CHANGED
@@ -84,9 +84,11 @@ Here are the various plugin categories:
84
84
  * **[Actions](docs/plugins/action)** implement a given **action**. For example: bash code execution, ruby code execution, file transfer...
85
85
  * **[Cmdbs](docs/plugins/cmdb)** parse **metadata** from various sources. For example: a database, Chef/Ansible inventory files, a configuration management database such as Consul...
86
86
  * **[Connectors](docs/plugins/connector)** give ways to execute commands and transfer files on **nodes**. For example: using an SSH connection, a CLI for a Cloud provider...
87
+ * **[Logs](docs/plugins/log)** handle any deployment log storage strategy. They take deployment logs and send them wherever the plugins want.
87
88
  * **[Platform handlers](docs/plugins/platform_handler)** handle any **platform** of a given **platform type**. They read **nodes**' inventory and **services** from **platforms**, and provide **actions** to deploy a **service** on a **node**.
88
89
  * **[Provisioners](docs/plugins/provisioner)** provision (create, destroy, start, stop...) **nodes**. For example: using OpenShift, Proxmox, Docker, Podman...
89
90
  * **[Reports](docs/plugins/report)** gather **platforms** and **nodes**' inventory information and publish them to some medium. For example: on command line, as a JSON file, on a content management system like Confluence or Mediawiki...
91
+ * **[Secrets readers](docs/plugins/secrets_reader)** reads secrets from various sources (vaults, APIs, local files...).
90
92
  * **[Tests](docs/plugins/test)** define tests to be performed on **platforms** and **nodes**.
91
93
  * **[Tests reports](docs/plugins/test_report)** publish tests results to some medium. For example: on command line, as a JSON file, on a content management system like Confluence or Mediawiki...
92
94
 
@@ -21,7 +21,9 @@ None
21
21
 
22
22
  | Metadata | Type | Usage
23
23
  | --- | --- | --- |
24
- | `hostname` | `String` | Used to query the IP from DNS records |
24
+ | `host_ip` | `String` | Used to perform the `ssh-keyscan` |
25
+ | `hostname` | `String` | Used in place of the `host_ip` in case `host_ip` is not available |
26
+ | `ssh_port` | `Integer` | Port on which the `ssh-keyscan` will be performed (default: 22) |
25
27
 
26
28
  ## Used environment variables
27
29
 
@@ -70,6 +70,7 @@ end
70
70
  | `host_keys` | `Array<String>` | The node's host keys used to generate a `known_hosts` file with those to avoid user confirmations when connecting. |
71
71
  | `hostname` | `String` | Host name used to connect in case no IP address can be found in metadata. |
72
72
  | `private_ips` | `Array<String>` | IP list to connect in case `host_ip` is not defined in metadata. |
73
+ | `ssh_port` | `Integer` | Port used to connect to SSH (default: 22). |
73
74
  | `ssh_session_exec` | `Boolean` | If set to `false`, then consider that the node does not have any SSH SessionExec capabilities. This will make sure that remote command executions is done using stdin piping on interactive sessions instead of SSH commands execution. |
74
75
 
75
76
  ## Used environment variables
@@ -34,6 +34,8 @@ module HybridPlatformsConductor
34
34
  end
35
35
  @mixin_initializers = []
36
36
 
37
+ expose :log_debug?
38
+
37
39
  # Directory of the definition of the platforms
38
40
  # String
39
41
  attr_reader :hybrid_platforms_dir
@@ -24,7 +24,7 @@ module HybridPlatformsConductor
24
24
  # * Hash<Symbol, Symbol or Array<Symbol> >: The list of necessary properties (or single one) that should be set, per property name (:others can also be used here)
25
25
  def property_dependencies
26
26
  {
27
- host_keys: %i[hostname host_ip]
27
+ host_keys: %i[hostname host_ip ssh_port]
28
28
  }
29
29
  end
30
30
 
@@ -41,19 +41,20 @@ module HybridPlatformsConductor
41
41
  # Nodes for which the property can't be fetched can be ommitted.
42
42
  def get_host_keys(_nodes, metadata)
43
43
  updated_metadata = {}
44
- # Get the list of nodes, per hostname (just in case several nodes share the same hostname)
45
- # Hash<String, Array<String> >
44
+ # Get the list of nodes, per [hostname, port] (just in case several nodes share the same hostname and port)
45
+ # Hash<[String, Integer], Array<String> >
46
46
  hostnames = Hash.new { |hash, key| hash[key] = [] }
47
47
  metadata.each do |node, node_metadata|
48
+ ssh_port = node_metadata[:ssh_port] || 22
48
49
  if node_metadata[:host_ip]
49
- hostnames[node_metadata[:host_ip]] << node
50
+ hostnames[[node_metadata[:host_ip], ssh_port]] << node
50
51
  elsif node_metadata[:hostname]
51
- hostnames[node_metadata[:hostname]] << node
52
+ hostnames[[node_metadata[:hostname], ssh_port]] << node
52
53
  end
53
54
  end
54
55
  unless hostnames.empty?
55
- host_keys_for(*hostnames.keys).each do |hostname, ip|
56
- hostnames[hostname].each do |node|
56
+ host_keys_for(*hostnames.keys).each do |host_id, ip|
57
+ hostnames[host_id].each do |node|
57
58
  updated_metadata[node] = ip
58
59
  end
59
60
  end
@@ -71,7 +72,7 @@ module HybridPlatformsConductor
71
72
  # Discover the host keys associated to a list of hosts.
72
73
  #
73
74
  # Parameters::
74
- # * *hosts* (Array<String>): The hosts to check for
75
+ # * *hosts* (Array<[String, Integer]>): The hosts to check for ([hostname, port])
75
76
  # Result::
76
77
  # * Hash<String, Array<String> >: The corresponding host keys, per host name
77
78
  def host_keys_for(*hosts)
@@ -82,9 +83,9 @@ module HybridPlatformsConductor
82
83
  parallel: true,
83
84
  nbr_threads_max: MAX_THREADS_SSH_KEY_SCAN,
84
85
  progress: log_debug? ? 'Gather host keys' : nil
85
- ) do |host|
86
+ ) do |(host, ssh_port)|
86
87
  exit_status, stdout, _stderr = @cmd_runner.run_cmd(
87
- "ssh-keyscan #{host}",
88
+ "ssh-keyscan -p #{ssh_port} #{host}",
88
89
  timeout: TIMEOUT_SSH_KEYSCAN,
89
90
  log_to_stdout: log_debug?,
90
91
  no_exception: true
@@ -97,9 +98,9 @@ module HybridPlatformsConductor
97
98
  found_keys << "#{type} #{key}"
98
99
  end
99
100
  end
100
- results[host] = found_keys.sort unless found_keys.empty?
101
+ results[[host, ssh_port]] = found_keys.sort unless found_keys.empty?
101
102
  else
102
- log_warn "Unable to get host key for #{host}. Ignoring it. Accessing #{host} might require manual acceptance of its host key."
103
+ log_warn "Unable to get host key for #{host} (port #{ssh_port}). Ignoring it. Accessing #{host} might require manual acceptance of its host key."
103
104
  end
104
105
  end
105
106
  results
@@ -384,16 +384,18 @@ module HybridPlatformsConductor
384
384
 
385
385
  # Add each node
386
386
  # Query for the metadata of all nodes at once
387
- @nodes_handler.prefetch_metadata_of nodes, %i[private_ips hostname host_ip description]
387
+ @nodes_handler.prefetch_metadata_of nodes, %i[private_ips hostname host_ip description ssh_port]
388
388
  nodes.sort.each do |node|
389
389
  # Generate the conf for the node
390
390
  connection, connection_user, gateway, gateway_user = connection_info_for(node, no_exception: true)
391
391
  if connection.nil?
392
392
  config_content << "# #{node} - Not connectable using SSH - #{@nodes_handler.get_description_of(node) || ''}\n"
393
393
  else
394
+ ssh_port = @nodes_handler.get_ssh_port_of(node)
394
395
  config_content << "# #{node} - #{connection} - #{@nodes_handler.get_description_of(node) || ''}\n"
395
396
  config_content << "Host #{ssh_aliases_for(node).join(' ')}\n"
396
397
  config_content << " Hostname #{connection}\n"
398
+ config_content << " Port #{ssh_port}\n" unless ssh_port.nil?
397
399
  config_content << " User \"#{connection_user}\"\n" if connection_user != @ssh_user
398
400
  config_content << " ProxyCommand #{ssh_exec} -q -W %h:%p #{gateway_user}@#{gateway}\n" unless gateway.nil?
399
401
  if @passwords.key?(node)
@@ -658,7 +660,7 @@ module HybridPlatformsConductor
658
660
  existing_users = File.exist?(control_master_users_file) ? File.read(control_master_users_file).split("\n") : []
659
661
  ssh_url = "hpc.#{node}"
660
662
  connection, connection_user, _gateway, _gateway_user = connection_info_for(node)
661
- control_path_file = control_master_file(connection, '22', connection_user)
663
+ control_path_file = control_master_file(connection, @nodes_handler.get_ssh_port_of(node) || 22, connection_user)
662
664
  if existing_users.empty?
663
665
  # Make sure there is no stale one.
664
666
  if File.exist?(control_path_file)
@@ -241,18 +241,25 @@ module HybridPlatformsConductor
241
241
  '--json-attributes', "nodes/#{node}.json"
242
242
  ]
243
243
  client_options << '--why-run' if use_why_run
244
+ # client_options.concat ['--log_level', 'debug'] if log_debug?
245
+ # Force setting of TERM variable and usage of unbuffer to get colored output from chef-client even if executed through a non-interactive SSH session.
246
+ client_env = {
247
+ 'SSL_CERT_DIR' => '/etc/ssl/certs',
248
+ 'TERM' => 'xterm-256color'
249
+ }
244
250
  if @nodes_handler.get_use_local_chef_of(node)
245
251
  # Just run the chef-client directly from the packaged repository
246
- sudo_prefix = @cmd_runner.root? ? '' : 'sudo '
252
+ sudo_prefix = @cmd_runner.root? ? '' : 'sudo -E '
247
253
  [
248
254
  {
249
255
  bash: [
250
256
  'set -e',
251
257
  "cd #{package_dir}"
252
258
  ] +
253
- gems_to_install.map { |(gem_name, gem_version)| "#{sudo_prefix}SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install #{gem_name} --version \"#{gem_version}\"" } +
259
+ client_env.map { |var_name, value| "export #{var_name}=#{value}" } +
260
+ gems_to_install.map { |(gem_name, gem_version)| "#{sudo_prefix}/opt/chef-workstation/bin/chef gem install #{gem_name} --version \"#{gem_version}\"" } +
254
261
  [
255
- "#{sudo_prefix}SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client #{client_options.join(' ')}"
262
+ "#{sudo_prefix}/opt/chef-workstation/bin/chef-client #{client_options.join(' ')}"
256
263
  ]
257
264
  }
258
265
  ]
@@ -270,7 +277,7 @@ module HybridPlatformsConductor
270
277
  remote_bash: [
271
278
  'set -e',
272
279
  'set -o pipefail',
273
- "if [ -n \"$(command -v apt)\" ]; then #{sudo}apt update && #{sudo}apt install -y curl build-essential ; else #{sudo}yum groupinstall 'Development Tools' && #{sudo}yum install -y curl ; fi",
280
+ "if [ -n \"$(command -v apt)\" ]; then #{sudo}apt update && #{sudo}apt install -y curl build-essential expect ; else #{sudo}yum groupinstall 'Development Tools' && #{sudo}yum install -y curl expect ; fi",
274
281
  'mkdir -p ./hpc_deploy',
275
282
  'rm -rf ./hpc_deploy/tmp',
276
283
  'mkdir -p ./hpc_deploy/tmp',
@@ -281,16 +288,19 @@ module HybridPlatformsConductor
281
288
  },
282
289
  {
283
290
  scp: { package_dir => './hpc_deploy' },
284
- remote_bash: [
285
- 'set -e',
286
- "cd ./hpc_deploy/#{package_name}"
287
- ] +
288
- gems_to_install.map { |(gem_name, gem_version)| "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/embedded/bin/gem install #{gem_name} --version \"#{gem_version}\"" } +
289
- [
290
- "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client #{client_options.join(' ')}",
291
- 'cd ..'
291
+ remote_bash: {
292
+ commands: [
293
+ 'set -e',
294
+ "cd ./hpc_deploy/#{package_name}"
292
295
  ] +
293
- (log_debug? ? [] : ["#{sudo}rm -rf ./hpc_deploy/#{package_name}"])
296
+ gems_to_install.map { |(gem_name, gem_version)| "#{sudo}/opt/chef/embedded/bin/gem install #{gem_name} --version \"#{gem_version}\"" } +
297
+ [
298
+ "#{sudo}unbuffer /opt/chef/bin/chef-client #{client_options.join(' ')}",
299
+ 'cd ..'
300
+ ] +
301
+ (log_debug? ? [] : ["#{sudo}rm -rf ./hpc_deploy/#{package_name}"]),
302
+ env: client_env
303
+ }
294
304
  }
295
305
  ]
296
306
  end
@@ -21,7 +21,7 @@ module HybridPlatformsConductor
21
21
  ::Docker.validate_version!
22
22
  docker_ok = true
23
23
  rescue
24
- log_error "[ #{@node}/#{@environment} ] - Docker is not installed correctly. Please install it. Error: #{$ERROR_INFO}"
24
+ log_error "Docker is not installed correctly. Please install it. Error: #{$ERROR_INFO}"
25
25
  end
26
26
  docker_ok
27
27
  end
@@ -40,7 +40,7 @@ module HybridPlatformsConductor
40
40
  docker_image = nil
41
41
  image_futex_file = "#{Dir.tmpdir}/hpc_docker_image_futexes/#{image_tag}"
42
42
  FileUtils.mkdir_p File.dirname(image_futex_file)
43
- Futex.new(image_futex_file).open do
43
+ Futex.new(image_futex_file, timeout: 600).open do
44
44
  docker_image = ::Docker::Image.all.find { |search_image| !search_image.info['RepoTags'].nil? && search_image.info['RepoTags'].include?("#{image_tag}:latest") }
45
45
  unless docker_image
46
46
  log_debug "[ #{@node}/#{@environment} ] - Creating Docker image #{image_tag}..."
@@ -2,6 +2,7 @@ require 'json'
2
2
  require 'proxmox'
3
3
  require 'digest'
4
4
  require 'hybrid_platforms_conductor/actions_executor'
5
+ require 'hybrid_platforms_conductor/credentials'
5
6
  require 'hybrid_platforms_conductor/provisioner'
6
7
 
7
8
  module HybridPlatformsConductor
@@ -26,7 +26,13 @@ module HybridPlatformsConductor
26
26
  # Check that we can connect with root
27
27
  ssh_ok = false
28
28
  begin
29
- Net::SSH.start(instance.ip, 'root', password: 'root_pwd', auth_methods: ['password'], verify_host_key: :never) do |ssh|
29
+ Net::SSH.start(
30
+ instance.ip,
31
+ 'root',
32
+ password: 'root_pwd',
33
+ auth_methods: ['password'],
34
+ verify_host_key: :never
35
+ ) do |ssh|
30
36
  ssh_ok = ssh.exec!('echo Works').strip == 'Works'
31
37
  end
32
38
  rescue
@@ -53,14 +59,22 @@ module HybridPlatformsConductor
53
59
  # System is booting up. See pam_nologin(8)
54
60
  # Authentication failed.
55
61
  instance.stop
56
- instance.with_running_instance(port: 22) do
62
+ ssh_port = @nodes_handler.get_ssh_port_of(@node) || 22
63
+ instance.with_running_instance(port: ssh_port) do
57
64
 
58
65
  unless @nodes_handler.get_root_access_allowed_of(@node)
59
66
  # ===== Deploy removes root access
60
67
  # Check that we can't connect with root
61
68
  ssh_ok = false
62
69
  begin
63
- Net::SSH.start(instance.ip, 'root', password: 'root_pwd', auth_methods: ['password'], verify_host_key: :never) do |ssh|
70
+ Net::SSH.start(
71
+ instance.ip,
72
+ 'root',
73
+ password: 'root_pwd',
74
+ auth_methods: ['password'],
75
+ verify_host_key: :never,
76
+ port: ssh_port
77
+ ) do |ssh|
64
78
  ssh_ok = ssh.exec!('echo Works').strip == 'Works'
65
79
  end
66
80
  rescue
@@ -18,7 +18,13 @@ module HybridPlatformsConductor
18
18
  # Check that we can connect with root
19
19
  ssh_ok = false
20
20
  begin
21
- Net::SSH.start(instance.ip, 'root', password: 'root_pwd', auth_methods: ['password'], verify_host_key: :never) do |ssh|
21
+ Net::SSH.start(
22
+ instance.ip,
23
+ 'root',
24
+ password: 'root_pwd',
25
+ auth_methods: ['password'],
26
+ verify_host_key: :never
27
+ ) do |ssh|
22
28
  ssh_ok = ssh.exec!('echo Works').strip == 'Works'
23
29
  end
24
30
  rescue
@@ -29,17 +35,31 @@ module HybridPlatformsConductor
29
35
  deployer.nbr_retries_on_error = 3
30
36
  deployer.deploy_on @node
31
37
  # As sshd is certainly being restarted, start and stop the container to reload it.
32
- deployer.restart @node
33
- # Check that we can't connect with root
34
- ssh_ok = false
35
- begin
36
- Net::SSH.start(instance.ip, 'root', password: 'root_pwd', auth_methods: ['password'], verify_host_key: :never) do |ssh|
37
- ssh_ok = ssh.exec!('echo Works').strip == 'Works'
38
+ # As it's possible sshd has to be restarted because of a change in its conf, restart the container.
39
+ # Otherwise you'll get the following error upon reconnection:
40
+ # System is booting up. See pam_nologin(8)
41
+ # Authentication failed.
42
+ instance.stop
43
+ ssh_port = @nodes_handler.get_ssh_port_of(@node) || 22
44
+ instance.with_running_instance(port: ssh_port) do
45
+ # Check that we can't connect with root
46
+ ssh_ok = false
47
+ begin
48
+ Net::SSH.start(
49
+ instance.ip,
50
+ 'root',
51
+ password: 'root_pwd',
52
+ auth_methods: ['password'],
53
+ verify_host_key: :never,
54
+ port: ssh_port
55
+ ) do |ssh|
56
+ ssh_ok = ssh.exec!('echo Works').strip == 'Works'
57
+ end
58
+ rescue
59
+ nil
38
60
  end
39
- rescue
40
- nil
61
+ assert_equal ssh_ok, false, 'Root can still connect on the image after deployment'
41
62
  end
42
- assert_equal ssh_ok, false, 'Root can still connect on the image after deployment'
43
63
  end
44
64
  end
45
65
  end
@@ -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)
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '33.5.1'
3
+ VERSION = '33.7.2'
4
4
 
5
5
  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: {
@@ -175,6 +175,16 @@ describe HybridPlatformsConductor::ActionsExecutor do
175
175
  end
176
176
  end
177
177
 
178
+ it 'generates a simple config for a node with host_ip and a given SSH port' do
179
+ with_test_platform({ nodes: { 'node' => { meta: { host_ip: '192.168.42.42', ssh_port: 666 } } } }) do
180
+ expect(ssh_config_for('node')).to eq <<~EO_SSH_CONFIG
181
+ Host hpc.node
182
+ Hostname 192.168.42.42
183
+ Port 666
184
+ EO_SSH_CONFIG
185
+ end
186
+ end
187
+
178
188
  it 'generates a simple config for several nodes' do
179
189
  with_test_platform(
180
190
  {
@@ -29,6 +29,17 @@ describe HybridPlatformsConductor::Config do
29
29
  end
30
30
  end
31
31
 
32
+ it 'can check if we are in debug mode' do
33
+ with_platforms(
34
+ <<~EO_CONFIG
35
+ os_image :image_1, '/path/to/image_1' if log_debug?
36
+ os_image :image_2, '/path/to/image_2'
37
+ EO_CONFIG
38
+ ) do
39
+ expect(test_config.known_os_images.sort).to eq %i[image_2].sort
40
+ end
41
+ end
42
+
32
43
  it 'returns the tests provisioner correctly' do
33
44
  with_platforms 'tests_provisioner :test_provisioner' do
34
45
  expect(test_config.tests_provisioner_id).to eq :test_provisioner
@@ -4,7 +4,7 @@ describe HybridPlatformsConductor::NodesHandler do
4
4
 
5
5
  it 'makes sure to have hostname or host_ip set to compute host_keys' do
6
6
  with_test_platform({}) do
7
- expect(cmdb(:host_keys).property_dependencies[:host_keys].sort).to eq %i[hostname host_ip].sort
7
+ expect(cmdb(:host_keys).property_dependencies[:host_keys].sort).to eq %i[hostname host_ip ssh_port].sort
8
8
  end
9
9
  end
10
10
 
@@ -17,17 +17,27 @@ describe HybridPlatformsConductor::NodesHandler do
17
17
  it 'returns host keys when hostname is set' do
18
18
  with_test_platform({ nodes: { 'test_node' => {} } }) do
19
19
  with_cmd_runner_mocked [
20
- ['ssh-keyscan my_host.my_domain', proc { [0, "my_host.my_domain ssh-rsa fake_host_key\n", ''] }]
20
+ ['ssh-keyscan -p 22 my_host.my_domain', proc { [0, "my_host.my_domain ssh-rsa fake_host_key\n", ''] }]
21
21
  ] do
22
22
  expect(cmdb(:host_keys).get_host_keys(['test_node'], { 'test_node' => { hostname: 'my_host.my_domain' } })).to eq('test_node' => ['ssh-rsa fake_host_key'])
23
23
  end
24
24
  end
25
25
  end
26
26
 
27
+ it 'returns host keys for the correct ssh port' do
28
+ with_test_platform({ nodes: { 'test_node' => {} } }) do
29
+ with_cmd_runner_mocked [
30
+ ['ssh-keyscan -p 666 my_host.my_domain', proc { [0, "my_host.my_domain ssh-rsa fake_host_key\n", ''] }]
31
+ ] do
32
+ expect(cmdb(:host_keys).get_host_keys(['test_node'], { 'test_node' => { hostname: 'my_host.my_domain', ssh_port: 666 } })).to eq('test_node' => ['ssh-rsa fake_host_key'])
33
+ end
34
+ end
35
+ end
36
+
27
37
  it 'returns host keys when host_ip is set' do
28
38
  with_test_platform({ nodes: { 'test_node' => {} } }) do
29
39
  with_cmd_runner_mocked [
30
- ['ssh-keyscan 192.168.42.42', proc { [0, "192.168.42.42 ssh-rsa fake_host_key\n", ''] }]
40
+ ['ssh-keyscan -p 22 192.168.42.42', proc { [0, "192.168.42.42 ssh-rsa fake_host_key\n", ''] }]
31
41
  ] do
32
42
  expect(cmdb(:host_keys).get_host_keys(['test_node'], { 'test_node' => { host_ip: '192.168.42.42' } })).to eq('test_node' => ['ssh-rsa fake_host_key'])
33
43
  end
@@ -37,7 +47,7 @@ describe HybridPlatformsConductor::NodesHandler do
37
47
  it 'returns several host keys' do
38
48
  with_test_platform({ nodes: { 'test_node' => {} } }) do
39
49
  with_cmd_runner_mocked [
40
- ['ssh-keyscan 192.168.42.42', proc do
50
+ ['ssh-keyscan -p 22 192.168.42.42', proc do
41
51
  [0, <<~EO_STDOUT, '']
42
52
  192.168.42.42 ssh-rsa fake_host_key_rsa
43
53
  192.168.42.42 ssh-ed25519 fake_host_key_ed25519
@@ -55,7 +65,7 @@ describe HybridPlatformsConductor::NodesHandler do
55
65
  it 'returns several host keys and ignores comments from ssh-keyscan' do
56
66
  with_test_platform({ nodes: { 'test_node' => {} } }) do
57
67
  with_cmd_runner_mocked [
58
- ['ssh-keyscan 192.168.42.42', proc do
68
+ ['ssh-keyscan -p 22 192.168.42.42', proc do
59
69
  [0, <<~EO_STDOUT, '']
60
70
  # That's a comment
61
71
  192.168.42.42 ssh-rsa fake_host_key_rsa
@@ -78,7 +88,7 @@ describe HybridPlatformsConductor::NodesHandler do
78
88
  it 'returns host keys sorted' do
79
89
  with_test_platform({ nodes: { 'test_node' => {} } }) do
80
90
  with_cmd_runner_mocked [
81
- ['ssh-keyscan 192.168.42.42', proc do
91
+ ['ssh-keyscan -p 22 192.168.42.42', proc do
82
92
  [0, <<~EO_STDOUT, '']
83
93
  192.168.42.42 ssh-dsa fake_host_key_dsa
84
94
  192.168.42.42 ssh-rsa fake_host_key_rsa
@@ -100,7 +110,7 @@ describe HybridPlatformsConductor::NodesHandler do
100
110
  it 'does not return host keys when ssh-keyscan can\'t retrieve them' do
101
111
  with_test_platform({ nodes: { 'test_node' => {} } }) do
102
112
  with_cmd_runner_mocked [
103
- ['ssh-keyscan 192.168.42.42', proc { [0, '', ''] }]
113
+ ['ssh-keyscan -p 22 192.168.42.42', proc { [0, '', ''] }]
104
114
  ] do
105
115
  expect(cmdb(:host_keys).get_host_keys(['test_node'], { 'test_node' => { host_ip: '192.168.42.42' } })).to eq({})
106
116
  end
@@ -119,9 +129,9 @@ describe HybridPlatformsConductor::NodesHandler do
119
129
  }
120
130
  ) do
121
131
  with_cmd_runner_mocked [
122
- ['ssh-keyscan 192.168.42.1', proc { [0, "192.168.42.1 ssh-rsa fake_host_key_1\n", ''] }],
123
- ['ssh-keyscan 192.168.42.2', proc { [0, '', ''] }],
124
- ['ssh-keyscan my_host_4.my_domain', proc { [0, "my_host_4.my_domain ssh-rsa fake_host_key_4\n", ''] }]
132
+ ['ssh-keyscan -p 22 192.168.42.1', proc { [0, "192.168.42.1 ssh-rsa fake_host_key_1\n", ''] }],
133
+ ['ssh-keyscan -p 22 192.168.42.2', proc { [0, '', ''] }],
134
+ ['ssh-keyscan -p 22 my_host_4.my_domain', proc { [0, "my_host_4.my_domain ssh-rsa fake_host_key_4\n", ''] }]
125
135
  ] do
126
136
  expect(
127
137
  cmdb(:host_keys).get_host_keys(
@@ -141,6 +151,35 @@ describe HybridPlatformsConductor::NodesHandler do
141
151
  end
142
152
  end
143
153
 
154
+ it 'returns different host keys for hosts having the same IPs but different SSH ports' do
155
+ with_test_platform(
156
+ {
157
+ nodes: {
158
+ 'test_node1' => {},
159
+ 'test_node2' => {}
160
+ }
161
+ }
162
+ ) do
163
+ with_cmd_runner_mocked [
164
+ ['ssh-keyscan -p 6661 192.168.42.1', proc { [0, "192.168.42.1 ssh-rsa fake_host_key_1\n", ''] }],
165
+ ['ssh-keyscan -p 6662 192.168.42.1', proc { [0, "192.168.42.1 ssh-rsa fake_host_key_2\n", ''] }]
166
+ ] do
167
+ expect(
168
+ cmdb(:host_keys).get_host_keys(
169
+ ['test_node'],
170
+ {
171
+ 'test_node1' => { host_ip: '192.168.42.1', ssh_port: 6661 },
172
+ 'test_node2' => { host_ip: '192.168.42.1', ssh_port: 6662 }
173
+ }
174
+ )
175
+ ).to eq(
176
+ 'test_node1' => ['ssh-rsa fake_host_key_1'],
177
+ 'test_node2' => ['ssh-rsa fake_host_key_2']
178
+ )
179
+ end
180
+ end
181
+ end
182
+
144
183
  end
145
184
 
146
185
  end
@@ -46,7 +46,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
46
46
  remote_bash: [
47
47
  'set -e',
48
48
  'set -o pipefail',
49
- "if [ -n \"$(command -v apt)\" ]; then #{sudo}apt update && #{sudo}apt install -y curl build-essential ; else #{sudo}yum groupinstall 'Development Tools' && #{sudo}yum install -y curl ; fi",
49
+ "if [ -n \"$(command -v apt)\" ]; then #{sudo}apt update && #{sudo}apt install -y curl build-essential expect ; else #{sudo}yum groupinstall 'Development Tools' && #{sudo}yum install -y curl expect ; fi",
50
50
  'mkdir -p ./hpc_deploy',
51
51
  'rm -rf ./hpc_deploy/tmp',
52
52
  'mkdir -p ./hpc_deploy/tmp',
@@ -57,16 +57,22 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
57
57
  },
58
58
  {
59
59
  scp: { "#{repository}/dist/#{env}/#{policy}" => './hpc_deploy' },
60
- remote_bash: [
61
- 'set -e',
62
- "cd ./hpc_deploy/#{policy}"
63
- ] +
64
- gems_install_cmds.map { |gem_install_cmd| "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/embedded/bin/#{gem_install_cmd}" } +
65
- [
66
- "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/#{node}.json#{check_mode ? ' --why-run' : ''}",
67
- 'cd ..',
68
- "#{sudo}rm -rf ./hpc_deploy/#{policy}"
69
- ]
60
+ remote_bash: {
61
+ commands: [
62
+ 'set -e',
63
+ "cd ./hpc_deploy/#{policy}"
64
+ ] +
65
+ gems_install_cmds.map { |gem_install_cmd| "#{sudo}/opt/chef/embedded/bin/#{gem_install_cmd}" } +
66
+ [
67
+ "#{sudo}unbuffer /opt/chef/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/#{node}.json#{check_mode ? ' --why-run' : ''}",
68
+ 'cd ..',
69
+ "#{sudo}rm -rf ./hpc_deploy/#{policy}"
70
+ ],
71
+ env: {
72
+ 'SSL_CERT_DIR' => '/etc/ssl/certs',
73
+ 'TERM' => 'xterm-256color'
74
+ }
75
+ }
70
76
  }
71
77
  ]
72
78
  end
@@ -323,7 +329,9 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
323
329
  bash: [
324
330
  'set -e',
325
331
  "cd #{repository}/dist/prod/test_policy_1",
326
- 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/local.json'
332
+ 'export SSL_CERT_DIR=/etc/ssl/certs',
333
+ 'export TERM=xterm-256color',
334
+ 'sudo -E /opt/chef-workstation/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/local.json'
327
335
  ]
328
336
  }
329
337
  ]
@@ -352,10 +360,12 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
352
360
  bash: [
353
361
  'set -e',
354
362
  "cd #{repository}/dist/prod/test_policy_1",
355
- 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install my_gem_1 --version "0.0.1"',
356
- 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install my_gem_2 --version "0.0.2"',
357
- 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install my_gem_3 --version "~> 1.3"',
358
- 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/local.json'
363
+ 'export SSL_CERT_DIR=/etc/ssl/certs',
364
+ 'export TERM=xterm-256color',
365
+ 'sudo -E /opt/chef-workstation/bin/chef gem install my_gem_1 --version "0.0.1"',
366
+ 'sudo -E /opt/chef-workstation/bin/chef gem install my_gem_2 --version "0.0.2"',
367
+ 'sudo -E /opt/chef-workstation/bin/chef gem install my_gem_3 --version "~> 1.3"',
368
+ 'sudo -E /opt/chef-workstation/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/local.json'
359
369
  ]
360
370
  }
361
371
  ]
@@ -11,6 +11,7 @@ module HybridPlatformsConductorTest
11
11
  # * *nodes_connections* (Hash<String, Hash<Symbol,Object> >): Nodes' connections info, per node name:
12
12
  # * *connection* (String): Connection string (fqdn, IP...) used by SSH
13
13
  # * *ip* (String): IP used by SSH (can be different from connection in case of transformed SSH) [default: connection]
14
+ # * *port* (Integer): SSH port used [default: 22]
14
15
  # * *user* (String): User used by SSH
15
16
  # * *times* (Integer): Number of times this connection should be used [default: 1]
16
17
  # * *control_master_create_error* (String or nil): Error to simulate during the SSH ControlMaster creation, or nil for none [default: nil]
@@ -37,6 +38,7 @@ module HybridPlatformsConductorTest
37
38
  )
38
39
  nodes_connections.map do |node, node_connection_info|
39
40
  node_connection_info[:times] = 1 unless node_connection_info.key?(:times)
41
+ node_connection_info[:port] = 22 unless node_connection_info.key?(:port)
40
42
  ssh_commands_once = []
41
43
  ssh_commands_per_connection = []
42
44
  if with_strict_host_key_checking
@@ -44,7 +46,7 @@ module HybridPlatformsConductorTest
44
46
  ssh_commands_once.concat(
45
47
  [
46
48
  [
47
- "ssh-keyscan #{ip}",
49
+ "ssh-keyscan -p #{node_connection_info[:port]} #{ip}",
48
50
  proc { [0, "#{ip} ssh-rsa fake_host_key_for_#{ip}", ''] }
49
51
  ]
50
52
  ]
@@ -67,7 +69,7 @@ module HybridPlatformsConductorTest
67
69
  %r{^xterm -e '.+/ssh -o ControlMaster=yes -o ControlPersist=yes hpc\.#{Regexp.escape(node)}'$}
68
70
  end,
69
71
  proc do
70
- control_file = test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], '22', node_connection_info[:user])
72
+ control_file = test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], node_connection_info[:port].to_s, node_connection_info[:user])
71
73
  # Fail if the ControlMaster file already exists, as would SSH do if the file is stalled
72
74
  if File.exist?(control_file)
73
75
  [255, '', "Control file #{control_file} already exists"]
@@ -97,7 +99,7 @@ module HybridPlatformsConductorTest
97
99
  %r{^.+/ssh -O exit hpc\.#{Regexp.escape(node)} 2>&1 \| grep -v 'Exit request sent\.'$},
98
100
  proc do
99
101
  # Really mock the control file deletion
100
- File.unlink(test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], '22', node_connection_info[:user]))
102
+ File.unlink(test_actions_executor.connector(:ssh).send(:control_master_file, node_connection_info[:connection], node_connection_info[:port].to_s, node_connection_info[:user]))
101
103
  [1, '', '']
102
104
  end,
103
105
  { optional: with_control_master_destroy_optional }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hybrid_platforms_conductor
3
3
  version: !ruby/object:Gem::Version
4
- version: 33.5.1
4
+ version: 33.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muriel Salvan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-07 00:00:00.000000000 Z
11
+ date: 2021-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: range_operators