hybrid_platforms_conductor 33.3.0 → 33.7.0
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.
- 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
|