hybrid_platforms_conductor 33.3.0 → 33.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +31 -2
  4. data/docs/config_dsl.md +43 -0
  5. data/lib/hybrid_platforms_conductor/bitbucket.rb +134 -90
  6. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +12 -44
  7. data/lib/hybrid_platforms_conductor/common_config_dsl/github.rb +9 -31
  8. data/lib/hybrid_platforms_conductor/confluence.rb +93 -88
  9. data/lib/hybrid_platforms_conductor/credentials.rb +112 -95
  10. data/lib/hybrid_platforms_conductor/deployer.rb +2 -2
  11. data/lib/hybrid_platforms_conductor/github.rb +39 -0
  12. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +4 -2
  13. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +3 -1
  14. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +2 -1
  15. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/thycotic.rb +3 -1
  16. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -1
  17. data/lib/hybrid_platforms_conductor/hpc_plugins/test/github_ci.rb +4 -1
  18. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +6 -2
  19. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +6 -2
  20. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +3 -1
  21. data/lib/hybrid_platforms_conductor/logger_helpers.rb +7 -1
  22. data/lib/hybrid_platforms_conductor/thycotic.rb +80 -75
  23. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  24. data/spec/hybrid_platforms_conductor_test.rb +6 -0
  25. data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +247 -0
  26. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +280 -319
  27. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/thycotic_spec.rb +2 -2
  28. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +49 -69
  29. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/github_ci_spec.rb +29 -39
  30. metadata +18 -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
@@ -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)
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
@@ -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
- Credentials.with_credentials_for(:proxmox, @logger, @logger_stderr, url: url) do |user, password|
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(
@@ -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
- Credentials.with_credentials_for(:proxmox, @logger, @logger_stderr, url: proxmox_test_info[:api_url]) do |user, password|
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
- HybridPlatformsConductor::Confluence.with_confluence(confluence_info[:url], @logger, @logger_stderr) do |confluence|
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]}"
@@ -17,6 +17,7 @@ module HybridPlatformsConductor
17
17
  class Keepass < HybridPlatformsConductor::SecretsReader
18
18
 
19
19
  include SafeMerge
20
+ include Credentials
20
21
 
21
22
  # Extend the Config DSL
22
23
  module ConfigDSLExtension
@@ -84,7 +85,7 @@ module HybridPlatformsConductor
84
85
  unless @secrets.key?(secret_id)
85
86
  raise 'Missing KPScript configuration. Please use use_kpscript_from to set it.' if @config.kpscript.nil?
86
87
 
87
- Credentials.with_credentials_for(:keepass, @logger, @logger_stderr) do |_user, password|
88
+ with_credentials_for(:keepass, resource: keepass_secrets_info[:database]) do |_user, password|
88
89
  Tempfile.create('hpc_keepass') do |xml_file|
89
90
  key_file = ENV['hpc_key_file_for_keepass']
90
91
  password_enc = ENV['hpc_password_enc_for_keepass']
@@ -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
- HybridPlatformsConductor::Thycotic.with_thycotic(thycotic_secrets_info[:thycotic_url], @logger, @logger_stderr) do |thycotic|
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
- @config.for_each_bitbucket_repo do |bitbucket, repo_info|
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
- @config.for_each_github_repo do |client, repo_info|
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,13 +15,16 @@ 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
- @config.for_each_bitbucket_repo do |bitbucket, repo_info|
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
- Credentials.with_credentials_for(:jenkins_ci, @logger, @logger_stderr, url: repo_info[:jenkins_ci_url]) do |jenkins_user, jenkins_password|
27
+ with_credentials_for(:jenkins_ci, resource: repo_info[:jenkins_ci_url]) do |jenkins_user, jenkins_password|
24
28
  # Get its config
