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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +29 -2
  4. data/docs/config_dsl.md +45 -0
  5. data/docs/plugins.md +1 -0
  6. data/docs/plugins/secrets_reader/keepass.md +62 -0
  7. data/lib/hybrid_platforms_conductor/bitbucket.rb +134 -90
  8. data/lib/hybrid_platforms_conductor/cmd_runner.rb +4 -4
  9. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +12 -44
  10. data/lib/hybrid_platforms_conductor/common_config_dsl/github.rb +9 -31
  11. data/lib/hybrid_platforms_conductor/config.rb +0 -35
  12. data/lib/hybrid_platforms_conductor/confluence.rb +93 -88
  13. data/lib/hybrid_platforms_conductor/connector.rb +1 -1
  14. data/lib/hybrid_platforms_conductor/core_extensions/bundler/without_bundled_env.rb +18 -1
  15. data/lib/hybrid_platforms_conductor/credentials.rb +122 -97
  16. data/lib/hybrid_platforms_conductor/deployer.rb +37 -30
  17. data/lib/hybrid_platforms_conductor/github.rb +39 -0
  18. data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +1 -1
  19. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +27 -17
  20. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +4 -2
  21. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +1 -1
  22. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +29 -20
  23. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +1 -1
  24. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +5 -3
  25. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +1 -0
  26. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +3 -1
  27. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +174 -0
  28. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/thycotic.rb +3 -1
  29. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -1
  30. data/lib/hybrid_platforms_conductor/hpc_plugins/test/github_ci.rb +4 -1
  31. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +7 -3
  32. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +8 -4
  33. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +3 -1
  34. data/lib/hybrid_platforms_conductor/logger_helpers.rb +24 -1
  35. data/lib/hybrid_platforms_conductor/plugins.rb +1 -0
  36. data/lib/hybrid_platforms_conductor/safe_merge.rb +37 -0
  37. data/lib/hybrid_platforms_conductor/thycotic.rb +80 -75
  38. data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +5 -3
  39. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  40. data/spec/hybrid_platforms_conductor_test.rb +10 -0
  41. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +15 -0
  42. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +32 -0
  43. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +9 -0
  44. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +8 -6
  45. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +38 -0
  46. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +21 -1
  47. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +48 -72
  48. data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +251 -0
  49. data/spec/hybrid_platforms_conductor_test/api/deployer/config_dsl_spec.rb +36 -0
  50. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +680 -0
  51. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/thycotic_spec.rb +2 -2
  52. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +2 -2
  53. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +1 -1
  54. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +49 -69
  55. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/github_ci_spec.rb +29 -39
  56. data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +21 -15
  57. data/spec/hybrid_platforms_conductor_test/test_connector.rb +2 -2
  58. metadata +188 -139
@@ -59,7 +59,7 @@ module HybridPlatformsConductor
59
59
  # Raise an exception if the exit status is not the expected one.
60
60
  #
61
61
  # Parameters::
62
- # * *cmd* (String): Command to be run
62
+ # * *cmd* (String or SecretString): Command to be run
63
63
  # * *log_to_file* (String or nil): Log file capturing stdout or stderr (or nil for none). [default: nil]
64
64
  # * *log_to_stdout* (Boolean): Do we send the output to stdout? [default: true]
65
65
  # * *log_stdout_to_io* (IO or nil): IO to send command's stdout to, or nil for none. [default: nil]
@@ -108,7 +108,7 @@ module HybridPlatformsConductor
108
108
  bash_file = nil
109
109
  if force_bash
110
110
  bash_file = Tempfile.new('hpc_bash')
111
- bash_file.write(cmd)
111
+ bash_file.write(cmd.to_unprotected)
112
112
  bash_file.chmod 0o700
113
113
  bash_file.close
114
114
  cmd = "/bin/bash -c #{bash_file.path}"
@@ -136,7 +136,7 @@ module HybridPlatformsConductor
136
136
  pty: true,
137
137
  timeout: timeout,
138
138
  uuid: false
139
- ).run!(cmd) do |stdout, stderr|
139
+ ).run!(cmd.to_unprotected) do |stdout, stderr|
140
140
  stdout_queue << stdout if stdout
