automate_soup 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +128 -0
  9. data/Rakefile +10 -0
  10. data/automate_soup.gemspec +31 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/docs/AutomateSoup.html +2566 -0
  14. data/docs/AutomateSoup/API.html +1174 -0
  15. data/docs/AutomateSoup/Change.html +892 -0
  16. data/docs/AutomateSoup/Credentials.html +452 -0
  17. data/docs/AutomateSoup/Rest.html +321 -0
  18. data/docs/AutomateSoup/Stage.html +428 -0
  19. data/docs/AutomateSoup/Topic.html +530 -0
  20. data/docs/_index.html +166 -0
  21. data/docs/class_list.html +51 -0
  22. data/docs/coverage/.last_run.json +5 -0
  23. data/docs/coverage/.resultset.json +145 -0
  24. data/docs/coverage/.resultset.json.lock +0 -0
  25. data/docs/coverage/assets/0.10.2/application.css +799 -0
  26. data/docs/coverage/assets/0.10.2/application.js +1707 -0
  27. data/docs/coverage/assets/0.10.2/colorbox/border.png +0 -0
  28. data/docs/coverage/assets/0.10.2/colorbox/controls.png +0 -0
  29. data/docs/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
  30. data/docs/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
  31. data/docs/coverage/assets/0.10.2/favicon_green.png +0 -0
  32. data/docs/coverage/assets/0.10.2/favicon_red.png +0 -0
  33. data/docs/coverage/assets/0.10.2/favicon_yellow.png +0 -0
  34. data/docs/coverage/assets/0.10.2/loading.gif +0 -0
  35. data/docs/coverage/assets/0.10.2/magnify.png +0 -0
  36. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  37. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  38. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  39. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  40. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  41. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  42. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  43. data/docs/coverage/assets/0.10.2/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  44. data/docs/coverage/assets/0.10.2/smoothness/images/ui-icons_222222_256x240.png +0 -0
  45. data/docs/coverage/assets/0.10.2/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  46. data/docs/coverage/assets/0.10.2/smoothness/images/ui-icons_454545_256x240.png +0 -0
  47. data/docs/coverage/assets/0.10.2/smoothness/images/ui-icons_888888_256x240.png +0 -0
  48. data/docs/coverage/assets/0.10.2/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  49. data/docs/coverage/index.html +968 -0
  50. data/docs/css/common.css +1 -0
  51. data/docs/css/full_list.css +58 -0
  52. data/docs/css/style.css +492 -0
  53. data/docs/file.README.html +211 -0
  54. data/docs/file_list.html +56 -0
  55. data/docs/frames.html +17 -0
  56. data/docs/index.html +211 -0
  57. data/docs/js/app.js +248 -0
  58. data/docs/js/full_list.js +216 -0
  59. data/docs/js/jquery.js +4 -0
  60. data/docs/method_list.html +395 -0
  61. data/docs/top-level-namespace.html +110 -0
  62. data/lib/automate_soup.rb +264 -0
  63. data/lib/automate_soup/api.rb +112 -0
  64. data/lib/automate_soup/change.rb +105 -0
  65. data/lib/automate_soup/credentials.rb +16 -0
  66. data/lib/automate_soup/rest.rb +40 -0
  67. data/lib/automate_soup/stage.rb +25 -0
  68. data/lib/automate_soup/version.rb +3 -0
  69. metadata +195 -0
