jenkins_api_client 1.0.0.alpha.2 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG.md +56 -35
- data/CONTRIBUTORS.md +1 -13
- data/README.md +88 -2
- data/jenkins_api_client.gemspec +5 -3
- data/lib/jenkins_api_client/client.rb +56 -12
- data/lib/jenkins_api_client/job.rb +272 -54
- data/lib/jenkins_api_client/node.rb +6 -45
- data/lib/jenkins_api_client/urihelper.rb +17 -0
- data/lib/jenkins_api_client/user.rb +3 -1
- data/lib/jenkins_api_client/version.rb +1 -1
- data/lib/jenkins_api_client/view.rb +10 -7
- data/spec/func_tests/job_spec.rb +90 -7
- data/spec/func_tests/{node_spec.rb → node_spec.rb.pending} +0 -0
- data/spec/unit_tests/client_spec.rb +81 -27
- data/spec/unit_tests/fake_http_response.rb +9 -0
- data/spec/unit_tests/job_spec.rb +109 -6
- data/spec/unit_tests/system_spec.rb +2 -1
- metadata +5 -3
@@ -124,8 +124,9 @@ module JenkinsApi
|
|
124
124
|
# )
|
125
125
|
#
|
126
126
|
def create_dump_slave(params)
|
127
|
-
unless params[:name] && params[:slave_host]
|
128
|
-
raise ArgumentError, "Name, slave host
|
127
|
+
unless params[:name] && params[:slave_host] && params[:private_key_file]
|
128
|
+
raise ArgumentError, "Name, slave host, and private key file are" +
|
129
|
+
" required for creating a slave."
|
129
130
|
end
|
130
131
|
|
131
132
|
@logger.info "Creating a dump slave '#{params[:name]}'"
|
@@ -136,11 +137,7 @@ module JenkinsApi
|
|
136
137
|
:remote_fs => "/var/jenkins",
|
137
138
|
:labels => params[:name],
|
138
139
|
:slave_port => 22,
|
139
|
-
:mode => "normal"
|
140
|
-
:credential_id => "",
|
141
|
-
:java_path => "",
|
142
|
-
:prefix_start_slave_cmd => "",
|
143
|
-
:suffix_start_slave_cmd => ""
|
140
|
+
:mode => "normal"
|
144
141
|
}
|
145
142
|
|
146
143
|
params = default_params.merge(params)
|
@@ -168,13 +165,8 @@ module JenkinsApi
|
|
168
165
|
"stapler-class" => "hudson.plugins.sshslaves.SSHLauncher",
|
169
166
|
"host" => params[:slave_host],
|
170
167
|
"port" => params[:slave_port],
|
171
|
-
"
|
172
|
-
"
|
173
|
-
"jvmOptions" => params[:jvm_options],
|
174
|
-
"prefixStartSlaveCmd" => params[:prefix_start_slave_cmd],
|
175
|
-
"suffixStartSlaveCmd" => params[:suffix_start_slave_cmd]
|
176
|
-
#"username" => params[:slave_user],
|
177
|
-
#"privatekey" => params[:private_key_file],
|
168
|
+
"username" => params[:slave_user],
|
169
|
+
"privatekey" => params[:private_key_file],
|
178
170
|
}
|
179
171
|
}.to_json
|
180
172
|
}
|
@@ -302,37 +294,6 @@ module JenkinsApi
|
|
302
294
|
@client.post_config("/computer/#{node_name}/config.xml", xml)
|
303
295
|
end
|
304
296
|
|
305
|
-
|
306
|
-
def create_cred
|
307
|
-
post_params = {
|
308
|
-
"name" => "testcred",
|
309
|
-
"description" => "test cred description",
|
310
|
-
"json" => {
|
311
|
-
"domainCredentials" => {
|
312
|
-
"domain" => {
|
313
|
-
"name" => "",
|
314
|
-
"description" => "",
|
315
|
-
},
|
316
|
-
"credentials" => {
|
317
|
-
"scope" => "GLOBAL",
|
318
|
-
"id" => "",
|
319
|
-
"username" => "root",
|
320
|
-
"description" => "root users credential",
|
321
|
-
"privateKeySource" => {
|
322
|
-
"value" => "0",
|
323
|
-
"privateKey" => "blah blah blah",
|
324
|
-
"stapler-class" => "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource"
|
325
|
-
},
|
326
|
-
"passphrase" => "",
|
327
|
-
"stapler-class" => "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey",
|
328
|
-
"kind" => "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
|
329
|
-
}
|
330
|
-
}
|
331
|
-
}.to_json
|
332
|
-
}
|
333
|
-
@client.api_post_request("/credentials/configSubmit", post_params)
|
334
|
-
end
|
335
|
-
|
336
297
|
end
|
337
298
|
end
|
338
299
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module JenkinsApi
|
4
|
+
module UriHelper
|
5
|
+
# Encode a string for using in the query part of an URL
|
6
|
+
#
|
7
|
+
def form_encode(string)
|
8
|
+
URI.encode_www_form_component string.encode(Encoding::UTF_8)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Encode a string for use in the hiearchical part of an URL
|
12
|
+
#
|
13
|
+
def path_encode(path)
|
14
|
+
URI.escape(path.encode(Encoding::UTF_8))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -21,6 +21,7 @@
|
|
21
21
|
#
|
22
22
|
|
23
23
|
require 'timeout'
|
24
|
+
require 'jenkins_api_client/urihelper'
|
24
25
|
|
25
26
|
module JenkinsApi
|
26
27
|
class Client
|
@@ -31,6 +32,7 @@ module JenkinsApi
|
|
31
32
|
# @since 0.14.0
|
32
33
|
#
|
33
34
|
class User
|
35
|
+
include JenkinsApi::UriHelper
|
34
36
|
|
35
37
|
# Initializes a new User object.
|
36
38
|
#
|
@@ -111,7 +113,7 @@ module JenkinsApi
|
|
111
113
|
# }
|
112
114
|
#
|
113
115
|
def get(user_id)
|
114
|
-
response = @client.api_get_request("/user/#{user_id}")
|
116
|
+
response = @client.api_get_request("/user/#{path_encode user_id}")
|
115
117
|
end
|
116
118
|
|
117
119
|
end
|
@@ -20,6 +20,8 @@
|
|
20
20
|
# THE SOFTWARE.
|
21
21
|
#
|
22
22
|
|
23
|
+
require 'jenkins_api_client/urihelper'
|
24
|
+
|
23
25
|
module JenkinsApi
|
24
26
|
class Client
|
25
27
|
# This class communicates with Jenkins "/view" API and used to create,
|
@@ -27,6 +29,7 @@ module JenkinsApi
|
|
27
29
|
# API.
|
28
30
|
#
|
29
31
|
class View
|
32
|
+
include JenkinsApi::UriHelper
|
30
33
|
|
31
34
|
# Initializes a new view object
|
32
35
|
#
|
@@ -150,7 +153,7 @@ module JenkinsApi
|
|
150
153
|
post_params.merge!("filterExecutors" => "on") if params[:filter_executors]
|
151
154
|
post_params.merge!("useincluderegex" => "on",
|
152
155
|
"includeRegex" => params[:regex]) if params[:regex]
|
153
|
-
@client.api_post_request("/view/#{params[:name]}/configSubmit",
|
156
|
+
@client.api_post_request("/view/#{path_encode params[:name]}/configSubmit",
|
154
157
|
post_params)
|
155
158
|
end
|
156
159
|
|
@@ -160,7 +163,7 @@ module JenkinsApi
|
|
160
163
|
#
|
161
164
|
def delete(view_name)
|
162
165
|
@logger.info "Deleting view '#{view_name}'"
|
163
|
-
@client.api_post_request("/view/#{view_name}/doDelete")
|
166
|
+
@client.api_post_request("/view/#{path_encode view_name}/doDelete")
|
164
167
|
end
|
165
168
|
|
166
169
|
# Deletes all views (except the All view) in Jenkins.
|
@@ -211,7 +214,7 @@ module JenkinsApi
|
|
211
214
|
job_names = []
|
212
215
|
raise "The view #{view_name} doesn't exists on the server"\
|
213
216
|
unless exists?(view_name)
|
214
|
-
response_json = @client.api_get_request("/view/#{view_name}")
|
217
|
+
response_json = @client.api_get_request("/view/#{path_encode view_name}")
|
215
218
|
response_json["jobs"].each do |job|
|
216
219
|
job_names << job["name"]
|
217
220
|
end
|
@@ -225,7 +228,7 @@ module JenkinsApi
|
|
225
228
|
#
|
226
229
|
def add_job(view_name, job_name)
|
227
230
|
@logger.info "Adding job '#{job_name}' to view '#{view_name}'"
|
228
|
-
post_msg = "/view/#{view_name}/addJobToView?name=#{job_name}"
|
231
|
+
post_msg = "/view/#{path_encode view_name}/addJobToView?name=#{form_encode job_name}"
|
229
232
|
@client.api_post_request(post_msg)
|
230
233
|
end
|
231
234
|
|
@@ -236,7 +239,7 @@ module JenkinsApi
|
|
236
239
|
#
|
237
240
|
def remove_job(view_name, job_name)
|
238
241
|
@logger.info "Removing job '#{job_name}' from view '#{view_name}'"
|
239
|
-
post_msg = "/view/#{view_name}/removeJobFromView?name=#{job_name}"
|
242
|
+
post_msg = "/view/#{path_encode view_name}/removeJobFromView?name=#{form_encode job_name}"
|
240
243
|
@client.api_post_request(post_msg)
|
241
244
|
end
|
242
245
|
|
@@ -246,7 +249,7 @@ module JenkinsApi
|
|
246
249
|
#
|
247
250
|
def get_config(view_name)
|
248
251
|
@logger.info "Obtaining the configuration of view '#{view_name}'"
|
249
|
-
@client.get_config("/view/#{view_name}")
|
252
|
+
@client.get_config("/view/#{path_encode view_name}")
|
250
253
|
end
|
251
254
|
|
252
255
|
# Post the configuration of a view given the view name and the config.xml
|
@@ -256,7 +259,7 @@ module JenkinsApi
|
|
256
259
|
#
|
257
260
|
def post_config(view_name, xml)
|
258
261
|
@logger.info "Posting the configuration of view '#{view_name}'"
|
259
|
-
@client.post_config("/view/#{view_name}/config.xml", xml)
|
262
|
+
@client.post_config("/view/#{path_encode view_name}/config.xml", xml)
|
260
263
|
end
|
261
264
|
|
262
265
|
end
|
data/spec/func_tests/job_spec.rb
CHANGED
@@ -481,6 +481,14 @@ describe JenkinsApi::Client::Job do
|
|
481
481
|
end
|
482
482
|
|
483
483
|
describe "#build" do
|
484
|
+
|
485
|
+
def wait_for_job_to_finish(job_name)
|
486
|
+
while @client.job.get_current_build_status(@job_name) == "running" do
|
487
|
+
# Waiting for this job to finish so it doesn't affect other tests
|
488
|
+
sleep 10
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
484
492
|
it "Should build the specified job" do
|
485
493
|
@client.job.get_current_build_status(
|
486
494
|
@job_name
|
@@ -489,14 +497,89 @@ describe JenkinsApi::Client::Job do
|
|
489
497
|
# As of Jenkins version 1.519 the job build responds with a 201
|
490
498
|
# status code.
|
491
499
|
@valid_post_responses.should include(response.to_i)
|
492
|
-
# Sleep for
|
500
|
+
# Sleep for 10 seconds so we don't hit the Jenkins quiet period (5
|
493
501
|
# seconds)
|
494
|
-
sleep
|
502
|
+
sleep 10
|
495
503
|
@client.job.get_current_build_status(@job_name).should == "running"
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
504
|
+
wait_for_job_to_finish(@job_name)
|
505
|
+
end
|
506
|
+
|
507
|
+
it "Should build the specified job (wait for start)" do
|
508
|
+
@client.job.get_current_build_status(
|
509
|
+
@job_name
|
510
|
+
).should_not == "running"
|
511
|
+
expected_build_id = (@client.job.get_current_build_number(@job_name) || 0) + 1
|
512
|
+
|
513
|
+
build_opts = {
|
514
|
+
'build_start_timeout' => 10,
|
515
|
+
'progress_proc' => lambda do |max_wait, curr_wait, poll_count|
|
516
|
+
puts "Waited #{curr_wait}s of #{max_wait}s max - poll count = #{poll_count}"
|
517
|
+
end,
|
518
|
+
'completion_proc' => lambda do |build_number, cancelled|
|
519
|
+
if build_number
|
520
|
+
puts "Wait over: build #{build_number} started"
|
521
|
+
else
|
522
|
+
puts "Wait over: build not started, build #{cancelled ? "" : "NOT "} cancelled"
|
523
|
+
end
|
524
|
+
end
|
525
|
+
}
|
526
|
+
build_id = @client.job.build(@job_name, {}, build_opts)
|
527
|
+
build_id.should_not be_nil
|
528
|
+
build_id.should eql(expected_build_id)
|
529
|
+
@client.job.get_current_build_status(@job_name).should == "running"
|
530
|
+
wait_for_job_to_finish(@job_name)
|
531
|
+
end
|
532
|
+
|
533
|
+
# This build doesn't start in time, but we don't cancel it, so it will run if
|
534
|
+
# Jenkins gets to it
|
535
|
+
it "Should build the specified job (wait for start - but not long enough)" do
|
536
|
+
@client.job.get_current_build_status(
|
537
|
+
@job_name
|
538
|
+
).should_not == "running"
|
539
|
+
|
540
|
+
build_opts = {
|
541
|
+
'build_start_timeout' => 1,
|
542
|
+
'progress_proc' => lambda do |max_wait, curr_wait, poll_count|
|
543
|
+
puts "Waited #{curr_wait}s of #{max_wait}s max - poll count = #{poll_count}"
|
544
|
+
end,
|
545
|
+
'completion_proc' => lambda do |build_number, cancelled|
|
546
|
+
if build_number
|
547
|
+
puts "Wait over: build #{build_number} started"
|
548
|
+
else
|
549
|
+
puts "Wait over: build not started, build #{cancelled ? "" : "NOT "}cancelled"
|
550
|
+
end
|
551
|
+
end
|
552
|
+
}
|
553
|
+
expect( lambda { @client.job.build(@job_name, {}, build_opts) } ).to raise_error(Timeout::Error)
|
554
|
+
# Sleep for 10 seconds so we don't hit the Jenkins quiet period (5
|
555
|
+
# seconds)
|
556
|
+
sleep 10
|
557
|
+
@client.job.get_current_build_status(@job_name).should == "running"
|
558
|
+
wait_for_job_to_finish(@job_name)
|
559
|
+
end
|
560
|
+
|
561
|
+
# This build doesn't start in time, and we will attempt to cancel it so it
|
562
|
+
# doesn't run
|
563
|
+
it "Should build the specified job (wait for start - but not long enough, cancelled)" do
|
564
|
+
@client.job.get_current_build_status(
|
565
|
+
@job_name
|
566
|
+
).should_not == "running"
|
567
|
+
|
568
|
+
build_opts = {
|
569
|
+
'build_start_timeout' => 1,
|
570
|
+
'cancel_on_build_start_timeout' => true,
|
571
|
+
'progress_proc' => lambda do |max_wait, curr_wait, poll_count|
|
572
|
+
puts "Waited #{curr_wait}s of #{max_wait}s max - poll count = #{poll_count}"
|
573
|
+
end,
|
574
|
+
'completion_proc' => lambda do |build_number, cancelled|
|
575
|
+
if build_number
|
576
|
+
puts "Wait over: build #{build_number} started"
|
577
|
+
else
|
578
|
+
puts "Wait over: build not started, build #{cancelled ? "" : "NOT "}cancelled"
|
579
|
+
end
|
580
|
+
end
|
581
|
+
}
|
582
|
+
expect( lambda { @client.job.build(@job_name, {}, build_opts) } ).to raise_error(Timeout::Error)
|
500
583
|
end
|
501
584
|
end
|
502
585
|
|
@@ -527,7 +610,7 @@ describe JenkinsApi::Client::Job do
|
|
527
610
|
@job_name
|
528
611
|
).should_not == "running"
|
529
612
|
@client.job.build(@job_name)
|
530
|
-
sleep
|
613
|
+
sleep 10
|
531
614
|
@client.job.get_current_build_status(@job_name).should == "running"
|
532
615
|
sleep 5
|
533
616
|
@valid_post_responses.should include(
|
File without changes
|
@@ -142,75 +142,129 @@ describe JenkinsApi::Client do
|
|
142
142
|
end
|
143
143
|
|
144
144
|
describe "InstanceMethods" do
|
145
|
-
describe "#
|
145
|
+
describe "#get_root" do
|
146
146
|
it "is defined with no parameters" do
|
147
|
-
|
148
|
-
|
149
|
-
).not_to raise_error(NoMethodError)
|
147
|
+
@client.respond_to?(:get_root).should be_true
|
148
|
+
@client.method(:get_root).parameters.size.should == 0
|
150
149
|
end
|
151
150
|
end
|
152
151
|
|
153
152
|
describe "#api_get_request" do
|
154
153
|
it "defined and should accept url_prefix, tree, and url_suffix" do
|
155
|
-
|
156
|
-
|
157
|
-
).not_to raise_error(NoMethodError)
|
154
|
+
@client.respond_to?(:api_get_request).should be_true
|
155
|
+
@client.method(:api_get_request).parameters.size.should == 4
|
158
156
|
end
|
159
157
|
end
|
160
158
|
|
161
159
|
describe "#api_post_request" do
|
162
160
|
it "is defined and should accept url_prefix" do
|
163
|
-
|
164
|
-
|
165
|
-
).not_to raise_error(NoMethodError)
|
161
|
+
@client.respond_to?(:api_post_request).should be_true
|
162
|
+
@client.method(:api_post_request).parameters.size.should == 3
|
166
163
|
end
|
167
164
|
end
|
168
165
|
|
169
166
|
describe "#get_config" do
|
170
167
|
it "is defined and should accept url_prefix" do
|
171
|
-
|
172
|
-
|
173
|
-
).not_to raise_error(NoMethodError)
|
168
|
+
@client.respond_to?(:get_config).should be_true
|
169
|
+
@client.method(:get_config).parameters.size.should == 1
|
174
170
|
end
|
175
171
|
end
|
176
172
|
|
177
173
|
describe "#post_config" do
|
178
174
|
it "is defined and should accept url_prefix and xml" do
|
179
|
-
|
180
|
-
|
181
|
-
).not_to raise_error(NoMethodError)
|
175
|
+
@client.respond_to?(:post_config).should be_true
|
176
|
+
@client.method(:post_config).parameters.size.should == 2
|
182
177
|
end
|
183
178
|
end
|
184
179
|
|
185
180
|
describe "#get_jenkins_version" do
|
186
181
|
it "is defined and accepts no parameters" do
|
187
|
-
|
188
|
-
|
189
|
-
).not_to raise_error(NoMethodError)
|
182
|
+
@client.respond_to?(:get_jenkins_version).should be_true
|
183
|
+
@client.method(:get_jenkins_version).parameters.size.should == 0
|
190
184
|
end
|
191
185
|
end
|
192
186
|
|
193
187
|
describe "#get_hudson_version" do
|
194
188
|
it "is defined and accepts no parameters" do
|
195
|
-
|
196
|
-
|
197
|
-
).not_to raise_error(NoMethodError)
|
189
|
+
@client.respond_to?(:get_hudson_version).should be_true
|
190
|
+
@client.method(:get_hudson_version).parameters.size.should == 0
|
198
191
|
end
|
199
192
|
end
|
200
193
|
|
201
194
|
describe "#get_server_date" do
|
202
195
|
it "is defined and accepts no parameters" do
|
203
|
-
|
204
|
-
|
205
|
-
).not_to raise_error(NoMethodError)
|
196
|
+
@client.respond_to?(:get_server_date).should be_true
|
197
|
+
@client.method(:get_server_date).parameters.size.should == 0
|
206
198
|
end
|
207
199
|
end
|
208
200
|
|
209
201
|
describe "#exec_cli" do
|
210
202
|
it "is defined and should execute the CLI" do
|
203
|
+
@client.respond_to?(:exec_cli).should be_true
|
204
|
+
@client.method(:exec_cli).parameters.size.should == 2
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "#deconstruct_version_string" do
|
209
|
+
it "is defined and accepts a single param" do
|
210
|
+
@client.respond_to?(:deconstruct_version_string).should be_true
|
211
|
+
@client.method(:deconstruct_version_string).parameters.size.should == 1
|
212
|
+
end
|
213
|
+
|
214
|
+
it "takes a version string in the form 'a.b' and returns an array [a,b,c]" do
|
215
|
+
TEST_2_PART_VERSION_STRING = "1.002"
|
216
|
+
version = @client.deconstruct_version_string(TEST_2_PART_VERSION_STRING)
|
217
|
+
version.should_not be_nil
|
218
|
+
version.should_not be_empty
|
219
|
+
version.size.should eql 3
|
220
|
+
version[0].should eql 1
|
221
|
+
version[1].should eql 2
|
222
|
+
version[2].should eql 0
|
223
|
+
end
|
224
|
+
|
225
|
+
it "takes a version string in the form 'a.b.c' and returns an array [a,b]" do
|
226
|
+
TEST_3_PART_VERSION_STRING = "1.002.3"
|
227
|
+
version = @client.deconstruct_version_string(TEST_3_PART_VERSION_STRING)
|
228
|
+
version.should_not be_nil
|
229
|
+
version.should_not be_empty
|
230
|
+
version.size.should eql 3
|
231
|
+
version[0].should eql 1
|
232
|
+
version[1].should eql 2
|
233
|
+
version[2].should eql 3
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should fail if parameter is not a string" do
|
211
237
|
expect(
|
212
|
-
lambda { @client.
|
213
|
-
).
|
238
|
+
lambda { @client.deconstruct_version_string(1) }
|
239
|
+
).to raise_error(NoMethodError) # match for fixnum
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should return nil if parameter is not a string in the form '\d+.\d+(.\d+)'" do
|
243
|
+
@client.deconstruct_version_string("A.B").should be_nil
|
244
|
+
@client.deconstruct_version_string("1").should be_nil
|
245
|
+
@client.deconstruct_version_string("1.").should be_nil
|
246
|
+
@client.deconstruct_version_string("1.2.3.4").should be_nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "#compare_versions" do
|
251
|
+
it "is defined and accepts two params" do
|
252
|
+
@client.respond_to?(:compare_versions).should be_true
|
253
|
+
@client.method(:compare_versions).parameters.size.should == 2
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should correctly compare version numbers" do
|
257
|
+
@client.compare_versions("1.0", "1.0").should eql(0)
|
258
|
+
@client.compare_versions("1.0", "1.1").should eql(-1)
|
259
|
+
@client.compare_versions("1.1", "1.0").should eql(1)
|
260
|
+
@client.compare_versions("2.0", "1.99").should eql(1)
|
261
|
+
@client.compare_versions("1.10", "1.2").should eql(1)
|
262
|
+
|
263
|
+
@client.compare_versions("1.0.0", "1.0.0").should eql(0)
|
264
|
+
@client.compare_versions("1.0", "1.0.1").should eql(-1)
|
265
|
+
@client.compare_versions("1.1", "1.0.1").should eql(1)
|
266
|
+
@client.compare_versions("2.0.0", "1.999.99").should eql(1)
|
267
|
+
@client.compare_versions("1.0.10", "1.0.2").should eql(1)
|
214
268
|
end
|
215
269
|
end
|
216
270
|
end
|