141
141
  stderr_queue << stderr if stderr
142
142
  end
@@ -162,7 +162,7 @@ module HybridPlatformsConductor
162
162
  log_debug "Finished in #{elapsed} seconds with exit status #{exit_status} (#{(expected_code.include?(exit_status) ? 'success'.light_green : 'failure'.light_red).bold})"
163
163
  end
164
164
  unless expected_code.include?(exit_status)
165
- error_title = "Command '#{cmd.split("\n").first}' returned error code #{exit_status} (expected #{expected_code.join(', ')})."
165
+ error_title = "Command '#{cmd.to_s.split("\n").first}' returned error code #{exit_status} (expected #{expected_code.join(', ')})."
166
166
  if no_exception
167
167
  # We consider the caller is responsible for logging what he wants about the details of the error (stdout and stderr)
168
168
  log_error error_title
@@ -1,5 +1,3 @@
1
- require 'hybrid_platforms_conductor/bitbucket'
2
-
3
1
  module HybridPlatformsConductor
4
2
 
5
3
  module CommonConfigDsl
@@ -7,12 +5,21 @@ module HybridPlatformsConductor
7
5
  # Add common Bitbucket config DSL to declare known Bitbucket repositories
8
6
  module Bitbucket
9
7
 
8
+ # List of known Bitbucket repos
9
+ # Array< Hash<Symbol, Object> >
10
+ # * *url* (String): URL to the Bitbucket server
11
+ # * *project* (String): Project name from the Bitbucket server, storing repositories
12
+ # * *repos* (Array<String> or Symbol): List of repository names from this project, or :all for all
13
+ # * *jenkins_ci_url* (String or nil): Corresponding Jenkins CI URL, or nil if none
14
+ # * *checks* (Hash<Symbol, Object>): Checks definition to be perform on those repositories (see the #for_each_bitbucket_repo to know the structure)
15
+ attr_reader :known_bitbucket_repos
16
+
10
17
  # Initialize the DSL
11
18
  def init_bitbucket
12
19
  # List of Bitbucket repositories definitions
13
20
  # Array< Hash<Symbol, Object> >
14
- # Each definition is just mapping the signature of #bitbucket_repos
15
- @bitbucket_repos = []
21
+ # Each definition is just mapping the signature of #known_bitbucket_repos
22
+ @known_bitbucket_repos = []
16
23
  end
17
24
 
18
25
  # Register new Bitbucket repositories
@@ -24,7 +31,7 @@ module HybridPlatformsConductor
24
31
  # * *jenkins_ci_url* (String or nil): Corresponding Jenkins CI URL, or nil if none [default: nil]
25
32
  # * *checks* (Hash<Symbol, Object>): Checks definition to be perform on those repositories (see the #for_each_bitbucket_repo to know the structure) [default: {}]
26
33
  def bitbucket_repos(url:, project:, repos: :all, jenkins_ci_url: nil, checks: {})
