hybrid_platforms_conductor 33.3.0 → 33.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +31 -2
- data/docs/config_dsl.md +45 -0
- data/docs/plugins/cmdb/host_keys.md +3 -1
- data/docs/plugins/connector/ssh.md +1 -0
- data/lib/hybrid_platforms_conductor/actions_executor.rb +29 -1
- data/lib/hybrid_platforms_conductor/bitbucket.rb +134 -90
- data/lib/hybrid_platforms_conductor/cmd_runner.rb +4 -4
- data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +12 -44
- data/lib/hybrid_platforms_conductor/common_config_dsl/github.rb +9 -31
- data/lib/hybrid_platforms_conductor/config.rb +2 -0
- data/lib/hybrid_platforms_conductor/confluence.rb +93 -88
- data/lib/hybrid_platforms_conductor/connector.rb +5 -2
- data/lib/hybrid_platforms_conductor/credentials.rb +122 -97
- data/lib/hybrid_platforms_conductor/deployer.rb +7 -9
- data/lib/hybrid_platforms_conductor/github.rb +39 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +27 -17
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +13 -12
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +6 -4
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +37 -25
- data/lib/hybrid_platforms_conductor/hpc_plugins/log/remote_fs.rb +5 -6
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +7 -4
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +3 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +3 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/thycotic.rb +3 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +17 -3
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +30 -10
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/github_ci.rb +4 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +1 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +1 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +7 -3
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +8 -4
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +1 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +1 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +1 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +1 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +3 -1
- data/lib/hybrid_platforms_conductor/logger_helpers.rb +24 -1
- data/lib/hybrid_platforms_conductor/test.rb +21 -7
- data/lib/hybrid_platforms_conductor/tests_runner.rb +7 -6
- data/lib/hybrid_platforms_conductor/thycotic.rb +80 -75
- data/lib/hybrid_platforms_conductor/version.rb +1 -1
- data/spec/hybrid_platforms_conductor_test.rb +6 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +15 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +32 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +87 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +30 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +10 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +38 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/helpers_spec.rb +195 -0
- data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +14 -0
- data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +11 -0
- data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +251 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/log_plugins/remote_fs_spec.rb +215 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +280 -319
- data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/thycotic_spec.rb +2 -2
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +49 -10
- data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +38 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +49 -69
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/github_ci_spec.rb +29 -39
- data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +5 -3
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/chef_versions.yml +3 -0
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/nodes/node.json +15 -0
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_local_node/policyfiles/test_policy.rb +3 -0
- data/spec/hybrid_platforms_conductor_test/shared_examples/deployer.rb +134 -0
- data/spec/hybrid_platforms_conductor_test/test_connector.rb +2 -2
- metadata +36 -2
@@ -106,10 +106,10 @@ module HybridPlatformsConductor
|
|
106
106
|
|
107
107
|
end
|
108
108
|
|
109
|
-
include LoggerHelpers
|
110
|
-
|
111
109
|
Config.extend_config_dsl_with ConfigDSLExtension, :init_deployer_config
|
112
110
|
|
111
|
+
include LoggerHelpers
|
112
|
+
|
113
113
|
# Do we use why-run mode while deploying? [default = false]
|
114
114
|
# Boolean
|
115
115
|
attr_accessor :use_why_run
|
@@ -544,15 +544,13 @@ module HybridPlatformsConductor
|
|
544
544
|
# Result::
|
545
545
|
# * Hash<String, [Integer or Symbol, String, String]>: Exit status code (or Symbol in case of error or dry run), standard output and error for each node.
|
546
546
|
def deploy(services)
|
547
|
-
# Get the ssh user directly from the connector
|
548
|
-
ssh_user = @actions_executor.connector(:ssh).ssh_user
|
549
|
-
|
550
547
|
# Deploy for real
|
551
548
|
@nodes_handler.prefetch_metadata_of services.keys, :image
|
552
549
|
outputs = @actions_executor.execute_actions(
|
553
550
|
services.map do |node, node_services|
|
554
551
|
image_id = @nodes_handler.get_image_of(node)
|
555
|
-
|
552
|
+
need_sudo = !@actions_executor.privileged_access?(node)
|
553
|
+
sudo = @actions_executor.sudo_prefix(node)
|
556
554
|
# Install corporate certificates if present
|
557
555
|
certificate_actions =
|
558
556
|
if @local_environment && ENV['hpc_certificates']
|
@@ -568,7 +566,7 @@ module HybridPlatformsConductor
|
|
568
566
|
{
|
569
567
|
scp: {
|
570
568
|
ENV['hpc_certificates'] => '/usr/local/share/ca-certificates',
|
571
|
-
:sudo =>
|
569
|
+
:sudo => need_sudo
|
572
570
|
},
|
573
571
|
remote_bash: "#{sudo}update-ca-certificates"
|
574
572
|
}
|
@@ -584,7 +582,7 @@ module HybridPlatformsConductor
|
|
584
582
|
cert_file,
|
585
583
|
'/etc/pki/ca-trust/source/anchors'
|
586
584
|
]
|
587
|
-
end.to_h.merge(sudo:
|
585
|
+
end.to_h.merge(sudo: need_sudo),
|
588
586
|
remote_bash: [
|
589
587
|
"#{sudo}update-ca-trust enable",
|
590
588
|
"#{sudo}update-ca-trust extract"
|
@@ -619,7 +617,7 @@ module HybridPlatformsConductor
|
|
619
617
|
services.keys.map do |node|
|
620
618
|
[
|
621
619
|
node,
|
622
|
-
{ remote_bash: "#{
|
620
|
+
{ remote_bash: "#{@actions_executor.sudo_prefix(node)}./mutex_dir unlock /tmp/hybrid_platforms_conductor_deploy_lock" }
|
623
621
|
]
|
624
622
|
end.to_h,
|
625
623
|
timeout: 10,
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
require 'hybrid_platforms_conductor/credentials'
|
3
|
+
|
4
|
+
module HybridPlatformsConductor
|
5
|
+
|
6
|
+
# Mixin used to access Github API
|
7
|
+
module Github
|
8
|
+
|
9
|
+
include Credentials
|
10
|
+
|
11
|
+
# Iterate over each Github repository
|
12
|
+
#
|
13
|
+
# Parameters::
|
14
|
+
# * Proc: Code called for each Github repository:
|
15
|
+
# * Parameters::
|
16
|
+
# * *github* (Octokit::Client): The client instance accessing the Github API
|
17
|
+
# * *repo_info* (Hash<Symbol, Object>): The repository info:
|
18
|
+
# * *name* (String): Repository name.
|
19
|
+
# * *slug* (String): Repository slug.
|
20
|
+
def for_each_github_repo
|
21
|
+
@config.known_github_repos.each do |repo_info|
|
22
|
+
Octokit.configure do |c|
|
23
|
+
c.api_endpoint = repo_info[:url]
|
24
|
+
end
|
25
|
+
with_credentials_for(:github, resource: repo_info[:url]) do |_github_user, github_token|
|
26
|
+
client = Octokit::Client.new(access_token: github_token&.to_unprotected)
|
27
|
+
(repo_info[:repos] == :all ? client.repositories(repo_info[:user]).map { |repo| repo[:name] } : repo_info[:repos]).each do |name|
|
28
|
+
yield client, {
|
29
|
+
name: name,
|
30
|
+
slug: "#{repo_info[:user]}/#{name}"
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -15,30 +15,30 @@ module HybridPlatformsConductor
|
|
15
15
|
#
|
16
16
|
# Parameters::
|
17
17
|
# * *remote_bash* (Array or Object): List of commands (or single command) to be executed. Each command can be the following:
|
18
|
-
# * String: Simple bash command.
|
18
|
+
# * String or SecretString: Simple bash command.
|
19
19
|
# * Hash<Symbol, Object>: Information about the commands to execute. Can have the following properties:
|
20
|
-
# * *commands* (Array<String> or String): List of bash commands to execute (can be a single one) [default: ''].
|
20
|
+
# * *commands* (Array<String or SecretString> or String or SecretString): List of bash commands to execute (can be a single one) [default: ''].
|
21
21
|
# * *file* (String): Name of file from which commands should be taken [optional].
|
22
|
-
# * *env* (Hash<String, String>): Environment variables to be set before executing those commands [default: {}].
|
22
|
+
# * *env* (Hash<String, String or SecretString>): Environment variables to be set before executing those commands [default: {}].
|
23
23
|
def setup(remote_bash)
|
24
24
|
# Normalize the parameters.
|
25
25
|
# Array< Hash<Symbol,Object> >: Simple array of info:
|
26
|
-
# * *commands* (Array<String>): List of bash commands to execute.
|
27
|
-
# * *env* (Hash<String, String>): Environment variables to be set before executing those commands.
|
26
|
+
# * *commands* (Array<String or SecretString>): List of bash commands to execute.
|
27
|
+
# * *env* (Hash<String, String or SecretString>): Environment variables to be set before executing those commands.
|
28
28
|
@remote_bash = (remote_bash.is_a?(Array) ? remote_bash : [remote_bash]).map do |cmd_info|
|
29
|
-
if cmd_info.is_a?(
|
30
|
-
{
|
31
|
-
commands: [cmd_info],
|
32
|
-
env: {}
|
33
|
-
}
|
34
|
-
else
|
29
|
+
if cmd_info.is_a?(Hash)
|
35
30
|
commands = []
|
36
|
-
commands.concat(cmd_info[:commands].is_a?(
|
31
|
+
commands.concat(cmd_info[:commands].is_a?(Array) ? cmd_info[:commands] : [cmd_info[:commands]]) if cmd_info[:commands]
|
37
32
|
commands << File.read(cmd_info[:file]) if cmd_info[:file]
|
38
33
|
{
|
39
34
|
commands: commands,
|
40
35
|
env: cmd_info[:env] || {}
|
41
36
|
}
|
37
|
+
else
|
38
|
+
{
|
39
|
+
commands: [cmd_info],
|
40
|
+
env: {}
|
41
|
+
}
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -63,11 +63,21 @@ module HybridPlatformsConductor
|
|
63
63
|
# [API] - @stderr_io can be used to log stderr messages
|
64
64
|
# [API] - run_cmd(String) method can be used to execute a command. See CmdRunner#run_cmd to know about the result's signature.
|
65
65
|
def execute
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
# The commands or ENV variables can contain secrets, so make sure to protect all strings from secrets leaking
|
67
|
+
bash_cmds = @remote_bash.map do |cmd_info|
|
68
|
+
cmd_info[:env].map do |var_name, var_value|
|
69
|
+
SecretString.new("export #{var_name}='#{var_value.to_unprotected}'", silenced_str: "export #{var_name}='#{var_value}'")
|
70
|
+
end + cmd_info[:commands]
|
71
|
+
end.flatten
|
72
|
+
begin
|
73
|
+
SecretString.protect(bash_cmds.map(&:to_unprotected).join("\n"), silenced_str: bash_cmds.join("\n")) do |bash_str|
|
74
|
+
log_debug "[#{@node}] - Execute remote Bash commands \"#{bash_str}\"..."
|
75
|
+
@connector.remote_bash bash_str
|
76
|
+
end
|
77
|
+
ensure
|
78
|
+
# Make sure we erase all secret strings
|
79
|
+
bash_cmds.each(&:erase)
|
80
|
+
end
|
71
81
|
end
|
72
82
|
|
73
83
|
end
|
@@ -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 |
|
56
|
-
hostnames[
|
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
|
@@ -35,9 +35,11 @@ module HybridPlatformsConductor
|
|
35
35
|
# [API] - @stderr_io can be used to send stderr output
|
36
36
|
#
|
37
37
|
# Parameters::
|
38
|
-
# * *bash_cmds* (String): Bash commands to execute
|
38
|
+
# * *bash_cmds* (String or SecretString): Bash commands to execute. Use #to_unprotected to access the real content (otherwise secrets are obfuscated).
|
39
39
|
def remote_bash(bash_cmds)
|
40
|
-
|
40
|
+
SecretString.protect("cd #{workspace_for(@node)} ; #{bash_cmds.to_unprotected}", silenced_str: "cd #{workspace_for(@node)} ; #{bash_cmds}") do |cmd|
|
41
|
+
run_cmd cmd, force_bash: true
|
42
|
+
end
|
41
43
|
end
|
42
44
|
|
43
45
|
# Execute an interactive shell on the remote node
|
@@ -75,8 +77,8 @@ module HybridPlatformsConductor
|
|
75
77
|
def remote_copy(from, to, sudo: false, owner: nil, group: nil)
|
76
78
|
# If the destination is a relative path, prepend the workspace dir to it.
|
77
79
|
to = "#{workspace_for(@node)}/#{to}" unless to.start_with?('/')
|
78
|
-
if sudo
|
79
|
-
run_cmd "#{@
|
80
|
+
if sudo && !@actions_executor.privileged_access?(@node)
|
81
|
+
run_cmd "#{@actions_executor.sudo_prefix(@node)}cp -r \"#{from}\" \"#{to}\""
|
80
82
|
else
|
81
83
|
FileUtils.cp_r from, to unless @cmd_runner.dry_run
|
82
84
|
end
|
@@ -104,7 +104,7 @@ module HybridPlatformsConductor
|
|
104
104
|
# [API] - @stderr_io can be used to send stderr output
|
105
105
|
#
|
106
106
|
# Parameters::
|
107
|
-
# * *bash_cmds* (String): Bash commands to execute
|
107
|
+
# * *bash_cmds* (String or SecretString): Bash commands to execute. Use #to_unprotected to access the real content (otherwise secrets are obfuscated).
|
108
108
|
def remote_bash(bash_cmds)
|
109
109
|
MyConnectLib.connect_to(@nodes_handler.get_host_ip_of(@node)).run_bash(bash_cmds)
|
110
110
|
end
|
@@ -236,31 +236,40 @@ module HybridPlatformsConductor
|
|
236
236
|
# [API] - @stderr_io can be used to send stderr output
|
237
237
|
#
|
238
238
|
# Parameters::
|
239
|
-
# * *bash_cmds* (String): Bash commands to execute
|
239
|
+
# * *bash_cmds* (String or SecretString): Bash commands to execute. Use #to_unprotected to access the real content (otherwise secrets are obfuscated).
|
240
240
|
def remote_bash(bash_cmds)
|
241
|
-
|
241
|
+
SecretString.protect(
|
242
242
|
if @nodes_handler.get_ssh_session_exec_of(@node) == false
|
243
243
|
# When ExecSession is disabled we need to use stdin directly
|
244
|
-
"{ cat | #{ssh_exec} #{ssh_url} -T; } <<'HPC_EOF'\n#{bash_cmds}\nHPC_EOF"
|
244
|
+
"{ cat | #{ssh_exec} #{ssh_url} -T; } <<'HPC_EOF'\n#{bash_cmds.to_unprotected}\nHPC_EOF"
|
245
245
|
else
|
246
|
-
"#{ssh_exec} #{ssh_url} /bin/bash <<'HPC_EOF'\n#{bash_cmds}\nHPC_EOF"
|
247
|
-
end
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
246
|
+
"#{ssh_exec} #{ssh_url} /bin/bash <<'HPC_EOF'\n#{bash_cmds.to_unprotected}\nHPC_EOF"
|
247
|
+
end,
|
248
|
+
silenced_str:
|
249
|
+
if @nodes_handler.get_ssh_session_exec_of(@node) == false
|
250
|
+
# When ExecSession is disabled we need to use stdin directly
|
251
|
+
"{ cat | #{ssh_exec} #{ssh_url} -T; } <<'HPC_EOF'\n#{bash_cmds}\nHPC_EOF"
|
252
|
+
else
|
253
|
+
"#{ssh_exec} #{ssh_url} /bin/bash <<'HPC_EOF'\n#{bash_cmds}\nHPC_EOF"
|
254
|
+
end
|
255
|
+
) do |ssh_cmd|
|
256
|
+
# Due to a limitation of Process.spawn, each individual argument is limited to 128KB of size.
|
257
|
+
# 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.
|
258
|
+
if bash_cmds.to_unprotected.size > MAX_CMD_ARG_LENGTH
|
259
|
+
# Write the commands in a file
|
260
|
+
temp_file = "#{Dir.tmpdir}/hpc_temp_cmds_#{Digest::MD5.hexdigest(bash_cmds.to_unprotected)}.sh"
|
261
|
+
File.open(temp_file, 'w+') do |file|
|
262
|
+
file.write ssh_cmd.to_unprotected
|
263
|
+
file.chmod 0o700
|
264
|
+
end
|
265
|
+
begin
|
266
|
+
run_cmd(temp_file)
|
267
|
+
ensure
|
268
|
+
File.unlink(temp_file)
|
269
|
+
end
|
270
|
+
else
|
271
|
+
run_cmd ssh_cmd
|
261
272
|
end
|
262
|
-
else
|
263
|
-
run_cmd ssh_cmd
|
264
273
|
end
|
265
274
|
end
|
266
275
|
|
@@ -301,13 +310,14 @@ module HybridPlatformsConductor
|
|
301
310
|
# * *owner* (String or nil): Owner to be used when copying the files, or nil for current one [default: nil]
|
302
311
|
# * *group* (String or nil): Group to be used when copying the files, or nil for current one [default: nil]
|
303
312
|
def remote_copy(from, to, sudo: false, owner: nil, group: nil)
|
313
|
+
need_sudo = sudo && !@actions_executor.privileged_access?(@node)
|
304
314
|
if @nodes_handler.get_ssh_session_exec_of(@node) == false
|
305
315
|
# We don't have ExecSession, so don't use ssh, but scp instead.
|
306
|
-
if
|
316
|
+
if need_sudo
|
307
317
|
# We need to first copy the file in an accessible directory, and then sudo mv
|
308
318
|
remote_bash('mkdir -p hpc_tmp_scp')
|
309
319
|
run_cmd "scp -S #{ssh_exec} #{from} #{ssh_url}:./hpc_tmp_scp"
|
310
|
-
remote_bash("#{@
|
320
|
+
remote_bash("#{@actions_executor.sudo_prefix(@node)}mv ./hpc_tmp_scp/#{File.basename(from)} #{to}")
|
311
321
|
else
|
312
322
|
run_cmd "scp -S #{ssh_exec} #{from} #{ssh_url}:#{to}"
|
313
323
|
end
|
@@ -323,7 +333,7 @@ module HybridPlatformsConductor
|
|
323
333
|
#{File.basename(from)} | \
|
324
334
|
#{ssh_exec} \
|
325
335
|
#{ssh_url} \
|
326
|
-
\"#{
|
336
|
+
\"#{need_sudo ? @actions_executor.sudo_prefix(@node) : ''}tar \
|
327
337
|
--extract \
|
328
338
|
--gunzip \
|
329
339
|
--file - \
|
@@ -374,16 +384,18 @@ module HybridPlatformsConductor
|
|
374
384
|
|
375
385
|
# Add each node
|
376
386
|
# Query for the metadata of all nodes at once
|
377
|
-
@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]
|
378
388
|
nodes.sort.each do |node|
|
379
389
|
# Generate the conf for the node
|
380
390
|
connection, connection_user, gateway, gateway_user = connection_info_for(node, no_exception: true)
|
381
391
|
if connection.nil?
|
382
392
|
config_content << "# #{node} - Not connectable using SSH - #{@nodes_handler.get_description_of(node) || ''}\n"
|
383
393
|
else
|
394
|
+
ssh_port = @nodes_handler.get_ssh_port_of(node)
|
384
395
|
config_content << "# #{node} - #{connection} - #{@nodes_handler.get_description_of(node) || ''}\n"
|
385
396
|
config_content << "Host #{ssh_aliases_for(node).join(' ')}\n"
|
386
397
|
config_content << " Hostname #{connection}\n"
|
398
|
+
config_content << " Port #{ssh_port}\n" unless ssh_port.nil?
|
387
399
|
config_content << " User \"#{connection_user}\"\n" if connection_user != @ssh_user
|
388
400
|
config_content << " ProxyCommand #{ssh_exec} -q -W %h:%p #{gateway_user}@#{gateway}\n" unless gateway.nil?
|
389
401
|
if @passwords.key?(node)
|
@@ -648,7 +660,7 @@ module HybridPlatformsConductor
|
|
648
660
|
existing_users = File.exist?(control_master_users_file) ? File.read(control_master_users_file).split("\n") : []
|
649
661
|
ssh_url = "hpc.#{node}"
|
650
662
|
connection, connection_user, _gateway, _gateway_user = connection_info_for(node)
|
651
|
-
control_path_file = control_master_file(connection,
|
663
|
+
control_path_file = control_master_file(connection, @nodes_handler.get_ssh_port_of(node) || 22, connection_user)
|
652
664
|
if existing_users.empty?
|
653
665
|
# Make sure there is no stale one.
|
654
666
|
if File.exist?(control_path_file)
|
@@ -29,10 +29,9 @@ module HybridPlatformsConductor
|
|
29
29
|
# Result::
|
30
30
|
# * Array< Hash<Symbol,Object> >: List of actions to be done
|
31
31
|
def actions_to_save_logs(node, services, deployment_info, exit_status, stdout, stderr)
|
32
|
-
# Create a log file to be scp with all relevant info
|
33
|
-
|
34
|
-
|
35
|
-
log_file = "#{Dir.tmpdir}/hpc_deploy_logs/#{node}_#{Time.now.utc.strftime('%F_%H%M%S')}_#{ssh_user}"
|
32
|
+
# Create a log file to be scp-ed with all relevant info
|
33
|
+
sudo_prefix = @actions_executor.sudo_prefix(node)
|
34
|
+
log_file = "#{Dir.tmpdir}/hpc_deploy_logs/#{node}_#{Time.now.utc.strftime('%F_%H%M%S')}_#{@actions_executor.connector(:ssh).ssh_user}"
|
36
35
|
[
|
37
36
|
{
|
38
37
|
ruby: proc do
|
@@ -56,7 +55,7 @@ module HybridPlatformsConductor
|
|
56
55
|
{
|
57
56
|
scp: {
|
58
57
|
log_file => '/var/log/deployments',
|
59
|
-
:sudo =>
|
58
|
+
:sudo => !@actions_executor.privileged_access?(node),
|
60
59
|
:owner => 'root',
|
61
60
|
:group => 'root'
|
62
61
|
}
|
@@ -85,7 +84,7 @@ module HybridPlatformsConductor
|
|
85
84
|
# Result::
|
86
85
|
# * Array< Hash<Symbol,Object> >: List of actions to be done
|
87
86
|
def actions_to_read_logs(node)
|
88
|
-
sudo_prefix = @actions_executor.
|
87
|
+
sudo_prefix = @actions_executor.sudo_prefix(node)
|
89
88
|
[
|
90
89
|
{ remote_bash: "#{sudo_prefix}cat /var/log/deployments/`#{sudo_prefix}ls -t /var/log/deployments/ | head -1`" }
|
91
90
|
]
|
@@ -263,7 +263,7 @@ module HybridPlatformsConductor
|
|
263
263
|
raise "Missing file #{chef_versions_file} specifying the Chef Infra Client version to be deployed" unless File.exist?(chef_versions_file)
|
264
264
|
|
265
265
|
required_chef_client_version = YAML.load_file(chef_versions_file)['client']
|
266
|
-
sudo =
|
266
|
+
sudo = @actions_executor.sudo_prefix(node, forward_env: true)
|
267
267
|
[
|
268
268
|
{
|
269
269
|
# Install dependencies
|
@@ -21,7 +21,7 @@ module HybridPlatformsConductor
|
|
21
21
|
::Docker.validate_version!
|
22
22
|
docker_ok = true
|
23
23
|
rescue
|
24
|
-
log_error "
|
24
|
+
log_error "Docker is not installed correctly. Please install it. Error: #{$ERROR_INFO}"
|
25
25
|
end
|
26
26
|
docker_ok
|
27
27
|
end
|