jenkins_api_client 1.0.0.alpha.2 → 1.0.0.beta.1
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.
- 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
|