hybrid_platforms_conductor 33.2.2 → 33.5.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 +49 -0
- data/README.md +29 -2
- data/docs/config_dsl.md +45 -0
- data/docs/plugins.md +1 -0
- data/docs/plugins/secrets_reader/keepass.md +62 -0
- 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 +0 -35
- data/lib/hybrid_platforms_conductor/confluence.rb +93 -88
- data/lib/hybrid_platforms_conductor/connector.rb +1 -1
- data/lib/hybrid_platforms_conductor/core_extensions/bundler/without_bundled_env.rb +18 -1
- data/lib/hybrid_platforms_conductor/credentials.rb +122 -97
- data/lib/hybrid_platforms_conductor/deployer.rb +37 -30
- 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/connector/local.rb +4 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +29 -20
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +5 -3
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +1 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +3 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +174 -0
- 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/github_ci.rb +4 -1
- 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_report/confluence.rb +3 -1
- data/lib/hybrid_platforms_conductor/logger_helpers.rb +24 -1
- data/lib/hybrid_platforms_conductor/plugins.rb +1 -0
- data/lib/hybrid_platforms_conductor/safe_merge.rb +37 -0
- data/lib/hybrid_platforms_conductor/thycotic.rb +80 -75
- data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +5 -3
- data/lib/hybrid_platforms_conductor/version.rb +1 -1
- data/spec/hybrid_platforms_conductor_test.rb +10 -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 +9 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +8 -6
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +38 -0
- data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +21 -1
- data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +48 -72
- data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +251 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/config_dsl_spec.rb +36 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +680 -0
- 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_plugins_api_spec.rb +2 -2
- data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +1 -1
- 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/executables/nodes_to_deploy_spec.rb +21 -15
- data/spec/hybrid_platforms_conductor_test/test_connector.rb +2 -2
- metadata +188 -139
@@ -11,10 +11,27 @@ module Bundler
|
|
11
11
|
# @return [Hash] Environment with all bundler-related variables removed
|
12
12
|
def current_unbundled_env
|
13
13
|
env = ENV.clone.to_hash
|
14
|
+
%w[
|
15
|
+
PATH
|
16
|
+
RUBYLIB
|
17
|
+
RUBYOPT
|
18
|
+
].each do |env_name|
|
19
|
+
if original_env.key?(env_name)
|
20
|
+
env[env_name] = original_env[env_name]
|
21
|
+
else
|
22
|
+
env.delete(env_name)
|
23
|
+
end
|
24
|
+
end
|
14
25
|
|
15
26
|
env['MANPATH'] = env['BUNDLER_ORIG_MANPATH'] if env.key?('BUNDLER_ORIG_MANPATH')
|
16
27
|
|
17
|
-
env.delete_if
|
28
|
+
env.delete_if do |k, _|
|
29
|
+
%w[
|
30
|
+
GEM_
|
31
|
+
BUNDLE_
|
32
|
+
BUNDLER_
|
33
|
+
].any? { |prefix| k.start_with?(prefix) }
|
34
|
+
end
|
18
35
|
|
19
36
|
if env.key?('RUBYOPT')
|
20
37
|
rubyopt = env['RUBYOPT'].split
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'netrc'
|
2
|
+
require 'secret_string'
|
2
3
|
require 'uri'
|
3
4
|
require 'hybrid_platforms_conductor/logger_helpers'
|
4
5
|
|
@@ -7,122 +8,146 @@ module HybridPlatformsConductor
|
|
7
8
|
# Give a secured and harmonized way to access credentials for a given service.
|
8
9
|
# It makes sure to remove passwords from memory for hardened security (this way if a vulnerability allows an attacker to dump the memory it won't get passwords).
|
9
10
|
# It gets credentials from the following sources:
|
11
|
+
# * Configuration
|
10
12
|
# * Environment variables
|
11
13
|
# * Netrc file
|
12
|
-
|
14
|
+
module Credentials
|
13
15
|
|
14
|
-
|
16
|
+
# Extend the Config DSL
|
17
|
+
module ConfigDSLExtension
|
18
|
+
|
19
|
+
# List of credentials. Each info has the following properties:
|
20
|
+
# * *credential_id* (Symbol): Credential ID this rule applies to
|
21
|
+
# * *resource* (Regexp): Resource filtering for this rule
|
22
|
+
# * *provider* (Proc): The code providing the credentials:
|
23
|
+
# * Parameters::
|
24
|
+
# * *resource* (String or nil): The resource for which we want credentials, or nil if none
|
25
|
+
# * *requester* (Proc): Code to be called to give credentials to:
|
26
|
+
# * Parameters::
|
27
|
+
# * *user* (String or nil): The user name, or nil if none
|
28
|
+
# * *password* (String or nil): The password, or nil if none
|
29
|
+
attr_reader :credentials
|
30
|
+
|
31
|
+
# Mixin initializer
|
32
|
+
def init_credentials_config
|
33
|
+
@credentials = []
|
34
|
+
end
|
35
|
+
|
36
|
+
# Define a credentials provider
|
37
|
+
#
|
38
|
+
# Parameters::
|
39
|
+
# * *credential_id* (Symbol): Credential ID this rule applies to
|
40
|
+
# * *resource* (String or Regexp): Resource filtering for this rule [default: /.*/]
|
41
|
+
# * *provider* (Proc): The code providing the credentials:
|
42
|
+
# * Parameters::
|
43
|
+
# * *resource* (String or nil): The resource for which we want credentials, or nil if none
|
44
|
+
# * *requester* (Proc): Code to be called to give credentials to:
|
45
|
+
# * Parameters::
|
46
|
+
# * *user* (String or nil): The user name, or nil if none
|
47
|
+
# * *password* (String or nil): The password, or nil if none
|
48
|
+
def credentials_for(credential_id, resource: /.*/, &provider)
|
49
|
+
@credentials << {
|
50
|
+
credential_id: credential_id,
|
51
|
+
resource: resource.is_a?(String) ? /^#{Regexp.escape(resource)}$/ : resource,
|
52
|
+
provider: provider
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
Config.extend_config_dsl_with ConfigDSLExtension, :init_credentials_config
|
15
59
|
|
16
60
|
# Get access to credentials and make sure they are wiped out from memory when client code ends.
|
17
61
|
# To ensure password safety, never store the password in a scope beyond the client code's Proc.
|
18
62
|
#
|
19
63
|
# Parameters::
|
20
64
|
# * *id* (Symbol): Credential ID
|
21
|
-
# * *
|
22
|
-
# * *logger_stderr* (Logger): Logger to be used for stderr
|
23
|
-
# * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
|
65
|
+
# * *resource* (String or nil): The resource for which we want the credentials, or nil if not associated to a resource [default: nil]
|
24
66
|
# * Proc: Client code called with credentials provided
|
25
67
|
# * Parameters::
|
26
68
|
# * *user* (String or nil): User name, or nil if none
|
27
|
-
# * *password* (
|
28
|
-
# !!! Never
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
yield credentials.user, credentials.password
|
33
|
-
ensure
|
34
|
-
credentials.clear_password
|
35
|
-
end
|
36
|
-
end
|
69
|
+
# * *password* (SecretString or nil): Password, or nil if none.
|
70
|
+
# !!! Never clone this password in a scope broader than the client code itself !!!
|
71
|
+
def with_credentials_for(id, resource: nil)
|
72
|
+
# Get the credentials provider
|
73
|
+
provider = nil
|
37
74
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def initialize(id, url: nil, logger: Logger.new($stdout), logger_stderr: Logger.new($stderr))
|
46
|
-
init_loggers(logger, logger_stderr)
|
47
|
-
@id = id
|
48
|
-
@url = url
|
49
|
-
@user = nil
|
50
|
-
@password = nil
|
51
|
-
@retrieved = false
|
52
|
-
end
|
53
|
-
|
54
|
-
# Provide a helper to clear password from memory for security.
|
55
|
-
# To be used when the client knows it won't use the password anymore.
|
56
|
-
def clear_password
|
57
|
-
@password&.replace('gotyou!' * 100)
|
58
|
-
GC.start
|
59
|
-
end
|
60
|
-
|
61
|
-
# Get the associated user
|
62
|
-
#
|
63
|
-
# Result::
|
64
|
-
# * String or nil: The user name, or nil if none
|
65
|
-
def user
|
66
|
-
retrieve_credentials
|
67
|
-
@user
|
68
|
-
end
|
69
|
-
|
70
|
-
# Get the associated password
|
71
|
-
#
|
72
|
-
# Result::
|
73
|
-
# * String or nil: The password, or nil if none
|
74
|
-
def password
|
75
|
-
retrieve_credentials
|
76
|
-
@password
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
|
81
|
-
# Retrieve credentials in @user and @password.
|
82
|
-
# Do it only once.
|
83
|
-
# Make sure the retrieved credentials are not linked to other objects in memory, so that we can remove any other trace of secrets.
|
84
|
-
def retrieve_credentials
|
85
|
-
return if @retrieved
|
75
|
+
# Check configuration
|
76
|
+
# Take the last matching provider, this way we can define several providers for resources matched in a increasingly refined way.
|
77
|
+
@config.credentials.each do |credentials_info|
|
78
|
+
provider = credentials_info[:provider] if credentials_info[:credential_id] == id && (
|
79
|
+
(resource.nil? && credentials_info[:resource] == /.*/) || credentials_info[:resource] =~ resource
|
80
|
+
)
|
81
|
+
end
|
86
82
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if
|
93
|
-
log_debug "[ Credentials for #{
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
83
|
+
provider ||= proc do |requested_resource, requester|
|
84
|
+
# Check environment variables
|
85
|
+
user = ENV["hpc_user_for_#{id}"]
|
86
|
+
# Clone the password as we are going to treat it as a secret string that will be wiped out
|
87
|
+
password = ENV["hpc_password_for_#{id}"].dup
|
88
|
+
if user.nil? || user.empty? || password.nil? || password.empty?
|
89
|
+
log_debug "[ Credentials for #{id} ] - Credentials not found from environment variables."
|
90
|
+
if requested_resource.nil?
|
91
|
+
log_debug "[ Credentials for #{id} ] - No resource associated to this credentials, so .netrc can't be used."
|
92
|
+
else
|
93
|
+
# Check Netrc
|
94
|
+
netrc = ::Netrc.read
|
95
|
+
begin
|
96
|
+
netrc_user, netrc_password = netrc[
|
97
|
+
begin
|
98
|
+
URI.parse(requested_resource).host.downcase
|
99
|
+
rescue URI::InvalidURIError
|
100
|
+
requested_resource
|
101
|
+
end
|
102
|
+
]
|
103
|
+
if netrc_user.nil?
|
104
|
+
log_debug "[ Credentials for #{id} ] - No credentials retrieved from .netrc."
|
105
|
+
# TODO: Add more credentials source if needed here
|
106
|
+
log_warn "[ Credentials for #{id} ] - Unable to get credentials for #{id} (Resource: #{requested_resource})."
|
107
|
+
else
|
108
|
+
# Clone in memory as we are going to wipe out ::Netrc's memory
|
109
|
+
user = netrc_user.dup
|
110
|
+
password = netrc_password.dup
|
111
|
+
log_debug "[ Credentials for #{id} ] - Credentials retrieved from .netrc using #{requested_resource}."
|
112
|
+
end
|
113
|
+
ensure
|
114
|
+
# Make sure the password does not stay in Netrc memory
|
115
|
+
# Wipe out any memory trace that might contain passwords in clear
|
116
|
+
netrc.instance_variable_get(:@data).each do |data_line|
|
117
|
+
data_line.each do |data_string|
|
118
|
+
data_string.replace('GotYou!!!' * 100)
|
119
|
+
end
|
114
120
|
end
|
115
121
|
end
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
122
|
+
end
|
123
|
+
else
|
124
|
+
log_debug "[ Credentials for #{id} ] - Credentials retrieved from environment variables."
|
125
|
+
end
|
126
|
+
if password.nil?
|
127
|
+
requester.call user, password
|
128
|
+
else
|
129
|
+
SecretString.protect(password) do |secret_password|
|
130
|
+
requester.call user, secret_password
|
120
131
|
end
|
121
132
|
end
|
122
|
-
else
|
123
|
-
log_debug "[ Credentials for #{@id} ] - Credentials retrieved from environment variables."
|
124
133
|
end
|
125
|
-
|
134
|
+
|
135
|
+
requester_called = false
|
136
|
+
provider.call(
|
137
|
+
resource,
|
138
|
+
proc do |user, password|
|
139
|
+
requester_called = true
|
140
|
+
if password.is_a?(String)
|
141
|
+
SecretString.protect(password) do |secret_password|
|
142
|
+
yield user, secret_password
|
143
|
+
end
|
144
|
+
else
|
145
|
+
yield user, password
|
146
|
+
end
|
147
|
+
end
|
148
|
+
)
|
149
|
+
|
150
|
+
raise "Requester not called by the credentials provider for #{id} (resource: #{resource}) - Please check the credentials_for code in your configuration." unless requester_called
|
126
151
|
end
|
127
152
|
|
128
153
|
end
|
@@ -9,6 +9,7 @@ require 'hybrid_platforms_conductor/logger_helpers'
|
|
9
9
|
require 'hybrid_platforms_conductor/nodes_handler'
|
10
10
|
require 'hybrid_platforms_conductor/services_handler'
|
11
11
|
require 'hybrid_platforms_conductor/plugins'
|
12
|
+
require 'hybrid_platforms_conductor/safe_merge'
|
12
13
|
|
13
14
|
module HybridPlatformsConductor
|
14
15
|
|
@@ -18,6 +19,12 @@ module HybridPlatformsConductor
|
|
18
19
|
# Extend the Config DSL
|
19
20
|
module ConfigDSLExtension
|
20
21
|
|
22
|
+
# List of retriable errors. Each info has the following properties:
|
23
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
|
24
|
+
# * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
|
25
|
+
# * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
|
26
|
+
attr_reader :retriable_errors
|
27
|
+
|
21
28
|
# List of log plugins. Each info has the following properties:
|
22
29
|
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
|
23
30
|
# * *log_plugins* (Array<Symbol>): List of log plugins to be used to store deployment logs.
|
@@ -36,6 +43,11 @@ module HybridPlatformsConductor
|
|
36
43
|
# Mixin initializer
|
37
44
|
def init_deployer_config
|
38
45
|
@packaging_timeout_secs = 60
|
46
|
+
# List of retriable errors. Each info has the following properties:
|
47
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
|
48
|
+
# * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
|
49
|
+
# * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
|
50
|
+
@retriable_errors = []
|
39
51
|
@deployment_logs = []
|
40
52
|
@secrets_readers = []
|
41
53
|
end
|
@@ -48,6 +60,28 @@ module HybridPlatformsConductor
|
|
48
60
|
@packaging_timeout_secs = packaging_timeout_secs
|
49
61
|
end
|
50
62
|
|
63
|
+
# Mark some errors on stdout to be retriable during a deploy
|
64
|
+
#
|
65
|
+
# Parameters::
|
66
|
+
# * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
|
67
|
+
def retry_deploy_for_errors_on_stdout(errors)
|
68
|
+
@retriable_errors << {
|
69
|
+
errors_on_stdout: errors.is_a?(Array) ? errors : [errors],
|
70
|
+
nodes_selectors_stack: current_nodes_selectors_stack
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Mark some errors on stderr to be retriable during a deploy
|
75
|
+
#
|
76
|
+
# Parameters::
|
77
|
+
# * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
|
78
|
+
def retry_deploy_for_errors_on_stderr(errors)
|
79
|
+
@retriable_errors << {
|
80
|
+
errors_on_stderr: errors.is_a?(Array) ? errors : [errors],
|
81
|
+
nodes_selectors_stack: current_nodes_selectors_stack
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
51
85
|
# Set the deployment log plugins to be used
|
52
86
|
#
|
53
87
|
# Parameters::
|
@@ -72,10 +106,10 @@ module HybridPlatformsConductor
|
|
72
106
|
|
73
107
|
end
|
74
108
|
|
75
|
-
include LoggerHelpers
|
76
|
-
|
77
109
|
Config.extend_config_dsl_with ConfigDSLExtension, :init_deployer_config
|
78
110
|
|
111
|
+
include LoggerHelpers
|
112
|
+
|
79
113
|
# Do we use why-run mode while deploying? [default = false]
|
80
114
|
# Boolean
|
81
115
|
attr_accessor :use_why_run
|
@@ -467,34 +501,7 @@ module HybridPlatformsConductor
|
|
467
501
|
|
468
502
|
private
|
469
503
|
|
470
|
-
|
471
|
-
# Safe-merging is done by:
|
472
|
-
# * Merging values that are hashes.
|
473
|
-
# * Reporting errors when values conflict.
|
474
|
-
# When values are conflicting, the initial hash won't modify those conflicting values and will stop the merge.
|
475
|
-
#
|
476
|
-
# Parameters::
|
477
|
-
# * *hash* (Hash): Hash to be modified merging hash_to_merge
|
478
|
-
# * *hash_to_merge* (Hash): Hash to be merged into hash
|
479
|
-
# Result::
|
480
|
-
# * nil or Array<Object>: nil in case of success, or the keys path leading to a conflicting value in case of error
|
481
|
-
def safe_merge(hash, hash_to_merge)
|
482
|
-
conflicting_path = nil
|
483
|
-
hash_to_merge.each do |key, value_to_merge|
|
484
|
-
if hash.key?(key)
|
485
|
-
if hash[key].is_a?(Hash) && value_to_merge.is_a?(Hash)
|
486
|
-
sub_conflicting_path = safe_merge(hash[key], value_to_merge)
|
487
|
-
conflicting_path = [key] + sub_conflicting_path unless sub_conflicting_path.nil?
|
488
|
-
elsif hash[key] != value_to_merge
|
489
|
-
conflicting_path = [key]
|
490
|
-
end
|
491
|
-
else
|
492
|
-
hash[key] = value_to_merge
|
493
|
-
end
|
494
|
-
break unless conflicting_path.nil?
|
495
|
-
end
|
496
|
-
conflicting_path
|
497
|
-
end
|
504
|
+
include SafeMerge
|
498
505
|
|
499
506
|
# Get the list of retriable errors a node got from deployment logs.
|
500
507
|
# Useful to know if an error is non-deterministic (due to external and temporary factors).
|
@@ -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
|