jenkins_api_client 0.14.1 → 1.0.0

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.
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