27
- @bitbucket_repos << {
34
+ @known_bitbucket_repos << {
28
35
  url: url,
29
36
  project: project,
30
37
  repos: repos,
@@ -33,45 +40,6 @@ module HybridPlatformsConductor
33
40
  }
34
41
  end
35
42
 
36
- # Iterate over each Bitbucket repository
37
- #
38
- # Parameters::
39
- # * Proc: Code called for each Bitbucket repository:
40
- # * Parameters::
41
- # * *bitbucket* (Bitbucket): The Bitbucket instance used to query the API for this repository
42
- # * *repo_info* (Hash<Symbol, Object>): The repository info:
43
- # * *name* (String): Repository name.
44
- # * *project* (String): Project name.
45
- # * *url* (String): Project Git URL.
46
- # * *jenkins_ci_url* (String or nil): Corresponding Jenkins CI URL, or nil if none.
47
- # * *checks* (Hash<Symbol, Object>): Checks to be performed on this repository:
48
- # * *branch_permissions* (Array< Hash<Symbol, Object> >): List of branch permissions to check [optional]
49
- # * *type* (String): Type of branch permissions to check. Examples of values are 'fast-forward-only', 'no-deletes', 'pull-request-only'.
50
- # * *branch* (String): Branch on which those permissions apply.
51
- # * *exempted_users* (Array<String>): List of exempted users for this permission [default: []]
52
- # * *exempted_groups* (Array<String>): List of exempted groups for this permission [default: []]
53
- # * *exempted_keys* (Array<String>): List of exempted access keys for this permission [default: []]
54
- # * *pr_settings* (Hash<Symbol, Object>): PR specific settings to check [optional]
55
- # * *required_approvers* (Integer): Number of required approvers [optional]
56
- # * *required_builds* (Integer): Number of required successful builds [optional]
57
- # * *default_merge_strategy* (String): Name of the default merge strategy. Example: 'rebase-no-ff' [optional]
58
- # * *mandatory_default_reviewers* (Array<String>): List of mandatory reviewers to check [default: []]
59
- def for_each_bitbucket_repo
60
- @bitbucket_repos.each do |bitbucket_repo_info|
61
- HybridPlatformsConductor::Bitbucket.with_bitbucket(bitbucket_repo_info[:url], @logger, @logger_stderr) do |bitbucket|
62
- (bitbucket_repo_info[:repos] == :all ? bitbucket.repos(bitbucket_repo_info[:project])['values'].map { |repo_info| repo_info['slug'] } : bitbucket_repo_info[:repos]).each do |name|
63
- yield bitbucket, {
64
- name: name,
65
- project: bitbucket_repo_info[:project],
66
- url: "#{bitbucket_repo_info[:url]}/scm/#{bitbucket_repo_info[:project].downcase}/#{name}.git",
67
- jenkins_ci_url: bitbucket_repo_info[:jenkins_ci_url].nil? ? nil : "#{bitbucket_repo_info[:jenkins_ci_url]}/job/#{name}",
68
- checks: bitbucket_repo_info[:checks]
69
- }
70
- end
71
- end
72
- end
73
- end
74
-
75
43
  end
76
44
 
77
45
  end
@@ -1,6 +1,3 @@
1
- require 'octokit'
2
- require 'hybrid_platforms_conductor/credentials'
3
-
4
1
  module HybridPlatformsConductor
5
2
 
6
3
  module CommonConfigDsl
@@ -8,12 +5,19 @@ module HybridPlatformsConductor
8
5
  # Add common Github config DSL to declare known Github repositories
9
6
  module Github
10
7
 
8
+ # List of Github repositories
9
+ # Array< Hash<Symbol, Object> >
10
+ # * *user* (String): User or organization name, storing repositories
11
+ # * *url* (String): URL to the Github API
12
+ # * *repos* (Array<String> or Symbol): List of repository names from this project, or :all for all
13
+ attr_reader :known_github_repos
14
+
11
15
  # Initialize the DSL
12
16
  def init_github
13
17
  # List of Github repositories definitions
14
18
  # Array< Hash<Symbol, Object> >
15
19
  # Each definition is just mapping the signature of #github_repos
16
- @github_repos = []
20
+ @known_github_repos = []
17
21
  end
18
22
 
19
23
  # Register new Github repositories
@@ -23,39 +27,13 @@ module HybridPlatformsConductor
23
27
  # * *url* (String): URL to the Github API [default: 'https://api.github.com']
24
28
  # * *repos* (Array<String> or Symbol): List of repository names from this project, or :all for all [default: :all]
25
29
  def github_repos(user:, url: 'https://api.github.com', repos: :all)
26
- @github_repos << {
30
+ @known_github_repos << {
27
31
  url: url,
28
32
  user: user,
29
33
  repos: repos
30
34
  }
31
35
  end
32
36
 
33
- # Iterate over each Github repository
34
- #
35
- # Parameters::
36
- # * Proc: Code called for each Github repository:
37
- # * Parameters::
38
- # * *github* (Octokit::Client): The client instance accessing the Github API
39
- # * *repo_info* (Hash<Symbol, Object>): The repository info:
40
- # * *name* (String): Repository name.
41
- # * *slug* (String): Repository slug.
42
- def for_each_github_repo
43
- @github_repos.each do |repo_info|
44
- Octokit.configure do |c|
45
- c.api_endpoint = repo_info[:url]
46
- end
47
- Credentials.with_credentials_for(:github, @logger, @logger_stderr, url: repo_info[:url]) do |_github_user, github_token|
48
- client = Octokit::Client.new(access_token: github_token)
49
- (repo_info[:repos] == :all ? client.repositories(repo_info[:user]).map { |repo| repo[:name] } : repo_info[:repos]).each do |name|
50
- yield client, {
51
- name: name,
52
- slug: "#{repo_info[:user]}/#{name}"
53
- }
54
- end
55
- end
56
- end
57
- end
58
-
59
37
  end
60
38
 
61
39
  end
@@ -47,12 +47,6 @@ module HybridPlatformsConductor
47
47
  # Array<Hash,Symbol,Object>
48
48
  attr_reader :expected_failures
49
49
 
50
- # List of retriable errors. Each info has the following properties:
51
- # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
52
- # * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
53
- # * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
54
- attr_reader :retriable_errors
55
-
56
50
  # List of deployment schedules. Each info has the following properties:
57
51
  # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
58
52
  # * *schedule* (IceCube::Schedule): The deployment schedule
@@ -81,11 +75,6 @@ module HybridPlatformsConductor
81
75
  # * *reason* (String): Reason for this expected failure
82
76
  # Array<Hash,Symbol,Object>
83
77
  @expected_failures = []
84
- # List of retriable errors. Each info has the following properties:
85
- # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
86
- # * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
87
- # * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
88
- @retriable_errors = []
89
78
  # List of deployment schedules. Each info has the following properties:
90
79
  # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
91
80
  # * *schedule* (IceCube::Schedule): The deployment schedule
@@ -162,30 +151,6 @@ module HybridPlatformsConductor
162
151
  end
163
152
  expose :expect_tests_to_fail
164
153
 
165
- # Mark some errors on stdout to be retriable during a deploy
166
- #
167
- # Parameters::
168
- # * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
169
- def retry_deploy_for_errors_on_stdout(errors)
170
- @retriable_errors << {
171
- errors_on_stdout: errors.is_a?(Array) ? errors : [errors],
172
- nodes_selectors_stack: current_nodes_selectors_stack
173
- }
174
- end
175
- expose :retry_deploy_for_errors_on_stdout
176
-
177
- # Mark some errors on stderr to be retriable during a deploy
178
- #
179
- # Parameters::
180
- # * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
181
- def retry_deploy_for_errors_on_stderr(errors)
182
- @retriable_errors << {
183
- errors_on_stderr: errors.is_a?(Array) ? errors : [errors],
184
- nodes_selectors_stack: current_nodes_selectors_stack
185
- }
186
- end
187
- expose :retry_deploy_for_errors_on_stderr
188
-
189
154
  # Set a deployment schedule
190
155
  #
191
156
  # Parameters::
@@ -7,111 +7,116 @@ require 'hybrid_platforms_conductor/credentials'
7
7
 
8
8
  module HybridPlatformsConductor
9
9
 
10
- # Object used to access Confluence API
11
- class Confluence
10
+ # Mixin used to access Confluence API
11
+ module Confluence
12
12
 
13
- include LoggerHelpers
13
+ include Credentials
14
14
 
15
15
  # Provide a Confluence connector, and make sure the password is being cleaned when exiting.
16
16
  #
17
17
  # Parameters::
18
18
  # * *confluence_url* (String): The Confluence URL
19
- # * *logger* (Logger): Logger to be used
20
- # * *logger_stderr* (Logger): Logger to be used for stderr
21
19
  # * Proc: Code called with the Confluence instance.
22
- # * *confluence* (Confluence): The Confluence instance to use.
23
- def self.with_confluence(confluence_url, logger, logger_stderr)
24
- Credentials.with_credentials_for(:confluence, logger, logger_stderr, url: confluence_url) do |confluence_user, confluence_password|
25
- yield Confluence.new(confluence_url, confluence_user, confluence_password, logger: logger, logger_stderr: logger_stderr)
20
+ # * *confluence* (ConfluenceApi): The Confluence instance to use.
21
+ def with_confluence(confluence_url)
22
+ with_credentials_for(:confluence, resource: confluence_url) do |confluence_user, confluence_password|
23
+ yield ConfluenceApi.new(confluence_url, confluence_user, confluence_password, logger: @logger, logger_stderr: @logger_stderr)
26
24
  end
27
25
  end
28
26
 
29
- # Constructor
30
- #
31
- # Parameters::
32
- # * *confluence_url* (String): The Confluence URL
33
- # * *confluence_user_name* (String): Confluence user name to be used when querying the API
34
- # * *confluence_password* (String): Confluence password to be used when querying the API
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(confluence_url, confluence_user_name, confluence_password, logger: Logger.new($stdout), logger_stderr: Logger.new($stderr))
38
- init_loggers(logger, logger_stderr)
39
- @confluence_url = confluence_url
40
- @confluence_user_name = confluence_user_name
41
- @confluence_password = confluence_password
42
- end
27
+ # Provide an API access on Confluence
28
+ class ConfluenceApi
43
29
 
44
- # Return a Confluence storage format content from a page ID
45
- #
46
- # Parameters::
47
- # * *page_id* (String): Confluence page ID
48
- # Result::
49
- # * Nokogiri::HTML: Storage format content, as a Nokogiri object
50
- def page_storage_format(page_id)
51
- Nokogiri::HTML(call_api("plugins/viewstorage/viewpagestorage.action?pageId=#{page_id}").body)
52
- end
30
+ include LoggerHelpers
53
31
 
54
- # Return some info of a given page ID
55
- #
56
- # Parameters::
57
- # * *page_id* (String): Confluence page ID
58
- # Result::
59
- # * Hash: Page information, as returned by the Confluence API
60
- def page_info(page_id)
61
- JSON.parse(call_api("rest/api/content/#{page_id}").body)
62
- end
32
+ # Constructor
33
+ #
34
+ # Parameters::
35
+ # * *confluence_url* (String): The Confluence URL
36
+ # * *confluence_user_name* (String): Confluence user name to be used when querying the API
37
+ # * *confluence_password* (SecretString): Confluence password to be used when querying the API
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(confluence_url, confluence_user_name, confluence_password, logger: Logger.new($stdout), logger_stderr: Logger.new($stderr))
41
+ init_loggers(logger, logger_stderr)
42
+ @confluence_url = confluence_url
43
+ @confluence_user_name = confluence_user_name
44
+ @confluence_password = confluence_password
45
+ end
63
46
 
64
- # Update a Confluence page to a new content.
65
- #
66
- # Parameters::
67
- # * *page_id* (String): Confluence page ID
68
- # * *content* (String): New content
69
- # * *version* (String or nil): New version, or nil to automatically increase last existing version [default: nil]
70
- def update_page(page_id, content, version: nil)
71
- info = page_info(page_id)
72
- version = info['version']['number'] + 1 if version.nil?
73
- log_debug "Update Confluence page #{page_id}..."
74
- call_api("rest/api/content/#{page_id}", :put) do |request|
75
- request['Content-Type'] = 'application/json'
76
- request.body = {
77
- type: 'page',
78
- title: info['title'],
79
- body: {
80
- storage: {
81
- value: content,
82
- representation: 'storage'
83
- }
84
- },
85
- version: { number: version }
86
- }.to_json
47
+ # Return a Confluence storage format content from a page ID
48
+ #
49
+ # Parameters::
50
+ # * *page_id* (String): Confluence page ID
51
+ # Result::
52
+ # * Nokogiri::HTML: Storage format content, as a Nokogiri object
53
+ def page_storage_format(page_id)
54
+ Nokogiri::HTML(call_api("plugins/viewstorage/viewpagestorage.action?pageId=#{page_id}").body)
87
55
  end
88
- end
89
56
 
90
- private
57
+ # Return some info of a given page ID
58
+ #
59
+ # Parameters::
60
+ # * *page_id* (String): Confluence page ID
61
+ # Result::
62
+ # * Hash: Page information, as returned by the Confluence API
63
+ def page_info(page_id)
64
+ JSON.parse(call_api("rest/api/content/#{page_id}").body)
65
+ end
91
66
 
92
- # Call the Confluence API for a given URL and HTTP verb.
93
- # Provide a simple way to tweak the request with an optional proc.
94
- # Automatically handles authentication, base URL and error handling.
95
- #
96
- # Parameters::
97
- # * *api_path* (String): The API path to query
98
- # * *http_method* (Symbol): HTTP method to be used to create the request [default = :get]
99
- # * Proc: Optional code called to alter the request
100
- # * Parameters::
101
- # * *request* (Net::HTTPRequest): The request
102
- # Result::
103
- # * Net::HTTPResponse: The corresponding response
104
- def call_api(api_path, http_method = :get)
105
- response = nil
106
- page_url = URI.parse("#{@confluence_url}/#{api_path}")
107
- Net::HTTP.start(page_url.host, page_url.port, use_ssl: true) do |http|
108
- request = Net::HTTP.const_get(http_method.to_s.capitalize.to_sym).new(page_url.request_uri)
109
- request.basic_auth @confluence_user_name, @confluence_password
110
- yield request if block_given?
111
- response = http.request(request)
112
- raise "Confluence page API request on #{page_url} returned an error: #{response.code}\n#{response.body}\n===== Request body =====\n#{request.body}" unless response.is_a?(Net::HTTPSuccess)
67
+ # Update a Confluence page to a new content.
68
+ #
69
+ # Parameters::
70
+ # * *page_id* (String): Confluence page ID
71
+ # * *content* (String): New content
72
+ # * *version* (String or nil): New version, or nil to automatically increase last existing version [default: nil]
73
+ def update_page(page_id, content, version: nil)
74
+ info = page_info(page_id)
75
+ version = info['version']['number'] + 1 if version.nil?
76
+ log_debug "Update Confluence page #{page_id}..."
77
+ call_api("rest/api/content/#{page_id}", :put) do |request|
78
+ request['Content-Type'] = 'application/json'
79
+ request.body = {
80
+ type: 'page',
81
+ title: info['title'],
82
+ body: {
83
+ storage: {
84
+ value: content,
85
+ representation: 'storage'
86
+ }
87
+ },
88
+ version: { number: version }
89
+ }.to_json
90
+ end
113
91
  end
114
- response
92
+
93
+ private
94
+
95
+ # Call the Confluence API for a given URL and HTTP verb.
96
+ # Provide a simple way to tweak the request with an optional proc.
97
+ # Automatically handles authentication, base URL and error handling.
98
+ #
99
+ # Parameters::
100
+ # * *api_path* (String): The API path to query
101
+ # * *http_method* (Symbol): HTTP method to be used to create the request [default = :get]
102
+ # * Proc: Optional code called to alter the request
103
+ # * Parameters::
104
+ # * *request* (Net::HTTPRequest): The request
105
+ # Result::
106
+ # * Net::HTTPResponse: The corresponding response
107
+ def call_api(api_path, http_method = :get)
108
+ response = nil
109
+ page_url = URI.parse("#{@confluence_url}/#{api_path}")
110
+ Net::HTTP.start(page_url.host, page_url.port, use_ssl: true) do |http|
111
+ request = Net::HTTP.const_get(http_method.to_s.capitalize.to_sym).new(page_url.request_uri)
112
+ request.basic_auth @confluence_user_name, @confluence_password&.to_unprotected
113
+ yield request if block_given?
114
+ response = http.request(request)
115
+ raise "Confluence page API request on #{page_url} returned an error: #{response.code}\n#{response.body}\n===== Request body =====\n#{request.body}" unless response.is_a?(Net::HTTPSuccess)
116
+ end
117
+ response
118
+ end
119
+
115
120
  end
116
121
 
117
122
  end
@@ -67,7 +67,7 @@ module HybridPlatformsConductor
67
67
  # Handle the redirection of standard output and standard error to file and stdout depending on the context of the run.
68
68
  #
69
69
  # Parameters::
70
- # * *cmd* (String): The command to be run
70
+ # * *cmd* (String or SecretString): The command to be run
71
71
  # * *force_bash* (Boolean): If true, then make sure command is invoked with bash instead of sh [default: false]
72
72
  # Result::
73
73
  # * Integer: Exit code