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.
@@ -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 are required for creating a slave."
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
- "credentialsId" => params[:credential_id],
172
- "javaPath" => params[:java_path],
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
@@ -29,7 +29,7 @@ module JenkinsApi
29
29
  # Tiny version of the gem used for patches
30
30
  TINY = 0
31
31
  # Used for pre-releases
32
- PRE = 'alpha.2'
32
+ PRE = 'beta.1'
33
33
  # Version String of Jenkins API Client.
34
34
  VERSION = [MAJOR, MINOR, TINY, PRE].compact.join('.')
35
35
  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
@@ -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 6 seconds so we don't hit the Jenkins quiet period (5
500
+ # Sleep for 10 seconds so we don't hit the Jenkins quiet period (5
493
501
  # seconds)
494
- sleep 6
502
+ sleep 10
495
503
  @client.job.get_current_build_status(@job_name).should == "running"
496
- while @client.job.get_current_build_status(@job_name) == "running" do
497
- # Waiting for this job to finish so it doesn't affect other tests
498
- sleep 10
499
- end
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 6
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(
@@ -142,75 +142,129 @@ describe JenkinsApi::Client do
142
142
  end
143
143
 
144
144
  describe "InstanceMethods" do
145
- describe "#getroot" do
145
+ describe "#get_root" do
146
146
  it "is defined with no parameters" do
147
- expect(
148
- lambda { @client.get_root }
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
- expect(
156
- lambda { @client.api_get_request("/some/prefix", "tree", "/json") }
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
- expect(
164
- lambda { @client.api_post_request("/some/prefix") }
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
- expect(
172
- lambda { @client.get_config("/some/prefix") }
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
- expect(
180
- lambda { @client.post_config("/some/prefix", "<tag></tag>") }
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
- expect(
188
- lambda { @client.get_jenkins_version }
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
- expect(
196
- lambda { @client.get_hudson_version }
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
- expect(
204
- lambda { @client.get_server_date }
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.exec_cli("version") }
213
- ).not_to raise_error(NoMethodError)
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