@@ -0,0 +1,264 @@
1
+ require 'automate_soup/api'
2
+ require 'automate_soup/credentials'
3
+ require 'automate_soup/rest'
4
+ require 'automate_soup/stage'
5
+ require 'automate_soup/change'
6
+ require 'automate_soup/version'
7
+ require 'ostruct'
8
+
9
+ ##
10
+ # Top level module
11
+ #
12
+ module AutomateSoup
13
+ class << self
14
+ attr_accessor :url, :credentials, :api, :enterprise, :organization, :project, :pipeline
15
+
16
+ ##
17
+ # Setup Automate Soup client.
18
+ #
19
+ # @option url [String] The Chef Automate URL.
20
+ # @option username [String] The Chef Automate username.
21
+ # @option token [String] The Chef Automate user token.
22
+ # @option password [String] The Chef Automate user password.
23
+ #
24
+ def setup(
25
+ url: nil,
26
+ username: nil,
27
+ token: nil,
28
+ password: nil,
29
+ enterprise: 'default',
30
+ organization: nil,
31
+ project: nil,
32
+ pipeline: nil
33
+ )
34
+ @url = url
35
+ @credentials = if token
36
+ token_credentials(username, token)
37
+ else
38
+ password_credentials(username, password)
39
+ end
40
+ @api = AutomateSoup::API.new(self)
41
+ @enterprise = enterprise
42
+ @organization = organization
43
+ @project = project
44
+ @pipeline = pipeline
45
+ self
46
+ end
47
+
48
+ ##
49
+ # Check the status of Automate
50
+ #
51
+ def status
52
+ o = @api.status
53
+ OpenStruct.new o
54
+ end
55
+
56
+ ##
57
+ # Fetch all organizations under an enterprise
58
+ #
59
+ def orgs(enterprise = 'default')
60
+ @api.orgs enterprise
61
+ end
62
+
63
+ ##
64
+ # Fetch all projects under an enterprise, organization pair
65
+ #
66
+ def projects(enterprise: 'default', organization: nil)
67
+ @api.projects(enterprise: enterprise, organization: organization)
68
+ end
69
+
70
+ ##
71
+ # Fetch all pipelines of a project under an enterprise, organization pair
72
+ #
73
+ def pipelines(enterprise: 'default', organization: nil, project: nil)
74
+ @api.pipelines(enterprise: enterprise, organization: organization, project: project)
75
+ end
76
+
77
+ ##
78
+ # Fetch a pipeline of a project under an enterprise, organization pair.
79
+ #
80
+ def pipeline(enterprise: 'default', organization: nil, project: nil, pipeline: nil)
81
+ arr = []
82
+ @api.pipeline(enterprise: enterprise, organization: organization, project: project, pipeline: pipeline).each do |o|
83
+ arr << OpenStruct.new(o)
84
+ end
85
+ arr
86
+ end
87
+
88
+ ##
89
+ # Filters out the topics from the pipelines changes .
90
+ #
91
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
92
+ # default.
93
+ # @option organization [String] the organization to fetch from.
94
+ # @option project [String] the project to fetch from.
95
+ # @option pipeline [String] the pipeline to fetch from.
96
+ #
97
+ def pipeline_topics(enterprise: 'default', organization: nil, project: nil, pipeline: nil)
98
+ self.pipeline(
99
+ enterprise: enterprise,
100
+ organization: organization,
101
+ project: project,
102
+ pipeline: pipeline
103
+ ).map { |p| p.topic }
104
+ end
105
+
106
+ ##
107
+ # Find a change by topic.
108
+ #
109
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
110
+ # default.
111
+ # @option organization [String] the organization to fetch from.
112
+ # @option project [String] the project to fetch from.
113
+ # @option pipeline [String] the pipeline to fetch from.
114
+ # @option topic [String] the topic to fetch a change from.
115
+ #
116
+ def change_by_topic(enterprise: @enterprise, organization: @organization, project: @project, pipeline: @pipeline, topic: nil)
117
+ o = self.pipeline(
118
+ enterprise: enterprise,
119
+ organization: organization,
120
+ project: project,
121
+ pipeline: pipeline
122
+ ).select { |p| p.topic.eql?(topic) }.first
123
+ AutomateSoup::Change.new o
124
+ end
125
+
126
+ ##
127
+ # Approve a change by change topic.
128
+ #
129
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
130
+ # default.
131
+ # @option organization [String] the organization to fetch from.
132
+ # @option project [String] the project to fetch from.
133
+ # @option pipeline [String] the pipeline to fetch from.
134
+ # @option topic [String] the change topic to approve
135
+ # @option wait [Boolean] to wait for the approval stages to complete.
136
+ # @option timeout [Integer] the time in seconds to wait between requests defaults
137
+ # to 10
138
+ # @option retries [Integer] the amount of retries to make, defaults to 5
139
+ #
140
+ def approve_change(enterprise: @enterprise, organization: @organization, project: @project, pipeline: @pipeline, topic: nil, wait: false, timeout: 10, retries: 5)
141
+ o = self.change_by_topic(
142
+ enterprise: enterprise,
143
+ organization: organization,
144
+ project: project,
145
+ pipeline: pipeline,
146
+ topic: topic
147
+ )
148
+ if wait && !o.approvable? && !o.deliverable?
149
+ times = 1
150
+ while times <= retries
151
+ o = self.change_by_topic(
152
+ enterprise: enterprise,
153
+ organization: organization,
154
+ project: project,
155
+ pipeline: pipeline,
156
+ topic: topic
157
+ )
158
+ break if o.approvable?
159
+ return false if o.current_stage.failed?
160
+ puts "Stage #{o.current_stage.stage}: #{o.current_stage.status} retries #{times}/#{retries}"
161
+ sleep timeout
162
+ times += 1
163
+ end
164
+ end
165
+
166
+ o.approve
167
+ return true if !wait && o.deliverable?
168
+ times = 1
169
+ while times <= retries
170
+ o = self.change_by_topic(
171
+ enterprise: enterprise,
172
+ organization: organization,
173
+ project: project,
174
+ pipeline: pipeline,
175
+ topic: topic
176
+ )
177
+ break if o.deliverable?
178
+ return false if o.current_stage.failed?
179
+ puts "Stage #{o.current_stage.stage}: #{o.current_stage.status} retries #{times}/#{retries}"
180
+ times += 1
181
+ sleep timeout
182
+ end
183
+ true
184
+ end
185
+
186
+
187
+ ##
188
+ # Delivery a change by a topic
189
+ #
190
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
191
+ # default.
192
+ # @option organization [String] the organization to fetch from.
193
+ # @option project [String] the project to fetch from.
194
+ # @option pipeline [String] the pipeline to fetch from.
195
+ # @option topic [String] the change topic to approve
196
+ # @option wait [Boolean] to wait for the approval stages to complete.
197
+ # @option timeout [Integer] the time in seconds to wait between requests defaults
198
+ # to 10
199
+ # @option retries [Integer] the amount of retries to make, defaults to 5
200
+ #
201
+ def deliver_change(enterprise: @enterprise, organization: @organization, project: @project, pipeline: @pipeline, topic: nil, wait: false, timeout: 10, retries: 5)
202
+ o = self.change_by_topic(
203
+ enterprise: enterprise,
204
+ organization: organization,
205
+ project: project,
206
+ pipeline: pipeline,
207
+ topic: topic
208
+ )
209
+ return false if !o.deliverable?
210
+ if wait && !o.deliverable?
211
+ times = 1
212
+ while times <= retries
213
+ o = self.change_by_topic(
214
+ enterprise: enterprise,
215
+ organization: organization,
216
+ project: project,
217
+ pipeline: pipeline,
218
+ topic: topic
219
+ )
220
+ break if o.deliverable?
221
+ return false if o.current_stage.failed?
222
+ puts "Stage #{o.current_stage.stage}: #{o.current_stage.status} retries #{times}/#{retries}"
223
+ sleep timeout
224
+ times += 1
225
+ end
226
+ end
227
+
228
+ o.deliver
229
+ return true if !wait && o.delivered?
230
+ times = 1
231
+ while times <= retries
232
+ o = self.change_by_topic(
233
+ enterprise: enterprise,
234
+ organization: organization,
235
+ project: project,
236
+ pipeline: pipeline,
237
+ topic: topic
238
+ )
239
+ break if o.delivered?
240
+ return false if o.current_stage.failed?
241
+ puts "Stage #{o.current_stage.stage}: #{o.current_stage.status} retries #{times}/#{retries}"
242
+ times += 1
243
+ sleep timeout
244
+ end
245
+ true
246
+ end
247
+
248
+ private
249
+
250
+ def password_credentials(username, password)
251
+ AutomateSoup::Credentials.new(
252
+ username: username,
253
+ password: password
254
+ )
255
+ end
256
+
257
+ def token_credentials(username, token)
258
+ AutomateSoup::Credentials.new(
259
+ username: username,
260
+ token: token
261
+ )
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,112 @@
1
+ module AutomateSoup
2
+ ##
3
+ # API class to interact with chef automate
4
+ class API
5
+ def initialize(soup)
6
+ @soup = soup
7
+ end
8
+
9
+ ##
10
+ # Get the status of the Automate API
11
+ #
12
+ def status
13
+ AutomateSoup::Rest.get(
14
+ url: "#{@soup.url}/api/_status",
15
+ username: @soup.credentials.username,
16
+ token: @soup.credentials.token
17
+ )
18
+ end
19
+
20
+ ##
21
+ # Get the organizations given the enterprise.
22
+ #
23
+ # @param enterprise [String] the enterprise to fetch orgs from, defaults to
24
+ # default.
25
+ #
26
+ def orgs(enterprise = 'default')
27
+ @hash = AutomateSoup::Rest.get(
28
+ url: "#{@soup.url}/api/v0/e/#{enterprise}/orgs",
29
+ username: @soup.credentials.username,
30
+ token: @soup.credentials.token
31
+ )
32
+ raise "Failed to fetch orgs under enterprise #{enterprise}" unless @hash['orgs']
33
+ @hash['orgs']
34
+ end
35
+
36
+ ##
37
+ # Get the projects under and organization given the enterprise.
38
+ #
39
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
40
+ # default.
41
+ # @option organization [String] the organization to fetch projects from.
42
+ #
43
+ def projects(enterprise: 'default', organization: nil)
44
+ @hash = AutomateSoup::Rest.get(
45
+ url: "#{@soup.url}/api/v0/e/#{enterprise}/orgs/#{organization}/projects",
46
+ username: @soup.credentials.username,
47
+ token: @soup.credentials.token
48
+ )
49
+
50
+ rescue JSON::ParserError
51
+ raise "Failed to fetch projects under organization #{organization} enterprise #{enterprise}"
52
+ end
53
+
54
+ ##
55
+ # Fetch a project under an enterprise, organization pair
56
+ #
57
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
58
+ # default.
59
+ # @option organization [String] the organization to fetch projects from.
60
+ # @option project [String] the organization to fetch projects from.
61
+ #
62
+ def project(enterprise: 'default', organization: nil, project: nil)
63
+ @hash = AutomateSoup::Rest.get(
64
+ url: "#{@soup.url}/api/v0/e/#{enterprise}/orgs/#{organization}/projects/#{project}/pipelines",
65
+ username: @soup.credentials.username,
66
+ token: @soup.credentials.token
67
+ )
68
+
69
+ rescue JSON::ParserError
70
+ raise "Failed to fetch projects under organization #{organization} enterprise #{enterprise}"
71
+ end
72
+
73
+ ##
74
+ # Fetch all project pipelines under an enterprise, organization pair
75
+ #
76
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
77
+ # default.
78
+ # @option organization [String] the organization to fetch pipelines from.
79
+ # @option project [String] the project to fetch pipelines from.
80
+ #
81
+ def pipelines(enterprise: 'default', organization: nil, project: nil)
82
+ @hash = AutomateSoup::Rest.get(
83
+ url: "#{@soup.url}/api/v0/e/#{enterprise}/orgs/#{organization}/projects/#{project}/pipelines",
84
+ username: @soup.credentials.username,
85
+ token: @soup.credentials.token
86
+ )
87
+ @hash['pipelines']
88
+
89
+ rescue JSON::ParserError
90
+ raise "Failed to fetch pipelines under organization #{organization} enterprise #{enterprise}"
91
+ end
92
+
93
+ ##
94
+ # Fetch a projects pipeline under an enterprise, organization pair
95
+ #
96
+ # @option enterprise [String] the enterprise to fetch org from, defaults to
97
+ # default.
98
+ # @option organization [String] the organization to fetch from.
99
+ # @option project [String] the project to fetch from.
100
+ # @option pipeline [String] the pipeline to fetch from.
101
+ #
102
+ def pipeline(enterprise: 'default', organization: nil, project: nil, pipeline: nil)
103
+ @hash = AutomateSoup::Rest.get(
104
+ url: "#{@soup.url}/api/v0/e/#{enterprise}/orgs/#{organization}/projects/#{project}/changes?pipeline=#{pipeline}&limit=25",
105
+ username: @soup.credentials.username,
106
+ token: @soup.credentials.token
107
+ )
108
+ rescue JSON::ParserError
109
+ raise "Failed to fetch pipelines under organization #{organization} enterprise #{enterprise}"
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,105 @@
1
+ require 'ostruct'
2
+
3
+ module AutomateSoup
4
+ ##
5
+ # Class to represent operations on a change.
6
+ #
7
+ class Change
8
+ def initialize(hash)
9
+ @source = OpenStruct.new hash
10
+ end
11
+
12
+ ##
13
+ # Delegate method missing to the underlying OpenStruct
14
+ #
15
+ def method_missing(method, *args, &block)
16
+ @source.send(method, *args, &block)
17
+ end
18
+
19
+ ##
20
+ # Determing the current stage of the change.
21
+ # @return [AutomateSoup::Stage] the current stage.
22
+ def current_stage
23
+ Stage.new @source.stages.last
24
+ end
25
+
26
+ ##
27
+ # Wrapper for the _links property on the struct
28
+ #
29
+ def links
30
+ @source._links
31
+ end
32
+
33
+ ##
34
+ # Determine if the change has been delivered successfully.
35
+ #
36
+ # @return [Boolean] if this change is delivered
37
+ def delivered?
38
+ (current_stage.stage.eql?('delivered') &&
39
+ current_stage.status.eql?('passed') &&
40
+ !AutomateSoup.url.nil? &&
41
+ !AutomateSoup.credentials.nil?)
42
+ end
43
+
44
+ ##
45
+ # Determine if the change is deliverable.
46
+ #
47
+ # @return [Boolean] if this change is deliverable
48
+ def deliverable?
49
+ (current_stage.stage.eql?('acceptance') &&
50
+ current_stage.status.eql?('passed') &&
51
+ !AutomateSoup.url.nil? &&
52
+ !AutomateSoup.credentials.nil? &&
53
+ !links.nil? &&
54
+ !links['deliver'].nil? &&
55
+ !links['deliver']['href'].nil?)
56
+ end
57
+
58
+ ##
59
+ # Determine if the change is approvable.
60
+ #
61
+ # @return [Boolean] if this change is approvable
62
+ def approvable?
63
+ (current_stage.stage.eql?('verify') &&
64
+ current_stage.status.eql?('passed') &&
65
+ !AutomateSoup.url.nil? &&
66
+ !AutomateSoup.credentials.nil? &&
67
+ !links.nil? &&
68
+ !links['approve'].nil? &&
69
+ !links['approve']['href'].nil?)
70
+ end
71
+
72
+ ##
73
+ # Approve this change.
74
+ def approve
75
+ return nil if current_stage.stage != 'verify'
76
+ raise 'Must run AutomateSoup.setup first' if AutomateSoup.url.nil? || AutomateSoup.credentials.nil?
77
+ raise 'Approve link not available' if links.nil? || links['approve'].nil? || links['approve']['href'].nil?
78
+ url = "#{AutomateSoup.url}#{links['approve']['href']}"
79
+ res = AutomateSoup::Rest.post(
80
+ url: url,
81
+ username: AutomateSoup.credentials.username,
82
+ token: AutomateSoup.credentials.token
83
+ )
84
+ raise "Failed to approve change: #{res.code}" if res.code != '204'
85
+ true
86
+ end
87
+
88
+ ##
89
+ # Deliver this change.
90
+ def deliver
91
+ raise 'Must approve change first' if current_stage.stage.eql? 'verify'
92
+ return nil if current_stage.stage != 'acceptance'
93
+ raise 'Must run AutomateSoup.setup first' if AutomateSoup.url.nil? || AutomateSoup.credentials.nil?
94
+ raise 'Deliver link not available' if links.nil? || links['deliver'].nil? || links['deliver']['href'].nil?
95
+ url = "#{AutomateSoup.url}#{links['deliver']['href']}"
96
+ res = AutomateSoup::Rest.post(
97
+ url: url,
98
+ username: AutomateSoup.credentials.username,
99
+ token: AutomateSoup.credentials.token
100
+ )
101
+ raise "Failed to deliver change: #{res.code}" if res.code != '204'
102
+ true
103
+ end
104
+ end
105
+ end