lita-rundeck 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a05e15d4f0a1ba9383674cf2660e484e21314818
4
+ data.tar.gz: 45cc66b54ccb75da8e7e8b2220ef3a0ee266f85d
5
+ SHA512:
6
+ metadata.gz: ec1be3ae856e800ece20de568b48ded35de604642bd3d7353531de37dd0b3bf5692a082017371c0a06bcc65478357a8f23b6c422f610708888be27ee366b3201
7
+ data.tar.gz: 08a903ce8c3d4b8a8cf721a635f30c6119d4de34f8556ae131b859077e23c24ab03623e9edeabcccdeae8dd13818fd7293dfeb70ead70353d0d9e51d77bdb241
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script: bundle exec rake
5
+ before_install:
6
+ - gem update --system
7
+ services:
8
+ - redis-server
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Harlan Barnes
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # lita-rundeck
2
+
3
+ [![Build Status](https://travis-ci.org/harlanbarnes/lita-rundeck.png?branch=master)](https://travis-ci.org/harlanbarnes/lita-rundeck)
4
+ [![Coverage Status](https://coveralls.io/repos/harlanbarnes/lita-rundeck/badge.png)](https://coveralls.io/r/harlanbarnes/lita-rundeck)
5
+
6
+ **lita-rundeck** is a handler for [Lita](https://github.com/jimmycuadra/lita) that interacts with a [Rundeck](http://rundeck.org/) server.
7
+
8
+ ## Installation
9
+
10
+ Add lita-rundeck to your Lita instance's Gemfile:
11
+
12
+ ``` ruby
13
+ gem "lita-rundeck"
14
+ ```
15
+
16
+ ## Configuration
17
+
18
+ ### Required Attributes
19
+
20
+ * ```url``` (String) - URL to then Rundeck server. Default: ```nil```
21
+ * ```token``` (String) - API token for access to the Rundeck server. Default: ```nil```
22
+
23
+ ### Optional Attributes
24
+
25
+ * ```api_debug``` (Boolean) - When ```conf.robot.log_level``` is set to ```:debug``` enable verbose details of the API interaction. Default: ```false```
26
+
27
+ ### Example
28
+
29
+ ```ruby
30
+ Lita.configure do |config|
31
+ config.handlers.rundeck.url = "https://rundeck.mycompany.org"
32
+ config.handlers.rundeck.token = "abcdefghijzlmnopqrstuvwxyz"
33
+ config.handlers.rundeck.api_debug = true
34
+ end
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Run
40
+
41
+ Start a job execution
42
+
43
+ ```
44
+ Lita > lita rundeck run aliasfoo
45
+ Execution 297 is running
46
+
47
+ Lita > rundeck run aliasfoo --options SECONDS=60
48
+ Execution 298 is running
49
+
50
+ Lita > rundeck run --project Litatest --job dateoutput
51
+ Execution 299 is running
52
+
53
+ Lita > rundeck run --project Litatest --job dateoutput --options SECONDS=60
54
+ Execution 300 is running
55
+
56
+ Lita > rundeck run --project Litatest --job dateoutput --options SECONDS=60,FORMAT=iso8601
57
+ Execution 301 is running
58
+ ```
59
+
60
+ * Users must be [added by a Lita admin](http://docs.lita.io/getting-started/usage/#authorization-groups) to the rundeck_users group to execute jobs
61
+ * Job names with spaces need to be quoted
62
+ * Multiple options can be submitted via comma-delimited key pairs
63
+ * Jobs identified via fully qualified project and job names (i.e. --project and --job) or via a registered alias
64
+
65
+ ### Projects
66
+
67
+ List projects
68
+
69
+ ```
70
+ Lita > lita rundeck projects
71
+ [Litatest] - https://rundeck.mycompany.org/api/10/project/Litatest
72
+ ```
73
+
74
+ ### Jobs
75
+
76
+ List jobs
77
+
78
+ ```
79
+ Lita > lita rundeck jobs
80
+ [Litatest] - dateoutput
81
+ [Litatest] - refreshcache
82
+ ```
83
+
84
+ ### Executions
85
+
86
+ List execuctions (activity)
87
+
88
+ ```
89
+ Lita > lita rundeck execuctions
90
+ 295 succeeded Shell User [Litatest] dateoutput SECONDS:600 start:2014-08-16T04:36:43Z end:2014-08-16T04:46:46Z
91
+ 296 succeeded Shell User [Litatest] dateoutput SECONDS:60 start:2014-08-16T05:17:07Z end:2014-08-16T05:18:09Z
92
+ ```
93
+
94
+ * Fields are: id, status, submitter, project, job, options, start and end
95
+
96
+ Optionally, limit the output to a number of executions
97
+
98
+ ```
99
+ Lita > lita rundeck execuctions 1
100
+ 296 succeeded Shell User [Litatest] dateoutput SECONDS:60 start:2014-08-16T05:17:07Z end:2014-08-16T05:18:09Z
101
+ ```
102
+
103
+ ### Running
104
+
105
+ List currently running executions
106
+
107
+ ```
108
+ Lita > lita rundeck running
109
+ 297 running Shell User [Litatest] dateoutput SECONDS:60 start:2014-08-16T05:46:32Z
110
+ ```
111
+
112
+ ### Options
113
+
114
+ List options for a job in detail
115
+
116
+ ```
117
+ Lita > lita rundeck options aliasfoo
118
+ [Litatest] - dateoutput
119
+ * SECONDS (REQUIRED) - Number of seconds to run
120
+ ```
121
+
122
+ * Aliases or full qualified job and project names (i.e. --project and --name) can be used
123
+
124
+ ### Aliases
125
+
126
+ List aliases with
127
+
128
+ ```
129
+ Lita > lita rundeck aliases
130
+ Alias = [Project] - Job
131
+ aliasfoo = [Litatest] - dateoutput
132
+ ```
133
+
134
+ Register a new alias
135
+
136
+ ```
137
+ Lita > rundeck alias register aliasfoo --project Litatest --job dateoutput
138
+ Alias registered
139
+ ```
140
+
141
+ Forget (remove) an alias
142
+
143
+ ```
144
+ Lita > lita rundeck alias forget aliasfoo
145
+ Alias removed
146
+ ```
147
+
148
+ ### Info
149
+
150
+ Lists the server version and users allowed to execute jobs
151
+
152
+ ```
153
+ Lita > lita rundeck info
154
+ System Stats for Rundeck 2.0.4 on node rundeck.mycompany.org
155
+ Users allowed to execute jobs: Shell User
156
+ ```
157
+
158
+ ## License
159
+
160
+ [MIT](http://opensource.org/licenses/MIT)
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,7 @@
1
+ require "lita"
2
+
3
+ Lita.load_locales Dir[File.expand_path(
4
+ File.join("..", "..", "locales", "*.yml"), __FILE__
5
+ )]
6
+
7
+ require "lita/handlers/rundeck"
@@ -0,0 +1,648 @@
1
+ require 'xmlsimple'
2
+ require 'lita-keyword-arguments'
3
+
4
+ module Lita
5
+ module Handlers
6
+ class Rundeck < Handler
7
+
8
+ route /rundeck info/i,
9
+ :info, command: true, help: {
10
+ t("help.info_key") => t("help.info_value")
11
+ }
12
+
13
+ route /rundeck project(?:s)?/i,
14
+ :projects, command: true, help: {
15
+ t("help.projects_key") => t("help.projects_value")
16
+ }
17
+
18
+ route /rundeck job(?:s)?/i,
19
+ :jobs, command: true, help: {
20
+ t("help.jobs_key") => t("help.jobs_value")
21
+ }
22
+
23
+ route /rundeck exec(?:utions)?(?: (\d+)?)?/i,
24
+ :executions, command: true, help: {
25
+ t("help.exec_key") => t("help.exec_value")
26
+ }
27
+
28
+ route /rundeck running(?: (\d+)?)?/i,
29
+ :running, command: true, help: {
30
+ t("help.running_key") => t("help.running_value")
31
+ }
32
+
33
+ route /rundeck alias(?:es)?\s*$/i,
34
+ :aliases, command: true, help: {
35
+ t("help.alias_key") => t("help.alias_value")
36
+ }
37
+
38
+ route /rundeck alias register ([a-zA-Z0-9\-+.]+)\s*/i,
39
+ :alias_register,
40
+ command: true,
41
+ kwargs: {
42
+ project: {
43
+ short: "p"
44
+ },
45
+ job: {
46
+ short: "j"
47
+ },
48
+ options: {
49
+ short: "o"
50
+ }
51
+ },
52
+ help: {
53
+ t("help.alias_register_key") => t("help.alias_register_value")
54
+ }
55
+
56
+ route /rundeck alias forget ([a-zA-Z0-9\-+.]+)?\s*/i,
57
+ :alias_forget, command: true, help: {
58
+ t("help.alias_forget_key") => t("help.alias_forget_value")
59
+ }
60
+
61
+ route /rundeck run(?!ning)(?: ([a-zA-Z0-9\-+.]+))?\s*/i,
62
+ :run,
63
+ command: true,
64
+ kwargs: {
65
+ project: {
66
+ short: "p"
67
+ },
68
+ job: {
69
+ short: "j"
70
+ },
71
+ options: {
72
+ short: "o"
73
+ }
74
+ },
75
+ help: {
76
+ t("help.run_key") => t("help.run_value")
77
+ }
78
+
79
+ route /rundeck options(?: ([a-zA-Z0-9\-+.]+))?\s*/i,
80
+ :options,
81
+ command: true,
82
+ kwargs: {
83
+ project: {
84
+ short: "p"
85
+ },
86
+ job: {
87
+ short: "j"
88
+ }
89
+ },
90
+ help: {
91
+ t("help.options_key") => t("help.options_value")
92
+ }
93
+
94
+
95
+ def self.default_config(config)
96
+ config.url = nil
97
+ config.token = nil
98
+ config.api_debug = false
99
+ end
100
+
101
+ def info(response)
102
+ text = []
103
+ text.push(client.info)
104
+ if users
105
+ text.push(t("info.users_allowed") +
106
+ users.map{ |u| u.name }.join(",") )
107
+ else
108
+ text.push(t("info.no_users"))
109
+ end
110
+ response.reply text.join("\n")
111
+ end
112
+
113
+ def projects(response)
114
+ text = []
115
+ client.projects.each do |p|
116
+ text.push("[#{p.name}] - #{p.href}")
117
+ end
118
+ if text.empty?
119
+ response.reply t("projects.none")
120
+ else
121
+ response.reply text.join("\n")
122
+ end
123
+ end
124
+
125
+ def jobs(response)
126
+ text = []
127
+ client.jobs.each do |j|
128
+ line = ""
129
+ if alias_name = aliasdb.reverse(j.project,j.name)
130
+ line = "#{alias_name} = "
131
+ end
132
+ text.push(line + "[#{j.project}] - #{j.name}")
133
+ end
134
+ if text.empty?
135
+ response.reply t("jobs.none")
136
+ else
137
+ response.reply text.join("\n")
138
+ end
139
+ end
140
+
141
+ def executions(response)
142
+ max = response.matches[0][0] if response.matches[0][0]
143
+
144
+ text = []
145
+ client.executions(max).each do |e|
146
+ text.push(e.pretty_print)
147
+ end
148
+
149
+ if text.empty?
150
+ response.reply t("executions.none")
151
+ else
152
+ response.reply text.join("\n")
153
+ end
154
+ end
155
+
156
+ def running(response)
157
+ max = response.matches[0][0] if response.matches[0][0]
158
+
159
+ text = []
160
+ client.running(max).each do |e|
161
+ text.push(e.pretty_print)
162
+ end
163
+
164
+ if text.empty?
165
+ response.reply t("executions.none")
166
+ else
167
+ response.reply text.join("\n")
168
+ end
169
+ end
170
+
171
+ def run(response)
172
+ unless user_in_group?(response.user)
173
+ response.reply t("run.unauthorized")
174
+ return
175
+ end
176
+
177
+ args = response.extensions[:kwargs]
178
+ name = response.matches[0][0]
179
+
180
+ project = args[:project]
181
+ job = args[:job]
182
+ user = response.user.name || response.user.id || robot.name
183
+ options = parse_options(args[:options]) if args[:options]
184
+
185
+ # keywoard arguments win over an alias (if someone happens to give both)
186
+ unless project && job
187
+ project, job = aliasdb.forward(name)
188
+ end
189
+
190
+ unless project && job
191
+ response.reply t("misc.job_not_found")
192
+ return
193
+ end
194
+
195
+ response.reply resolve(client.run(project,job,options,user))
196
+ end
197
+
198
+ def resolve(e)
199
+ case e.status
200
+ when "running"
201
+ t("run.success", id: e.id)
202
+ when "api.error.execution.conflict"
203
+ t("run.conflict")
204
+ when "api.error.item.unauthorized"
205
+ t("run.token_unauthorized")
206
+ when "api.error.job.options-invalid"
207
+ e.message.gsub(/\n/,"")
208
+ end
209
+ end
210
+
211
+ def parse_options(string)
212
+ options = {}
213
+ pairs = string.split(/\|/)
214
+ pairs.each do |p|
215
+ if p =~ /\=/
216
+ k,v = p.split(/\=/)
217
+ options[k] = v
218
+ end
219
+ end
220
+ options
221
+ end
222
+
223
+ def options(response)
224
+ args = response.extensions[:kwargs]
225
+ name = response.matches[0][0]
226
+
227
+ project = args[:project]
228
+ job = args[:job]
229
+
230
+ # keywoard arguments win over an alias (if someone happens to give both)
231
+ unless project && job
232
+ project, job = aliasdb.forward(name)
233
+ end
234
+
235
+ unless project && job
236
+ response.reply t("misc.job_not_found")
237
+ return
238
+ end
239
+
240
+ response.reply "[#{project}] - #{job}\n" +
241
+ client.definition(project,job).pretty_print_options
242
+ end
243
+
244
+ def aliases(response)
245
+ all = aliasdb.all
246
+ if all.empty?
247
+ response.reply t("alias.none")
248
+ else
249
+ text = [ t('alias.list') ]
250
+ text.push(all.map{ |a| " #{a["id"]} = [#{a["project"]}] - #{a["job"]}" })
251
+ response.reply text.join("\n")
252
+ end
253
+ end
254
+
255
+ def alias_register(response)
256
+ args = response.extensions[:kwargs]
257
+ name = response.matches[0][0]
258
+ project = args[:project]
259
+ job = args[:job]
260
+
261
+ if name && project && job
262
+ begin
263
+ aliasdb.register(name,project,job)
264
+ response.reply t("alias.registered")
265
+ rescue ArgumentError
266
+ response.reply t("alias.exists")
267
+ end
268
+ else
269
+ response.reply t("alias.format")
270
+ end
271
+ end
272
+
273
+ def alias_forget(response)
274
+ name = response.matches[0][0]
275
+ begin
276
+ aliasdb.forget(name)
277
+ response.reply t("alias.forgotten")
278
+ rescue ArgumentError
279
+ response.reply t('alias.notexists')
280
+ end
281
+ end
282
+
283
+ def user_in_group?(user)
284
+ Lita::Authorization.user_in_group?(user,:rundeck_users)
285
+ end
286
+
287
+ def users
288
+ Lita::Authorization.groups_with_users[:rundeck_users]
289
+ end
290
+
291
+ def url
292
+ @url ||= Lita.config.handlers.rundeck.url
293
+ end
294
+
295
+ def token
296
+ @token ||= Lita.config.handlers.rundeck.token
297
+ end
298
+
299
+ def api_debug
300
+ @api_debug ||= Lita.config.handlers.rundeck.api_debug
301
+ end
302
+
303
+ def client
304
+ @client ||= API::Client.new(url,token,http,log,api_debug)
305
+ end
306
+
307
+ def aliasdb
308
+ @aliasdb ||= Database::Alias.new(redis)
309
+ end
310
+
311
+ module Database
312
+ class Alias
313
+ attr_accessor :redis
314
+
315
+ def initialize(redis)
316
+ @redis = redis
317
+ end
318
+
319
+ def akey(id)
320
+ "alias:#{id}"
321
+ end
322
+
323
+ def akeys
324
+ redis.keys("alias:*")
325
+ end
326
+
327
+ def ids
328
+ akeys.map { |e| e.split(/:/).last }
329
+ end
330
+
331
+ def register(id,project,job)
332
+ if registered?(id)
333
+ raise ArgumentError, "Alias already exists"
334
+ else
335
+ save(id,project,job)
336
+ end
337
+ end
338
+
339
+ def forget(id)
340
+ if registered?(id)
341
+ delete(id)
342
+ else
343
+ raise ArgumentError, "Alias does not exist"
344
+ end
345
+ end
346
+
347
+ def save(id,project,job)
348
+ redis.hmset(akey(id), "project", project, "job", job)
349
+ end
350
+
351
+ def delete(id)
352
+ redis.del(akey(id))
353
+ end
354
+
355
+ def forward(id)
356
+ redis.hmget(akey(id), "project", "job")
357
+ end
358
+
359
+ def reverse(project,job)
360
+ ids.select{ |n| p, j = forward(n); p == project && j == job }.first
361
+ end
362
+
363
+ def all
364
+ list = []
365
+ ids.each do |id|
366
+ hash = {}
367
+ hash["id"] = id
368
+ hash["project"], hash["job"] = forward(id)
369
+ list.push(hash)
370
+ end
371
+ list
372
+ end
373
+
374
+ def registered?(id)
375
+ project, job = forward(id)
376
+ if project && job
377
+ true
378
+ else
379
+ false
380
+ end
381
+ end
382
+ end
383
+ end
384
+
385
+ module API
386
+ class Client
387
+ attr_accessor :url, :token
388
+
389
+ MAX_EXECUTIONS = 10
390
+
391
+ def self.ensure_array(ref)
392
+ return [ref] if ref.is_a?(Hash)
393
+ return ref if ref.is_a?(Array)
394
+ end
395
+
396
+ def initialize(url, token, http, log, debug=false)
397
+ @url = url
398
+ @token = token
399
+ @http = http
400
+ @log = log
401
+ @debug = debug
402
+ end
403
+
404
+ def get(path,options={})
405
+ uri = "#{@url}/#{path}"
406
+ options[:authtoken] = @token
407
+
408
+ http_response = @http.get(
409
+ uri,
410
+ options
411
+ )
412
+
413
+ # Trying to avoid nokogiri but not wanting to use ReXML directly,
414
+ # hence the xmlsimple gem. ForceArray has usually worked well for
415
+ # me, but this XML data seemed to cause it to be inconsistent. So
416
+ # the ensure_array method and a little extra code has worked.
417
+ hash = ::XmlSimple.xml_in(
418
+ http_response.body,
419
+ {
420
+ "ForceArray" => false,
421
+ "GroupTags" => {
422
+ "options" => "option"
423
+ }
424
+ }
425
+ )
426
+
427
+ if @debug
428
+ output = options.map{ |k,v| "#{k.to_s}=#{v}" }.join("&")
429
+ @log.debug "API request: GET #{uri}&#{output}"
430
+ @log.debug "API response: (HTTP #{http_response.status}) #{http_response.body}"
431
+ @log.debug "Hash: #{hash.inspect}"
432
+ end
433
+
434
+ hash
435
+ end
436
+
437
+ def info
438
+ get('/api/1/system/info')["success"][1]["message"]
439
+ end
440
+
441
+ def projects
442
+ @projects ||= Project.all(self).sort_by{|p| p.name}
443
+ end
444
+
445
+ def jobs
446
+ @jobs ||= Job.all(self).sort_by{|j| [j.project, j.name]}
447
+ end
448
+
449
+ def job(project,name)
450
+ jobs.select{|j| j.project == project && j.name == name}.first
451
+ end
452
+
453
+ def definition(project,name)
454
+ Definition.load(self,job(project,name).id)
455
+ end
456
+
457
+ def executions(max)
458
+ max ||= MAX_EXECUTIONS
459
+ @executions ||= Execution.all(self,max).sort_by{|i| i.id}.reverse[0,max.to_i].reverse
460
+ end
461
+
462
+ def running(max)
463
+ max ||= MAX_EXECUTIONS
464
+ @running ||= Running.all(self,max).sort_by{|i| i.id}.reverse[0,max].reverse
465
+ end
466
+
467
+ def run(project,name,options,user)
468
+ job = job(project,name)
469
+ Job.run(self,job.id,options,user)
470
+ end
471
+ end
472
+
473
+ class Project
474
+ attr_accessor :name, :description, :href
475
+
476
+ def self.all(client)
477
+ all = []
478
+ response = client.get("/api/1/projects")
479
+ if response["projects"]["count"].to_i > 0
480
+ Client.ensure_array(response["projects"]["project"]).each do |p|
481
+ all.push(Project.new(p))
482
+ end
483
+ end
484
+ all
485
+ end
486
+
487
+ def initialize(hash)
488
+ @name = hash["name"]
489
+ @description = hash["description"]
490
+ @href = hash["href"]
491
+ end
492
+ end
493
+
494
+ class Definition
495
+ attr_accessor :id, :name, :project, :description, :options
496
+
497
+ def self.load(client,id)
498
+ response = client.get("/api/1/job/#{id}")
499
+ if response["job"]
500
+ Definition.new(response["job"])
501
+ end
502
+ end
503
+
504
+ def initialize(hash)
505
+ @id = hash["id"]
506
+ @name = hash["name"]
507
+ @project = hash["context"]["project"]
508
+ @description = hash["description"]
509
+ if option_response = hash["context"]["options"]
510
+ @options ||= {}
511
+ Client.ensure_array(option_response).each do |o|
512
+ @options[o["name"]] = o
513
+ end
514
+ end
515
+ end
516
+
517
+ def pretty_print_options
518
+ text = []
519
+ @options.each do |name,data|
520
+ text.push(
521
+ " * #{name} " +
522
+ ( data["required"] ? "(REQUIRED) " : "" ) +
523
+ ( data["description"] ? "- #{data["description"]}" : "" )
524
+ )
525
+ end
526
+ text.join("\n")
527
+ end
528
+ end
529
+
530
+ class Job
531
+ attr_accessor :id, :name, :group, :project, :description,
532
+ :average_duration, :options
533
+
534
+ def self.all(client)
535
+ all = []
536
+ client.projects.each do |p|
537
+ response = client.get("/api/2/project/#{p.name}/jobs")
538
+ if response["jobs"]["count"].to_i > 0
539
+ Client.ensure_array(response["jobs"]["job"]).each do |j|
540
+ all.push(Job.new(j))
541
+ end
542
+ end
543
+ end
544
+ all
545
+ end
546
+
547
+ def self.run(client,id,options,user)
548
+ args = {}
549
+ args[:asUser] = user if user
550
+ if options
551
+ arg_string = []
552
+ options.each do |k,v|
553
+ arg_string.push("-#{k} #{v}")
554
+ end
555
+ args[:argString] = arg_string.join(" ")
556
+ end
557
+
558
+ api_response = client.get("/api/5/job/#{id}/run", args)
559
+
560
+ if api_response["success"]
561
+ Execution.new(api_response["executions"]["execution"])
562
+ elsif api_response["error"][0]
563
+ Execution.new(
564
+ "status" => api_response["error"][1]["code"],
565
+ "message" => api_response["error"][1]["message"]
566
+ )
567
+ end
568
+ end
569
+
570
+ def initialize(hash)
571
+ @id = hash["id"]
572
+ @name = hash["name"]
573
+ @group = hash["group"]
574
+ @project = hash["project"]
575
+ @description = hash["description"]
576
+ @average_duration = hash["average_duration"] if hash["average_duration"]
577
+ @options = Client.ensure_array(hash["options"]) if hash["options"]
578
+ end
579
+ end
580
+
581
+ class Execution
582
+ attr_accessor :id, :href, :status, :message, :project, :user, :start,
583
+ :end, :job, :description, :argstring, :successful_nodes,
584
+ :failed_nodes, :aborted_by
585
+
586
+ def self.all(client,max)
587
+ all = []
588
+ client.projects.each do |p|
589
+ response = client.get("/api/5/executions",
590
+ project: p.name, max: max)
591
+ if response["executions"]["count"].to_i > 0
592
+ list = Client.ensure_array(response["executions"]["execution"])
593
+ list.each do |e|
594
+ all.push(Execution.new(e))
595
+ end
596
+ end
597
+ end
598
+ all
599
+ end
600
+
601
+ def initialize(hash)
602
+ @id = hash["id"]
603
+ @href = hash["href"]
604
+ @status = hash["status"]
605
+ @message = hash["message"]
606
+ @project = hash["project"]
607
+ @user = hash["user"]
608
+ @start = hash["date-started"]["content"] if hash["date-started"]
609
+ @end = hash["date-ended"]["content"] if hash["date-ended"]
610
+ @job = Job.new(hash["job"]) if hash["job"]
611
+ @description = hash["description"]
612
+ @argstring = hash["argstring"]
613
+ @aborted_by = hash["aborted_by"]
614
+ @successful_nodes = Client.ensure_array(hash["successful_nodes"]["node"]) if hash["successful_nodes"]
615
+ @failed_nodes = Client.ensure_array(hash["failed_nodes"]["node"]) if hash["failed_nodes"]
616
+ end
617
+
618
+ def pretty_print
619
+ line = "#{@id} #{@status} #{@user} [#{@job.project}] #{@job.name} "
620
+ line += @job.options.map { |o| "#{o["name"]}:#{o["value"]}" }.join(", ") + " " if @job.options
621
+ line += "start:#{@start}" + ( @end ? " end:#{@end}" : "" )
622
+ end
623
+ end
624
+
625
+ class Running < Execution
626
+
627
+ def self.all(client,max)
628
+ all = []
629
+ client.projects.each do |p|
630
+ response = client.get("/api/5/executions/running",
631
+ project: p.name, max: max)
632
+ if response["executions"]["count"].to_i > 0
633
+ list = Client.ensure_array(response["executions"]["execution"])
634
+ list.each do |e|
635
+ all.push(Execution.new(e))
636
+ end
637
+ end
638
+ end
639
+ all
640
+ end
641
+
642
+ end
643
+ end
644
+ end
645
+
646
+ Lita.register_handler(Rundeck)
647
+ end
648
+ end