25
29
  doc = Nokogiri::XML(URI.parse("#{repo_info[:jenkins_ci_url]}/config.xml").open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
26
30
  # Check that this job builds the correct Bitbucket repository
@@ -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,12 +27,12 @@ module HybridPlatformsConductor
23
27
 
24
28
  # Check my_test_plugin.rb.sample documentation for signature details.
25
29
  def test
26
- @config.for_each_bitbucket_repo do |_bitbucket, repo_info|
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
- Credentials.with_credentials_for(:jenkins_ci, @logger, @logger_stderr, url: master_info_url) do |jenkins_user, jenkins_password|
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
37
  master_info = JSON.parse(URI.parse(master_info_url).open(http_basic_authentication: [jenkins_user, jenkins_password]).read)
34
38
  # Get the last build's URL
@@ -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
  # Maximum errors to be reported by item
18
20
  MAX_ERROR_ITEMS_DISPLAYED = 10
19
21
 
@@ -28,7 +30,7 @@ module HybridPlatformsConductor
28
30
  confluence_info = @config.confluence_info
29
31
  if confluence_info
30
32
  if confluence_info[:tests_report_page_id]
31
- HybridPlatformsConductor::Confluence.with_confluence(confluence_info[:url], @logger, @logger_stderr) do |confluence|
33
+ with_confluence(confluence_info[:url]) do |confluence|
32
34
  # Get previous percentages for the evolution
33
35
  @previous_success_percentages = confluence.page_storage_format(confluence_info[:tests_report_page_id]).
34
36
  at('h1:contains("Evolution")').
@@ -88,7 +88,13 @@ module HybridPlatformsConductor
88
88
  define_method("log_#{level}") do |message|
89
89
  (LEVELS_TO_STDERR.include?(level) ? @logger_stderr : @logger).send(
90
90
  level,
91
- defined?(@log_component) ? @log_component : self.class.name.split('::').last
91
+ if defined?(@log_component)
92
+ @log_component
93
+ else
94
+ # Handle the case when the class is unnamed
95
+ class_name = self.class.name
96
+ class_name.nil? ? '<Unnamed class>' : class_name.split('::').last
97
+ end
92
98
  ) { message }
93
99
  end
94
100
  end
@@ -5,95 +5,100 @@ require 'hybrid_platforms_conductor/logger_helpers'
5
5
 
6
6
  module HybridPlatformsConductor
7
7
 
8
- # Gives ways to query the Thycotic SOAP API at a given URL
9
- class Thycotic
8
+ # Mixin giving ways to query the Thycotic SOAP API at a given URL
9
+ module Thycotic
10
10
 
11
- include LoggerHelpers
11
+ include Credentials
12
12
 
13
13
  # Provide a Thycotic connector, and make sure the password is being cleaned when exiting.
14
14
  #
15
15
  # Parameters::
16
16
  # * *thycotic_url* (String): The Thycotic URL
17
- # * *logger* (Logger): Logger to be used
18
- # * *logger_stderr* (Logger): Logger to be used for stderr
19
17
  # * *domain* (String): Domain to use for authentication to Thycotic [default: ENV['hpc_domain_for_thycotic']]
20
18
  # * Proc: Code called with the Thyctotic instance.
21
- # * *thycotic* (Thyctotic): The Thyctotic instance to use.
22
- def self.with_thycotic(thycotic_url, logger, logger_stderr, domain: ENV['hpc_domain_for_thycotic'])
23
- Credentials.with_credentials_for(:thycotic, logger, logger_stderr, url: thycotic_url) do |thycotic_user, thycotic_password|
24
- yield Thycotic.new(thycotic_url, thycotic_user, thycotic_password, domain: domain, logger: logger, logger_stderr: logger_stderr)
19
+ # * *thycotic* (ThyctoticApi): The Thycotic instance to use.
20
+ def with_thycotic(thycotic_url, domain: ENV['hpc_domain_for_thycotic'])
21
+ with_credentials_for(:thycotic, resource: thycotic_url) do |thycotic_user, thycotic_password|
22
+ yield ThycoticApi.new(thycotic_url, thycotic_user, thycotic_password, domain: domain, logger: @logger, logger_stderr: @logger_stderr)
25
23
  end
26
24
  end
27
25
 
28
- # Constructor
29
- #
30
- # Parameters::
31
- # * *url* (String): URL of the Thycotic Secret Server
32
- # * *user* (String): User name to be used to connect to Thycotic
33
- # * *password* (String): Password to be used to connect to Thycotic
34
- # * *domain* (String): Domain to use for authentication to Thycotic [default: ENV['hpc_domain_for_thycotic']]
35
- # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
36
- # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
37
- def initialize(
38
- url,
39
- user,
40
- password,
41
- domain: ENV['hpc_domain_for_thycotic'],
42
- logger: Logger.new($stdout),
43
- logger_stderr: Logger.new($stderr)
44
- )
45
- init_loggers(logger, logger_stderr)
46
- # Get a token to this SOAP API
47
- @client = Savon.client(
48
- wsdl: "#{url}/webservices/SSWebservice.asmx?wsdl",
49
- ssl_verify_mode: :none,
50
- logger: @logger,
51
- log: log_debug?
26
+ # Access to the Thycotic API
27
+ class ThycoticApi
28
+
29
+ include LoggerHelpers
30
+
31
+ # Constructor
32
+ #
33
+ # Parameters::
34
+ # * *url* (String): URL of the Thycotic Secret Server
35
+ # * *user* (String): User name to be used to connect to Thycotic
36
+ # * *password* (String): Password to be used to connect to Thycotic
37
+ # * *domain* (String): Domain to use for authentication to Thycotic [default: ENV['hpc_domain_for_thycotic']]
38
+ # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
39
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
40
+ def initialize(
41
+ url,
42
+ user,
43
+ password,
44
+ domain: ENV['hpc_domain_for_thycotic'],
45
+ logger: Logger.new($stdout),
46
+ logger_stderr: Logger.new($stderr)
52
47
  )
53
- @token = @client.call(
54
- :authenticate,
55
- message: {
56
- username: user,
57
- password: password,
58
- domain: domain
59
- }
60
- ).to_hash.dig(:authenticate_response, :authenticate_result, :token)
61
- raise "Unable to get token from SOAP authentication to #{url}" if @token.nil?
62
- end
48
+ init_loggers(logger, logger_stderr)
49
+ # Get a token to this SOAP API
50
+ @client = Savon.client(
51
+ wsdl: "#{url}/webservices/SSWebservice.asmx?wsdl",
52
+ ssl_verify_mode: :none,
53
+ logger: @logger,
54
+ log: log_debug?
55
+ )
56
+ @token = @client.call(
57
+ :authenticate,
58
+ message: {
59
+ username: user,
60
+ password: password,
61
+ domain: domain
62
+ }
63
+ ).to_hash.dig(:authenticate_response, :authenticate_result, :token)
64
+ raise "Unable to get token from SOAP authentication to #{url}" if @token.nil?
65
+ end
63
66
 
64
- # Return secret corresponding to a given secret ID
65
- #
66
- # Parameters::
67
- # * *secret_id* (Object): The secret ID
68
- # Result::
69
- # * Hash: The corresponding API result
70
- def get_secret(secret_id)
71
- @client.call(
72
- :get_secret,
73
- message: {
74
- token: @token,
75
- secretId: secret_id
76
- }
77
- ).to_hash.dig(:get_secret_response, :get_secret_result)
78
- end
67
+ # Return secret corresponding to a given secret ID
68
+ #
69
+ # Parameters::
70
+ # * *secret_id* (Object): The secret ID
71
+ # Result::
72
+ # * Hash: The corresponding API result
73
+ def get_secret(secret_id)
74
+ @client.call(
75
+ :get_secret,
76
+ message: {
77
+ token: @token,
78
+ secretId: secret_id
79
+ }
80
+ ).to_hash.dig(:get_secret_response, :get_secret_result)
81
+ end
82
+
83
+ # Get a file attached to a given secret
84
+ #
85
+ # Parameters::
86
+ # * *secret_id* (Object): The secret ID
87
+ # * *secret_item_id* (Object): The secret item id
88
+ # Result::
89
+ # * String or nil: The file content, or nil if none
90
+ def download_file_attachment_by_item_id(secret_id, secret_item_id)
91
+ encoded_file = @client.call(
92
+ :download_file_attachment_by_item_id,
93
+ message: {
94
+ token: @token,
95
+ secretId: secret_id,
96
+ secretItemId: secret_item_id
97
+ }
98
+ ).to_hash.dig(:download_file_attachment_by_item_id_response, :download_file_attachment_by_item_id_result, :file_attachment)
99
+ encoded_file.nil? ? nil : Base64.decode64(encoded_file)
100
+ end
79
101
 
80
- # Get a file attached to a given secret
81
- #
82
- # Parameters::
83
- # * *secret_id* (Object): The secret ID
84
- # * *secret_item_id* (Object): The secret item id
85
- # Result::
86
- # * String or nil: The file content, or nil if none
87
- def download_file_attachment_by_item_id(secret_id, secret_item_id)
88
- encoded_file = @client.call(
89
- :download_file_attachment_by_item_id,
90
- message: {
91
- token: @token,
92
- secretId: secret_id,
93
- secretItemId: secret_item_id
94
- }
95
- ).to_hash.dig(:download_file_attachment_by_item_id_response, :download_file_attachment_by_item_id_result, :file_attachment)
96
- encoded_file.nil? ? nil : Base64.decode64(encoded_file)
97
102
  end
98
103
 
99
104
  end
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '33.3.0'
3
+ VERSION = '33.4.0'
4
4
 
5
5
  end