jenkins_api_client 0.14.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +5 -23
  4. data/CHANGELOG.md +104 -35
  5. data/CONTRIBUTORS.md +1 -11
  6. data/Gemfile +4 -2
  7. data/README.md +107 -7
  8. data/Rakefile +1 -0
  9. data/Vagrantfile +6 -8
  10. data/jenkins_api_client.gemspec +30 -12
  11. data/lib/jenkins_api_client/cli/helper.rb +1 -0
  12. data/lib/jenkins_api_client/client.rb +162 -69
  13. data/lib/jenkins_api_client/exceptions.rb +26 -17
  14. data/lib/jenkins_api_client/job.rb +321 -75
  15. data/lib/jenkins_api_client/node.rb +22 -10
  16. data/lib/jenkins_api_client/plugin_manager.rb +460 -0
  17. data/lib/jenkins_api_client/urihelper.rb +17 -0
  18. data/lib/jenkins_api_client/user.rb +4 -2
  19. data/lib/jenkins_api_client/version.rb +3 -3
  20. data/lib/jenkins_api_client/view.rb +10 -7
  21. data/lib/jenkins_api_client.rb +1 -0
  22. data/scripts/login_with_pry.rb +54 -0
  23. data/spec/func_tests/client_spec.rb +3 -3
  24. data/spec/func_tests/job_spec.rb +90 -7
  25. data/spec/func_tests/{node_spec.rb → node_spec.rb.pending} +9 -9
  26. data/spec/func_tests/plugin_spec.rb +148 -0
  27. data/spec/unit_tests/client_spec.rb +108 -27
  28. data/spec/unit_tests/fake_http_response.rb +9 -0
  29. data/spec/unit_tests/fixtures/files/available_plugins.json +1 -0
  30. data/spec/unit_tests/fixtures/files/installed_plugins.json +1 -0
  31. data/spec/unit_tests/fixtures/files/updatable_plugins.json +1 -0
  32. data/spec/unit_tests/job_spec.rb +109 -6
  33. data/spec/unit_tests/node_spec.rb +18 -6
  34. data/spec/unit_tests/plugin_spec.rb +165 -0
  35. data/spec/unit_tests/spec_helper.rb +11 -1
  36. data/spec/unit_tests/system_spec.rb +2 -1
  37. data/spec/unit_tests/user_spec.rb +1 -1
  38. data/travis/hudson.model.UpdateCenter.xml +7 -0
  39. data/travis/setup.sh +2 -1
  40. metadata +76 -64
@@ -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
@@ -28,6 +28,7 @@ require 'jenkins_api_client/node'
28
28
  require 'jenkins_api_client/system'
29
29
  require 'jenkins_api_client/view'
30
30
  require 'jenkins_api_client/build_queue'
31
+ require 'jenkins_api_client/plugin_manager'
31
32
  require 'jenkins_api_client/user'
32
33
 
33
34
  require 'jenkins_api_client/cli/helper'
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby1.9.1
2
+ # This script provides an easier way to login to Jenkins server API.
3
+ # It logs you in with the credentials and server details you proided and then
4
+ # starts an IRB session so you can interactively play with the API.
5
+
6
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
7
+ require 'jenkins_api_client'
8
+ require 'yaml'
9
+ require 'pry'
10
+
11
+ def prompt_for_username
12
+ get_from_stdin("Username: ", false)
13
+ end
14
+
15
+ def prompt_for_password
16
+ get_from_stdin("Password: ", true)
17
+ end
18
+
19
+ def get_from_stdin(prompt, mask = false)
20
+ $stdout.write(prompt)
21
+
22
+ begin
23
+ Kernel::system "stty -echo" if mask == true
24
+ ret = gets.chomp!
25
+ ensure
26
+ if mask == true
27
+ Kernel::system "stty echo"
28
+ puts ""
29
+ end
30
+ end
31
+
32
+ ret
33
+ end
34
+
35
+ if ARGV.empty?
36
+ config_file = '~/.jenkins_api_client/spec.yml'
37
+ else
38
+ config_file = ARGV.shift
39
+ end
40
+
41
+ begin
42
+ client_opts = YAML.load_file(File.expand_path(config_file))
43
+ unless client_opts.has_key?(:username)
44
+ client_opts[:username] = prompt_for_username()
45
+ end
46
+ unless client_opts.has_key?(:password) or client_opts.has_key?(:password_base64)
47
+ client_opts[:password] = prompt_for_password()
48
+ end
49
+
50
+ @client = JenkinsApi::Client.new(client_opts)
51
+ puts "logged-in to the Jenkins API, use the '@client' variable to use the client"
52
+ end
53
+
54
+ Pry.start
@@ -66,9 +66,9 @@ describe JenkinsApi::Client do
66
66
  end
67
67
  end
68
68
 
