autojenkins 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/bin/mjenk +160 -0
  2. data/lib/autojenkins.rb +294 -0
  3. data/lib/test.rb +82 -0
  4. metadata +67 -0
data/bin/mjenk ADDED
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
4
+ require 'optparse'
5
+ require 'autojenkins'
6
+
7
+ URL=''
8
+ AUTH={}
9
+ TOKEN=""
10
+
11
+ mJENK = AutoJenkins::Jenkins.new(URL, AUTH)
12
+ ###
13
+ # Commands
14
+ #
15
+ module AutoJenkins
16
+ class Command
17
+ attr_accessor :jenk,
18
+ :error,
19
+ :errmsg
20
+
21
+ def initialize(jenk)
22
+ @jenk = jenk
23
+ @error = 0
24
+ @errmsg = ""
25
+ end
26
+
27
+ end
28
+
29
+ class JobCommand < Command
30
+ def exec(hash)
31
+
32
+ jobs = @jenk.all_jobs()
33
+
34
+ if not hash.key?(:jobname)
35
+
36
+ jobs.each do |n|
37
+ puts "Name: #{n.name} -> Descr: #{n.get_description} -> URL: #{n.get_url}"
38
+ end
39
+
40
+ else
41
+ jobs.each do |n|
42
+ if n.name == hash[:jobname]
43
+
44
+ if hash.key?(:delete)
45
+ n.delete()
46
+ elsif hash.key?(:build)
47
+ out = n.launch(hash[:token])
48
+ if out
49
+ puts "Lauch successful"
50
+ else
51
+ puts "Could not launch"
52
+ end
53
+
54
+ puts n.get_url
55
+ else
56
+ puts n.get_config()
57
+ end
58
+
59
+ return
60
+ end
61
+ end
62
+
63
+ @error = 2
64
+ @errmsg = "Job #{hash[:jobname]} not found"
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ cCommands = {
71
+ "job" => AutoJenkins::JobCommand
72
+ }
73
+
74
+ # End Commands
75
+ options = {:verbose => false,
76
+ :build => true,
77
+ :emails => "",
78
+ :auth => nil,
79
+ :token => ""}
80
+
81
+ p = OptionParser.new do |opts|
82
+ opts.banner = "Usage: mjenk CMD [options]"
83
+
84
+ opts.on("-n", "--dont-run", "Do not run after creating the job") do
85
+ options[:build] = false
86
+ end
87
+
88
+ opts.on("-v", "--verbose", "Be verbose") do
89
+ options[:verbose] = true
90
+ end
91
+
92
+ opts.on("-h", "--url URL", String, "Jenkins URL") do |u|
93
+ URL = u
94
+ end
95
+
96
+ opts.on("-c", "--config-file URL", String, "Jenkins Configuration File") do |cf|
97
+ options[:auth] = cf
98
+ end
99
+
100
+ opts.on("-j", "--jobname JOBNAME", String, "Name of the job to retrieve") do |n|
101
+ options[:jobname] = n
102
+ end
103
+
104
+ opts.on("-d", "--delete", String, "Job: If -j is given, delete the job") do |d|
105
+ options[:delete] = true
106
+ end
107
+
108
+ opts.on("-B", "--build-job", String, "Job: Launches a new build on the given job") do |tst|
109
+ options[:tests] = tst
110
+ end
111
+
112
+ end
113
+
114
+ p.parse!(ARGV)
115
+
116
+ if options[:auth] == nil
117
+ config = File.expand_path("~/.mjenk")
118
+ else
119
+ config = options[:auth]
120
+ end
121
+
122
+ auth = AutoJenkins::Helpers.parseConfig(config)
123
+ vCreds = [auth['USER'], auth['PASSWD']]
124
+ vURL = auth['URL']
125
+
126
+ if options[:token] == nil
127
+ # Wasn't overriden..
128
+
129
+ if auth.has_key?('TOKEN')
130
+ options[:token] = auth['TOKEN']
131
+ end
132
+ end
133
+
134
+ cCMD = nil
135
+ mJENK = AutoJenkins::Jenkins.new(vURL, vCreds)
136
+
137
+ # Lets process the CMD
138
+ cCMD = "job"
139
+ if ARGV.length > 0
140
+ cCMD = ARGV[0]
141
+ end
142
+
143
+ if cCommands.key?(cCMD)
144
+ cmd = cCommands[cCMD].new(mJENK)
145
+
146
+ begin
147
+ cmd.exec(options)
148
+ rescue AutoJenkins::ExUnauthorized => e1
149
+ puts e1.message
150
+ puts "Got an Unathorized error. Check your credentials."
151
+ rescue AutoJenkins::ExUndefined => e2
152
+ puts "Got an Undefined error. Maybe trying to build a disabled job?"
153
+ puts e2.message
154
+ end
155
+
156
+ if cmd.error != 0
157
+ puts cmd.errmsg
158
+ exit cmd.error
159
+ end
160
+ end
@@ -0,0 +1,294 @@
1
+ #Module autojenkins
2
+ #Ruby module to talk with Jenkins using JSON API
3
+ #
4
+
5
+ require 'pp'
6
+ require 'uri'
7
+ require 'json'
8
+ require 'yaml'
9
+ require 'net/http'
10
+ require 'nokogiri'
11
+
12
+ module AutoJenkins
13
+
14
+ LOGIN = 'LOGIN'
15
+ PASSWD = 'PASSWD'
16
+ BASE_URL = "/api/json"
17
+
18
+ URLs = {
19
+ 'LIST' => "#{BASE_URL}",
20
+ 'JOBINFO' => "/job/%s#{BASE_URL}",
21
+ 'CONFIG' => "/job/%s/config.xml",
22
+ '_ABLE' => "/job/%s/%s",
23
+ 'DELETE' => "/job/%s/doDelete",
24
+ 'NEWJOB' => "/createItem?name=%s",
25
+ 'LAUNCH' => "/job/%s/build/api/json",
26
+ 'BUILD' => "/job/%s/%i#{BASE_URL}",
27
+ }
28
+
29
+ class JenkinsException < Exception
30
+ end
31
+
32
+ class ExUnauthorized < Exception
33
+ end
34
+
35
+ class ExUndefined < Exception
36
+ end
37
+
38
+ class Helpers
39
+
40
+ def self.parseConfig(config)
41
+ tree = {}
42
+
43
+ begin
44
+ tree = YAML::parse(File.open(config)).transform
45
+ rescue
46
+ raise IOError, "Config file not found"
47
+ end
48
+
49
+ ['URL', 'USER', 'PASSWD'].each do |k|
50
+ if not tree.has_key?(k)
51
+ raise IndexError.new("Key %s not found in config file" % k)
52
+ end
53
+ end
54
+
55
+ return tree
56
+ end
57
+
58
+ def self._get_request(jurl, command, args, auth,
59
+ post_args=nil, content_type=nil)
60
+
61
+ url = Helpers._build_url(jurl, command, args)
62
+
63
+ url_str = (url.query == nil) ? url.path : "#{url.path}?#{url.query}"
64
+
65
+ if post_args.nil?:
66
+ req = Net::HTTP::Get.new(url_str)
67
+ else
68
+ req = Net::HTTP::Post.new(url_str)
69
+ req.body = post_args
70
+ end
71
+
72
+ req.basic_auth auth[LOGIN], auth[PASSWD]
73
+
74
+ res = Net::HTTP.start(url.host, url.port) {|http|
75
+
76
+ if content_type
77
+ req["Content-Type"] = content_type
78
+ end
79
+
80
+ ret = http.request(req)
81
+
82
+ if not [Net::HTTPOK, Net::HTTPFound, Net::HTTPNotFound].include? ret.class
83
+ if ret.instance_of? Net::HTTPUnauthorized
84
+ raise ExUnauthorized, "Unauthorized, check your credentials"
85
+ else
86
+ raise ExUndefined, "Http request failed!"
87
+ end
88
+ end
89
+
90
+ ret
91
+ }
92
+
93
+ #Some requests do not return JSON. Such as config.xml
94
+ ct = res.get_fields('Content-type')
95
+
96
+ if ct.nil?
97
+ ct = "text/plain"
98
+ else
99
+ ct = ct[0]
100
+ end
101
+
102
+ ctype = ct.split(';')[0]
103
+
104
+ if ctype == 'application/javascript' or
105
+ ctype == 'application/json'
106
+ #handle JSON
107
+ begin
108
+ return JSON.parse(res.body)
109
+ rescue JSON::ParserError
110
+ return "{}"
111
+ end
112
+ end
113
+
114
+ unless res.body.nil?
115
+ return res.body
116
+ end
117
+
118
+ return "{}"
119
+ end
120
+
121
+ def self._build_url(jurl, command, args)
122
+ return URI.parse("#{jurl}" + (URLs[command] % args))
123
+ end
124
+
125
+ end
126
+
127
+ class Build
128
+ attr_accessor :name,
129
+ :url,
130
+ :revision,
131
+ :branch,
132
+ :revision,
133
+ :status,
134
+ :building,
135
+ :node,
136
+ :duration
137
+ def ppretty()
138
+ print "Name: #{name}\n"
139
+ print "\tBranch: #{branch}\n"
140
+ print "\tRevision: #{revision}\n"
141
+ print "\tStatus: #{status}\n"
142
+ print "\tDuration: #{duration}\n"
143
+ print "\tNode: #{node}\n"
144
+ print "\tBuilding: #{building}\n"
145
+ end
146
+ end
147
+
148
+ class Job
149
+ attr_accessor :info, :name, :jurl, :auth, :is_set
150
+
151
+ def initialize(name, jurl, auth, token=nil)
152
+ @name = name
153
+ @jurl = jurl
154
+ @auth = auth
155
+ @info = false
156
+
157
+ if name
158
+ @e_name = URI.escape(@name)
159
+ @info = Helpers._get_request(@jurl, 'JOBINFO', URI.escape(name), auth)
160
+ end
161
+
162
+ @is_set = @info ? true : false
163
+ end
164
+
165
+ def launch(token)
166
+ res = Helpers._get_request(@jurl, 'LAUNCH',
167
+ [@e_name, token], @auth)
168
+ return true
169
+ end
170
+
171
+ def delete()
172
+ res = Helpers._get_request(@jurl, 'DELETE',
173
+ [@e_name], @auth, post_args="delete=true")
174
+ return true
175
+ end
176
+
177
+ def get_build(buildnum)
178
+ res = Helpers._get_request(@jurl, 'BUILD',
179
+ [@e_name, buildnum], @auth)
180
+
181
+ build = Build.new()
182
+ begin
183
+ build.branch = res['actions'][1]['lastBuiltRevision']['branch'][0]['name']
184
+ build.revision = res['actions'][1]['lastBuiltRevision']['branch'][0]['SHA1']
185
+ rescue
186
+ end
187
+
188
+ build.status = res['result']
189
+ build.name = res['fullDisplayName']
190
+ build.duration = res['duration']
191
+ build.building = res['building']
192
+ build.url = res['url']
193
+ build.node = res['builtOn']
194
+
195
+ return build
196
+ end
197
+
198
+ def info_items()
199
+ return @info.keys
200
+ end
201
+
202
+ def _able_job(action)
203
+ res = Helpers._get_request(@jurl, '_ABLE', [@e_name, action], @auth, post_args={'data' => ""})
204
+ return true
205
+ end
206
+
207
+ def disable()
208
+ return _able_job('disable')
209
+ end
210
+
211
+ def enable()
212
+ return _able_job('enable')
213
+ end
214
+
215
+ def copy(jobname, enable=false)
216
+ config = get_config()
217
+ return create_job(jobname, config, enable)
218
+ end
219
+
220
+ def create_job(jobname, config, enable=false)
221
+ sts = Helpers._get_request(@jurl, 'NEWJOB', [URI.escape(jobname)],
222
+ @auth, post_args=config, content_type="application/xml")
223
+
224
+ newjob = Job.new(jobname, @jurl, @auth)
225
+
226
+ unless enable
227
+ newjob.disable()
228
+ end
229
+
230
+ return newjob
231
+ end
232
+
233
+ def get_config()
234
+ if @is_set
235
+ config_xml = Helpers._get_request(@jurl, 'CONFIG', [URI.escape(@name)], @auth)
236
+ return config_xml
237
+ end
238
+ return ""
239
+ end
240
+
241
+ def is_set?()
242
+ return @is_set
243
+ end
244
+
245
+ def method_missing(name)
246
+
247
+ name.to_s =~ /^get_(.*)$/
248
+ match = $1
249
+
250
+ unless match.nil?
251
+ if @info.has_key? $1
252
+ return @info[$1]
253
+ end
254
+ end
255
+
256
+ return nil
257
+ end
258
+ end
259
+
260
+ class Jenkins
261
+ def initialize(s_uri, auth_info, debug=false)
262
+ @debug = debug
263
+ @jurl = s_uri
264
+ @auth = {LOGIN => auth_info[0], PASSWD => auth_info[1]}
265
+ end
266
+
267
+ def get_job(jobname)
268
+ return Job.new(jobname, @jurl, @auth)
269
+ end
270
+
271
+ def delete_job(jobname)
272
+ j = get_job(jobname)
273
+ res = j.delete()
274
+ return res
275
+ end
276
+
277
+ def create_from_xml(jobname, config_xml)
278
+ newjob = Job.new(jobname, @jurl, @auth)
279
+ newjob = newjob.create_job(jobname, config_xml, enable='true')
280
+ return newjob
281
+ end
282
+
283
+ def all_jobs()
284
+ ob_jobs = []
285
+ jobs = Helpers._get_request(@jurl, 'LIST', [], @auth)
286
+
287
+ jobs['jobs'].each do |j|
288
+ ob_jobs << Job.new(j['name'], @jurl, @auth)
289
+ end
290
+
291
+ return ob_jobs
292
+ end
293
+ end
294
+ end
data/lib/test.rb ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'autojenkins'
4
+
5
+ URL='http://jenkins.ng.com:8080'
6
+ AUTH=["neurogeek", "123qwe4r!!"]
7
+ #TOKEN=""
8
+
9
+ if URL == ''
10
+ puts "To use this functions, please configure URL and AUTH"
11
+ end
12
+ JENK = AutoJenkins::Jenkins.new(URL, AUTH)
13
+
14
+ def ListAll()
15
+ jobs = JENK.all_jobs()
16
+ if jobs
17
+ jobs.each do |jb|
18
+ print "#{jb.name} --> #{jb.get_url}\n"
19
+ print "DisplayName: #{jb.get_displayName}\n"
20
+ jb.info_items.each do |i|
21
+ puts i
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def CreateFromJob(jobname, newjobname)
28
+ job = JENK.get_job(jobname)
29
+ job2 = job.copy(newjobname)
30
+ print "#{job2.name}\n"
31
+ end
32
+
33
+ def GetBuildInfo(jobname, buildnum)
34
+ job = JENK.get_job(jobname)
35
+ bld = job.get_build(buildnum)
36
+ return bld
37
+ end
38
+
39
+ def GetConfig(jobname)
40
+ job = JENK.get_job(jobname)
41
+ return job.get_config()
42
+ end
43
+
44
+ def GetJob(jobname)
45
+ return JENK.get_job(jobname)
46
+ end
47
+
48
+ def GetInfo(jobname)
49
+ def pretty_print(entry, tab=1)
50
+
51
+ if entry.kind_of? Hash
52
+ entry.keys.each do |k|
53
+ print " " * tab * 2
54
+ print "#{k} ----> \n"
55
+ pretty_print(entry[k], tab=tab+1)
56
+ end
57
+ elsif entry == nil
58
+ print " " * tab * 2
59
+ print "[NONE]\n"
60
+ elsif entry.kind_of? Array
61
+ entry.each do |v|
62
+ pretty_print(v, tab+1)
63
+ end
64
+ else
65
+ print "\t" * tab
66
+ print "#{entry}\n"
67
+
68
+ end
69
+ end
70
+ job = JENK.get_job(jobname)
71
+ job.info.keys.each do |k|
72
+ print "#{k} ----> \n"
73
+ pretty_print job.info[k]
74
+ end
75
+ end
76
+
77
+ # Uncomment to test functions
78
+ #ListAll()
79
+ CreateFromJob("TestJOb", "NewJob")
80
+ #puts GetBuildInfo("TestJOb", 1)
81
+ #puts GetConfig("TestJOb")
82
+ #puts GetInfo("TestJOb")
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: autojenkins
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Jesus Rivero
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-06-01 00:00:00 Z
18
+ dependencies: []
19
+
20
+ description: Library to remotely control Jenkins CI. Autojenkins can fetch, create, enable/disable and launch jobs in Jenkins
21
+ email: jesus.riveroa@gmail.com
22
+ executables:
23
+ - mjenk
24
+ extensions: []
25
+
26
+ extra_rdoc_files: []
27
+
28
+ files:
29
+ - lib/autojenkins.rb
30
+ - lib/test.rb
31
+ - bin/mjenk
32
+ homepage: https://github.com/neurogeek/autojenkins-rb
33
+ licenses:
34
+ - MIT
35
+ post_install_message:
36
+ rdoc_options: []
37
+
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ requirements:
59
+ - nokogiri
60
+ - json
61
+ rubyforge_project:
62
+ rubygems_version: 1.8.24
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Library to remotely control Jenkins CI
66
+ test_files: []
67
+