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
@@ -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
|
@@ -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
|
|
@@ -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 = (@actions_executor.connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} ")
|
266
|
+
sudo = (@actions_executor.connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} -E ")
|
267
267
|
[
|
268
268
|
{
|
269
269
|
# Install dependencies
|
@@ -263,6 +263,8 @@ module HybridPlatformsConductor
|
|
263
263
|
|
264
264
|
private
|
265
265
|
|
266
|
+
include Credentials
|
267
|
+
|
266
268
|
# Connect to the Proxmox API
|
267
269
|
#
|
268
270
|
# Parameters::
|
@@ -273,7 +275,7 @@ module HybridPlatformsConductor
|
|
273
275
|
url = proxmox_test_info[:api_url]
|
274
276
|
raise 'No Proxmox server defined' if url.nil?
|
275
277
|
|
276
|
-
|
278
|
+
with_credentials_for(:proxmox, resource: url) do |user, password|
|
277
279
|
log_debug "[ #{@node}/#{@environment} ] - Connect to Proxmox #{url}"
|
278
280
|
proxmox_logs = StringIO.new
|
279
281
|
proxmox = ::Proxmox::Proxmox.new(
|
@@ -282,7 +284,7 @@ module HybridPlatformsConductor
|
|
282
284
|
# cf https://pve.proxmox.com/wiki/Renaming_a_PVE_node
|
283
285
|
URI.parse(url).host.downcase.split('.').first,
|
284
286
|
user,
|
285
|
-
password,
|
287
|
+
password&.to_unprotected,
|
286
288
|
ENV['hpc_realm_for_proxmox'] || 'pam',
|
287
289
|
{
|
288
290
|
verify_ssl: false,
|
@@ -415,7 +417,7 @@ module HybridPlatformsConductor
|
|
415
417
|
extra_files[config_file] = './proxmox/config'
|
416
418
|
cmd << " --config ./proxmox/config/#{File.basename(config_file)}"
|
417
419
|
stdout = nil
|
418
|
-
|
420
|
+
with_credentials_for(:proxmox, resource: proxmox_test_info[:api_url]) do |user, password|
|
419
421
|
# To avoid too fine concurrent accesses on the sync node file system, make sure all threads of our process wait for their turn to upload their files.
|
420
422
|
# Otherwise there is a small probability that a directory scp makes previously copied files inaccessible for a short period of time.
|
421
423
|
self.class.proxmox_waiter_files_mutex.synchronize do
|
@@ -14,6 +14,8 @@ module HybridPlatformsConductor
|
|
14
14
|
|
15
15
|
extend_config_dsl_with CommonConfigDsl::Confluence, :init_confluence
|
16
16
|
|
17
|
+
include HybridPlatformsConductor::Confluence
|
18
|
+
|
17
19
|
# Give the list of supported locales by this report generator
|
18
20
|
# [API] - This method is mandatory.
|
19
21
|
#
|
@@ -34,7 +36,7 @@ module HybridPlatformsConductor
|
|
34
36
|
if confluence_info
|
35
37
|
if confluence_info[:inventory_report_page_id]
|
36
38
|
@nodes = nodes
|
37
|
-
|
39
|
+
with_confluence(confluence_info[:url]) do |confluence|
|
38
40
|
confluence.update_page(confluence_info[:inventory_report_page_id], render('confluence_inventory'))
|
39
41
|
end
|
40
42
|
out "Inventory report Confluence page updated. Please visit #{confluence_info[:url]}/pages/viewpage.action?pageId=#{confluence_info[:inventory_report_page_id]}"
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'keepass_kpscript'
|
5
|
+
require 'zlib'
|
6
|
+
require 'hybrid_platforms_conductor/credentials'
|
7
|
+
require 'hybrid_platforms_conductor/safe_merge'
|
8
|
+
require 'hybrid_platforms_conductor/secrets_reader'
|
9
|
+
|
10
|
+
module HybridPlatformsConductor
|
11
|
+
|
12
|
+
module HpcPlugins
|
13
|
+
|
14
|
+
module SecretsReader
|
15
|
+
|
16
|
+
# Get secrets from a KeePass database
|
17
|
+
class Keepass < HybridPlatformsConductor::SecretsReader
|
18
|
+
|
19
|
+
include SafeMerge
|
20
|
+
include Credentials
|
21
|
+
|
22
|
+
# Extend the Config DSL
|
23
|
+
module ConfigDSLExtension
|
24
|
+
|
25
|
+
# List of defined KeePass secrets. Each info has the following properties:
|
26
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
|
27
|
+
# * *database* (String): Database file path.
|
28
|
+
# * *group_path* (Array<String>): Group path to extract from.
|
29
|
+
# Array< Hash<Symbol, Object> >
|
30
|
+
attr_reader :keepass_secrets
|
31
|
+
|
32
|
+
# String: The KPScript command line
|
33
|
+
attr_reader :kpscript
|
34
|
+
|
35
|
+
# Mixin initializer
|
36
|
+
def init_keepass_config
|
37
|
+
@keepass_secrets = []
|
38
|
+
@kpscript = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set the KPScript command line
|
42
|
+
#
|
43
|
+
# Parameters::
|
44
|
+
# * *cmd* (String): KPScript command line
|
45
|
+
def use_kpscript_from(cmd)
|
46
|
+
@kpscript = cmd
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set a KeePass database configuration
|
50
|
+
#
|
51
|
+
# Parameters::
|
52
|
+
# * *database* (String): Database file path.
|
53
|
+
# * *group_path* (Array<String>): Group path to extract from [default: []].
|
54
|
+
def secrets_from_keepass(database:, group_path: [])
|
55
|
+
@keepass_secrets << {
|
56
|
+
nodes_selectors_stack: current_nodes_selectors_stack,
|
57
|
+
database: database,
|
58
|
+
group_path: group_path
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
Config.extend_config_dsl_with ConfigDSLExtension, :init_keepass_config
|
65
|
+
|
66
|
+
# Return secrets for a given service to be deployed on a node.
|
67
|
+
# [API] - This method is mandatory
|
68
|
+
# [API] - The following API components are accessible:
|
69
|
+
# * *@config* (Config): Main configuration API.
|
70
|
+
# * *@cmd_runner* (CmdRunner): Command Runner API.
|
71
|
+
# * *@nodes_handler* (NodesHandler): Nodes handler API.
|
72
|
+
#
|
73
|
+
# Parameters::
|
74
|
+
# * *node* (String): Node to be deployed
|
75
|
+
# * *service* (String): Service to be deployed
|
76
|
+
# Result::
|
77
|
+
# * Hash: The secrets
|
78
|
+
def secrets_for(node, service)
|
79
|
+
secrets = {}
|
80
|
+
# As we are dealing with global secrets, cache the reading for performance between nodes and services.
|
81
|
+
# Keep secrets cache grouped by URL/ID
|
82
|
+
@secrets = {} unless defined?(@secrets)
|
83
|
+
@nodes_handler.select_confs_for_node(node, @config.keepass_secrets).each do |keepass_secrets_info|
|
84
|
+
secret_id = "#{keepass_secrets_info[:database]}:#{keepass_secrets_info[:group_path].join('/')}"
|
85
|
+
unless @secrets.key?(secret_id)
|
86
|
+
raise 'Missing KPScript configuration. Please use use_kpscript_from to set it.' if @config.kpscript.nil?
|
87
|
+
|
88
|
+
with_credentials_for(:keepass, resource: keepass_secrets_info[:database]) do |_user, password|
|
89
|
+
Tempfile.create('hpc_keepass') do |xml_file|
|
90
|
+
key_file = ENV['hpc_key_file_for_keepass']
|
91
|
+
password_enc = ENV['hpc_password_enc_for_keepass']
|
92
|
+
keepass_credentials = {}
|
93
|
+
keepass_credentials[:password] = password.to_unprotected if password
|
94
|
+
keepass_credentials[:password_enc] = password_enc if password_enc
|
95
|
+
keepass_credentials[:key_file] = key_file if key_file
|
96
|
+
KeepassKpscript.
|
97
|
+
use(@config.kpscript, debug: log_debug?).
|
98
|
+
open(keepass_secrets_info[:database], **keepass_credentials).
|
99
|
+
export('KeePass XML (2.x)', xml_file.path, group_path: keepass_secrets_info[:group_path].empty? ? nil : keepass_secrets_info[:group_path])
|
100
|
+
@secrets[secret_id] = parse_xml_secrets(Nokogiri::XML(xml_file).at_xpath('KeePassFile/Root/Group'))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
conflicting_path = safe_merge(secrets, @secrets[secret_id])
|
105
|
+
raise "Secret set at path #{conflicting_path.join('->')} by #{keepass_secrets_info[:database]}#{keepass_secrets_info[:group_path].empty? ? '' : " from group #{keepass_secrets_info[:group_path].join('/')}"} for service #{service} on node #{node} has conflicting values (#{log_debug? ? "#{@secrets[secret_id].dig(*conflicting_path)} != #{secrets.dig(*conflicting_path)}" : 'set debug for value details'})." unless conflicting_path.nil?
|
106
|
+
end
|
107
|
+
secrets
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# List of fields to include in the secrets and their corresponding XML name
|
113
|
+
FIELDS = {
|
114
|
+
notes: 'Notes',
|
115
|
+
password: 'Password',
|
116
|
+
url: 'URL',
|
117
|
+
user_name: 'UserName'
|
118
|
+
}
|
119
|
+
|
120
|
+
# Parse XML secrets from a Nokogiri XML group node
|
121
|
+
#
|
122
|
+
# Parameters::
|
123
|
+
# * *group* (Nokogiri::XML::Element): The group to parse
|
124
|
+
# Result::
|
125
|
+
# * Hash: The JSON secrets parsed from this group
|
126
|
+
def parse_xml_secrets(group)
|
127
|
+
# Parse all entries
|
128
|
+
group.xpath('Entry').map do |entry|
|
129
|
+
[
|
130
|
+
entry.at_xpath('String/Key[contains(.,"Title")]/../Value').text,
|
131
|
+
FIELDS.map do |property, field|
|
132
|
+
value = entry.at_xpath("String/Key[contains(.,\"#{field}\")]/../Value")&.text
|
133
|
+
if value.nil? || value.empty?
|
134
|
+
nil
|
135
|
+
else
|
136
|
+
[
|
137
|
+
property.to_s,
|
138
|
+
value
|
139
|
+
]
|
140
|
+
end
|
141
|
+
end.compact.to_h.merge(
|
142
|
+
entry.xpath('Binary').map do |binary|
|
143
|
+
binary_meta = group.document.at_xpath("KeePassFile/Meta/Binaries/Binary[@ID=#{Integer(binary.xpath('Value').attr('Ref').value)}]")
|
144
|
+
binary_content = Base64.decode64(binary_meta.text)
|
145
|
+
if binary_meta.attr('Compressed') == 'True'
|
146
|
+
gz = Zlib::GzipReader.new(StringIO.new(binary_content))
|
147
|
+
binary_content = gz.read
|
148
|
+
gz.close
|
149
|
+
end
|
150
|
+
[
|
151
|
+
binary.xpath('Key').text,
|
152
|
+
binary_content
|
153
|
+
]
|
154
|
+
end.to_h
|
155
|
+
)
|
156
|
+
]
|
157
|
+
end.to_h.merge(
|
158
|
+
# Add children groups
|
159
|
+
group.xpath('Group').map do |sub_group|
|
160
|
+
[
|
161
|
+
sub_group.at_xpath('Name').text,
|
162
|
+
parse_xml_secrets(sub_group)
|
163
|
+
]
|
164
|
+
end.to_h
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
@@ -42,6 +42,8 @@ module HybridPlatformsConductor
|
|
42
42
|
|
43
43
|
Config.extend_config_dsl_with ConfigDSLExtension, :init_thycotic_config
|
44
44
|
|
45
|
+
include HybridPlatformsConductor::Thycotic
|
46
|
+
|
45
47
|
# Return secrets for a given service to be deployed on a node.
|
46
48
|
# [API] - This method is mandatory
|
47
49
|
# [API] - The following API components are accessible:
|
@@ -62,7 +64,7 @@ module HybridPlatformsConductor
|
|
62
64
|
@nodes_handler.select_confs_for_node(node, @config.thycotic_secrets).each do |thycotic_secrets_info|
|
63
65
|
server_id = "#{thycotic_secrets_info[:thycotic_url]}:#{thycotic_secrets_info[:secret_id]}"
|
64
66
|
unless @secrets.key?(server_id)
|
65
|
-
|
67
|
+
with_thycotic(thycotic_secrets_info[:thycotic_url]) do |thycotic|
|
66
68
|
secret_file_item_id = thycotic.get_secret(thycotic_secrets_info[:secret_id]).dig(:secret, :items, :secret_item, :id)
|
67
69
|
raise "Unable to fetch secret file ID #{thycotic_secrets_info[:secret_id]} from #{thycotic_secrets_info[:thycotic_url]}" if secret_file_item_id.nil?
|
68
70
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'git'
|
2
|
+
require 'hybrid_platforms_conductor/bitbucket'
|
2
3
|
require 'hybrid_platforms_conductor/common_config_dsl/bitbucket'
|
3
4
|
|
4
5
|
module HybridPlatformsConductor
|
@@ -12,9 +13,11 @@ module HybridPlatformsConductor
|
|
12
13
|
|
13
14
|
extend_config_dsl_with CommonConfigDsl::Bitbucket, :init_bitbucket
|
14
15
|
|
16
|
+
include HybridPlatformsConductor::Bitbucket
|
17
|
+
|
15
18
|
# Check my_test_plugin.rb.sample documentation for signature details.
|
16
19
|
def test
|
17
|
-
|
20
|
+
for_each_bitbucket_repo do |bitbucket, repo_info|
|
18
21
|
# Test repo_info
|
19
22
|
repo_id = "#{repo_info[:project]}/#{repo_info[:name]}"
|
20
23
|
settings_pr = bitbucket.settings_pr(repo_info[:project], repo_info[:name])
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'hybrid_platforms_conductor/github'
|
1
2
|
require 'hybrid_platforms_conductor/common_config_dsl/github'
|
2
3
|
|
3
4
|
module HybridPlatformsConductor
|
@@ -11,9 +12,11 @@ module HybridPlatformsConductor
|
|
11
12
|
|
12
13
|
extend_config_dsl_with CommonConfigDsl::Github, :init_github
|
13
14
|
|
15
|
+
include HybridPlatformsConductor::Github
|
16
|
+
|
14
17
|
# Check my_test_plugin.rb.sample documentation for signature details.
|
15
18
|
def test
|
16
|
-
|
19
|
+
for_each_github_repo do |client, repo_info|
|
17
20
|
log_debug "Checking CI for Github repository #{repo_info[:slug]}"
|
18
21
|
last_status = client.repository_workflow_runs(repo_info[:slug])[:workflow_runs].
|
19
22
|
select { |run| run[:head_branch] == 'master' }.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'open-uri'
|
2
2
|
require 'nokogiri'
|
3
3
|
require 'hybrid_platforms_conductor/credentials'
|
4
|
+
require 'hybrid_platforms_conductor/bitbucket'
|
4
5
|
require 'hybrid_platforms_conductor/common_config_dsl/bitbucket'
|
5
6
|
|
6
7
|
module HybridPlatformsConductor
|
@@ -14,15 +15,18 @@ module HybridPlatformsConductor
|
|
14
15
|
|
15
16
|
extend_config_dsl_with CommonConfigDsl::Bitbucket, :init_bitbucket
|
16
17
|
|
18
|
+
include Credentials
|
19
|
+
include HybridPlatformsConductor::Bitbucket
|
20
|
+
|
17
21
|
# Check my_test_plugin.rb.sample documentation for signature details.
|
18
22
|
def test
|
19
|
-
|
23
|
+
for_each_bitbucket_repo do |bitbucket, repo_info|
|
20
24
|
if repo_info[:jenkins_ci_url].nil?
|
21
25
|
error "Repository #{repo_info[:name]} does not have any Jenkins CI URL configured."
|
22
26
|
else
|
23
|
-
|
27
|
+
with_credentials_for(:jenkins_ci, resource: repo_info[:jenkins_ci_url]) do |jenkins_user, jenkins_password|
|
24
28
|
# Get its config
|
25
|
-
doc = Nokogiri::XML(URI.parse("#{repo_info[:jenkins_ci_url]}/config.xml").open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
|
29
|
+
doc = Nokogiri::XML(URI.parse("#{repo_info[:jenkins_ci_url]}/config.xml").open(http_basic_authentication: [jenkins_user, jenkins_password&.to_unprotected]).read)
|
26
30
|
# Check that this job builds the correct Bitbucket repository
|
27
31
|
assert_equal(
|
28
32
|
doc.xpath('/org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject/sources/data/jenkins.branch.BranchSource/source/serverUrl').text,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'hybrid_platforms_conductor/credentials'
|
3
|
+
require 'hybrid_platforms_conductor/bitbucket'
|
3
4
|
require 'hybrid_platforms_conductor/common_config_dsl/bitbucket'
|
4
5
|
|
5
6
|
module HybridPlatformsConductor
|
@@ -13,6 +14,9 @@ module HybridPlatformsConductor
|
|
13
14
|
|
14
15
|
extend_config_dsl_with CommonConfigDsl::Bitbucket, :init_bitbucket
|
15
16
|
|
17
|
+
include Credentials
|
18
|
+
include HybridPlatformsConductor::Bitbucket
|
19
|
+
|
16
20
|
SUCCESS_STATUSES = [
|
17
21
|
# Add nil as the status of a currently running job (which is always the case for hybrid-platforms) is null
|
18
22
|
nil,
|
@@ -23,17 +27,17 @@ module HybridPlatformsConductor
|
|
23
27
|
|
24
28
|
# Check my_test_plugin.rb.sample documentation for signature details.
|
25
29
|
def test
|
26
|
-
|
30
|
+
for_each_bitbucket_repo do |_bitbucket, repo_info|
|
27
31
|
if repo_info[:jenkins_ci_url].nil?
|
28
32
|
error "Repository #{repo_info[:name]} does not have any Jenkins CI URL configured."
|
29
33
|
else
|
30
34
|
master_info_url = "#{repo_info[:jenkins_ci_url]}/job/master/api/json"
|
31
|
-
|
35
|
+
with_credentials_for(:jenkins_ci, resource: master_info_url) do |jenkins_user, jenkins_password|
|
32
36
|
# Get the master branch info from the API
|
33
|
-
master_info = JSON.parse(URI.parse(master_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
|
37
|
+
master_info = JSON.parse(URI.parse(master_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password&.to_unprotected]).read)
|
34
38
|
# Get the last build's URL
|
35
39
|
last_build_info_url = "#{master_info['lastBuild']['url']}/api/json"
|
36
|
-
last_build_info = JSON.parse(URI.parse(last_build_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
|
40
|
+
last_build_info = JSON.parse(URI.parse(last_build_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password&.to_unprotected]).read)
|
37
41
|
log_debug "Build info for #{master_info_url}:\n#{JSON.pretty_generate(last_build_info)}"
|
38
42
|
error "Last build for job #{repo_info[:project]}/#{repo_info[:name]} is in status #{last_build_info['result']}: #{master_info['lastBuild']['url']}" unless SUCCESS_STATUSES.include?(last_build_info['result'])
|
39
43
|
rescue
|