69
- describe "#get_server_date" do
70
- it "Should return the server date" do
71
- @client.get_server_date.class.should == String
69
+ describe "#exec_script" do
70
+ it "Should execute the provided groovy script" do
71
+ @client.exec_script('println("hi")').should == "hi\n"
72
72
  end
73
73
  end
74
74
  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(
@@ -21,7 +21,7 @@ describe JenkinsApi::Client::Node do
21
21
  puts e.message
22
22
  end
23
23
 
24
- @client.node.create_dump_slave(
24
+ @client.node.create_dumb_slave(
25
25
  :name => "slave",
26
26
  :slave_host => "10.0.0.1",
27
27
  :private_key_file => "/root/.ssh/id_rsa"
@@ -43,12 +43,12 @@ describe JenkinsApi::Client::Node do
43
43
  end
44
44
  end
45
45
 
46
- describe "#create_dump_slave" do
46
+ describe "#create_dumb_slave" do
47
47
 
48
48
  def test_and_validate(params)
49
49
  name = params[:name]
50
50
  @valid_post_responses.should include(
51
- @client.node.create_dump_slave(params).to_i
51
+ @client.node.create_dumb_slave(params).to_i
52
52
  )
53
53
  @client.node.list(name).include?(name).should be_true
54
54
  @valid_post_responses.should include(
@@ -79,7 +79,7 @@ describe JenkinsApi::Client::Node do
79
79
  :private_key_file => "/root/.ssh/id_rsa"
80
80
  }
81
81
  expect(
82
- lambda{ @client.node.create_dump_slave(params) }
82
+ lambda{ @client.node.create_dumb_slave(params) }
83
83
  ).to raise_error(ArgumentError)
84
84
  end
85
85
  it "fails if slave_host is missing" do
@@ -88,7 +88,7 @@ describe JenkinsApi::Client::Node do
88
88
  :private_key_file => "/root/.ssh/id_rsa"
89
89
  }
90
90
  expect(
91
- lambda{ @client.node.create_dump_slave(params) }
91
+ lambda{ @client.node.create_dumb_slave(params) }
92
92
  ).to raise_error(ArgumentError)
93
93
  end
94
94
  it "fails if private_key_file is missing" do
@@ -97,7 +97,7 @@ describe JenkinsApi::Client::Node do
97
97
  :slave_host => "10.10.10.10"
98
98
  }
99
99
  expect(
100
- lambda{ @client.node.create_dump_slave(params) }
100
+ lambda{ @client.node.create_dumb_slave(params) }
101
101
  ).to raise_error(ArgumentError)
102
102
  end
103
103
  it "fails if the slave already exists in Jenkins" do
@@ -107,10 +107,10 @@ describe JenkinsApi::Client::Node do
107
107
  :private_key_file => "/root/.ssh/id_rsa"
108
108
  }
109
109
  @valid_post_responses.should include(
110
- @client.node.create_dump_slave(params).to_i
110
+ @client.node.create_dumb_slave(params).to_i
111
111
  )
112
112
  expect(
113
- lambda{ @client.node.create_dump_slave(params) }
113
+ lambda{ @client.node.create_dumb_slave(params) }
114
114
  ).to raise_error(JenkinsApi::Exceptions::NodeAlreadyExists)
115
115
  @valid_post_responses.should include(
116
116
  @client.node.delete(params[:name]).to_i
@@ -126,7 +126,7 @@ describe JenkinsApi::Client::Node do
126
126
  :private_key_file => "/root/.ssh/id_rsa"
127
127
  }
128
128
  @valid_post_responses.should include(
129
- @client.node.create_dump_slave(params).to_i
129
+ @client.node.create_dumb_slave(params).to_i
130
130
  )
