automate_soup 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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