131
131
  @valid_post_responses.should include(
132
132
  @client.node.delete(params[:name]).to_i
@@ -0,0 +1,148 @@
1
+ #
2
+ # Specifying JenkinsApi::Client::PluginManager class capabilities
3
+ # Author Kannan Manickam <arangamani.kannan@gmail.com>
4
+ #
5
+
6
+ require File.expand_path('../spec_helper', __FILE__)
7
+ require 'yaml'
8
+
9
+ describe JenkinsApi::Client::PluginManager do
10
+ context "With properly initialized client" do
11
+ before(:all) do
12
+ @creds_file = '~/.jenkins_api_client/spec.yml'
13
+ @valid_post_responses = [200, 201, 302]
14
+ @test_plugin = "scripttrigger"
15
+ @test_plugins = ["text-finder", "terminal", "warnings"]
16
+ begin
17
+ @client = JenkinsApi::Client.new(
18
+ YAML.load_file(File.expand_path(@creds_file, __FILE__))
19
+ )
20
+ @client.init_update_center
21
+ sleep 30
22
+ rescue Exception => e
23
+ puts "WARNING: Credentials are not set properly."
24
+ puts e.message
25
+ end
26
+ end
27
+
28
+ describe "InstanceMethods" do
29
+ describe "#list_installed" do
30
+ it "lists all installed plugins in jenkins" do
31
+ @client.plugin.list_installed.class.should == Hash
32
+ end
33
+ supported_filters = [
34
+ :active, :bundled, :deleted, :downgradable, :enabled,
35
+ :hasUpdate, :pinned
36
+ ]
37
+ supported_filters.each do |filter|
38
+ it "lists all installed plugins matching filter '#{filter}'" do
39
+ @client.plugin.list_installed(filter => true).class.should == Hash
40
+ end
41
+ end
42
+ it "raises an error if unsupported filter is specified" do
43
+ expect(
44
+ lambda { @client.plugin.list_installed(:unsupported => true) }
45
+ ).to raise_error(ArgumentError)
46
+ end
47
+ end
48
+
49
+ describe "#list_available" do
50
+ it "lists all available plugins in jenkins update center" do
51
+ @client.plugin.list_available.class.should == Hash
52
+ end
53
+ end
54
+
55
+ describe "#list_updates" do
56
+ it "lists all available plugin updates in jenkins update center" do
57
+ @client.plugin.list_updates.class.should == Hash
58
+ end
59
+ end
60
+
61
+ describe "#install, #restart_required?" do
62
+ it "installs a single plugin given as a string" do
63
+ @client.plugin.install(@test_plugin)
64
+ # Plugin installation might take a bit
65
+ sleep 5
66
+ @client.system.restart(true) if @client.plugin.restart_required?
67
+ @client.system.wait_for_ready
68
+ @client.plugin.list_installed.keys.should include(@test_plugin)
69
+ end
70
+ it "installs multiple plugins given as an array" do
71
+ @client.plugin.install(@test_plugins)
72
+ # Plugin installation might take a bit
73
+ sleep 15
74
+ @client.system.restart(true) if @client.plugin.restart_required?
75
+ @client.system.wait_for_ready
76
+ installed = @client.plugin.list_installed.keys
77
+ @test_plugins.all? { |plugin| installed.include?(plugin) }.
78
+ should == true
79
+ end
80
+ end
81
+
82
+ describe "#disable, #restart_required?" do
83
+ it "disables a single plugin given as a string" do
84
+ @client.plugin.disable(@test_plugin)
85
+ # Plugin installation might take a bit
86
+ sleep 5
87
+ @client.system.restart(true)
88
+ @client.system.wait_for_ready
89
+ @client.plugin.list_installed(:active => false).keys.
90
+ should include(@test_plugin)
91
+ end
92
+ it "disables multiple plugins given as an array" do
93
+ @client.plugin.disable(@test_plugins)
94
+ # Plugin installation might take a bit
95
+ sleep 5
96
+ @client.system.restart(true)
97
+ @client.system.wait_for_ready
98
+ installed = @client.plugin.list_installed(:active => false).keys
99
+ @test_plugins.all? { |plugin| installed.include?(plugin) }.
100
+ should == true
101
+ end
102
+ end
103
+
104
+ describe "#enable, #restart_required?" do
105
+ it "enables a single plugin given as a string" do
106
+ @client.plugin.enable(@test_plugin)
107
+ # Plugin installation might take a bit
108
+ sleep 5
109
+ @client.system.restart(true)
110
+ @client.system.wait_for_ready
111
+ @client.plugin.list_installed(:active => true).keys.
112
+ should include(@test_plugin)
113
+ end
114
+ it "enables multiple plugins given as an array" do
115
+ @client.plugin.enable(@test_plugins)
116
+ # Plugin installation might take a bit
117
+ sleep 5
118
+ @client.system.restart(true)
119
+ @client.system.wait_for_ready
120
+ installed = @client.plugin.list_installed(:active => true).keys
121
+ @test_plugins.all? { |plugin| installed.include?(plugin) }.
122
+ should == true
123
+ end
124
+ end
125
+
126
+ describe "#uninstall, #restart_required?" do
127
+ it "uninstalls a single plugin given as a string" do
128
+ @client.plugin.uninstall(@test_plugin)
129
+ # Plugin uninstallation might take a bit
130
+ sleep 5
131
+ @client.system.restart(true) if @client.plugin.restart_required?
132
+ @client.system.wait_for_ready
133
+ @client.plugin.list_installed.keys.should_not include(@test_plugin)
134
+ end
135
+ it "uninstalls multiple plugins given as an array" do
136
+ @client.plugin.uninstall(@test_plugins)
137
+ # Plugin uninstallation might take a bit
138
+ sleep 5
139
+ @client.system.restart(true) if @client.plugin.restart_required?
140
+ @client.system.wait_for_ready
141
+ installed = @client.plugin.list_installed.keys
142
+ @test_plugins.all? { |plugin| installed.include?(plugin) }.
143
+ should == false
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end