jenkins_api_client 0.14.1 → 1.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -30,4 +30,4 @@ notifications:
30
30
  on_success: change
31
31
  on_failure: always
32
32
  channels:
33
- - "irc.freenode.org##jenins-api-client"
33
+ - "irc.freenode.org##jenkins-api-client"
@@ -11,3 +11,5 @@ Thank you very much to all who contributed to this project.
11
11
  * [rubytester](https://github.com/rubytester)
12
12
  * [dougforpres](https://github.com/dougforpres)
13
13
  * [brettporter](https://github.com/brettporter)
14
+ * [riywo](https://github.com/riywo)
15
+ * [bobbrez](https://github.com/bobbrez)
data/Gemfile CHANGED
@@ -11,5 +11,6 @@ group :development do
11
11
  gem "jeweler", ">= 1.6.4"
12
12
  gem "rspec", "~> 2.13.0"
13
13
  gem "simplecov"
14
+ gem "yard-thor"
14
15
  gem "yard"
15
16
  end
data/README.md CHANGED
@@ -102,7 +102,7 @@ puts @client.job.list_all
102
102
  ### Chaining and Building Jobs
103
103
 
104
104
  Sometimes we want certain jobs to be added as downstream projects and run them
105
- sequencially. The following example will explain how this could be done.
105
+ sequentially. The following example will explain how this could be done.
106
106
 
107
107
  ```ruby
108
108
  require 'jenkins_api_client'
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
40
40
  "lib/jenkins_api_client/exceptions.rb",
41
41
  "lib/jenkins_api_client/job.rb",
42
42
  "lib/jenkins_api_client/node.rb",
43
+ "lib/jenkins_api_client/plugin_manager.rb",
43
44
  "lib/jenkins_api_client/system.rb",
44
45
  "lib/jenkins_api_client/user.rb",
45
46
  "lib/jenkins_api_client/version.rb",
@@ -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'
@@ -212,6 +212,15 @@ module JenkinsApi
212
212
  JenkinsApi::Client::BuildQueue.new(self)
213
213
  end
214
214
 
215
+ # Creates an instance to the PluginManager by passing a reference to self
216
+ #
217
+ # @return [JenkinsApi::Client::PluginManager] an object to PluginManager
218
+ # subclass
219
+ #
220
+ def plugin
221
+ JenkinsApi::Client::PluginManager.new(self)
222
+ end
223
+
215
224
  # Creates an instance of the User class by passing a reference to self
216
225
  #
217
226
  # @return [JenkinsApi::Client::User] An object of User subclass
@@ -288,8 +297,8 @@ module JenkinsApi
288
297
  # @return [Net::HTTP::Response] Response from Jenkins for "/"
289
298
  #
290
299
  def get_root
291
- @logger.info "GET /"
292
- request = Net::HTTP::Get.new("/")
300
+ @logger.info "GET #{@jenkins_path}/"
301
+ request = Net::HTTP::Get.new("#{@jenkins_path}/")
293
302
  make_http_request(request)
294
303
  end
295
304
 
@@ -633,7 +642,10 @@ module JenkinsApi
633
642
  when 404
634
643
  raise Exceptions::NotFound.new @logger
635
644
  when 500
636
- raise Exceptions::InternalServerError.new @logger
645
+ matched = response.body.match(/Exception: (.*)<br>/)
646
+ api_message = matched[1] unless matched.nil?
647
+ @logger.debug "API message: #{api_message}"
648
+ raise Exceptions::InternalServerError.new(@logger, api_message)
637
649
  when 503
638
650
  raise Exceptions::ServiceUnavailable.new @logger
639
651
  else
@@ -41,8 +41,8 @@ module JenkinsApi
41
41
  #
42
42
  class NothingSubmitted < ApiException
43
43
  def initialize(logger, message = "", log_level = Logger::ERROR)
44
- msg = "Nothing is submitted. #{message}"
45
- super(logger, msg)
44
+ message = "Nothing is submitted." if message.empty?
45
+ super(logger, message)
46
46
  end
47
47
  end
48
48
 
@@ -80,8 +80,8 @@ module JenkinsApi
80
80
  #
81
81
  class Unauthorized < ApiException
82
82
  def initialize(logger, message = "", log_level = Logger::ERROR)
83
- msg = "Invalid credentials are provided. #{message}"
84
- super(logger, msg, Logger::FATAL)
83
+ message = "Invalid credentials are provided." if message.empty?
84
+ super(logger, message, Logger::FATAL)
85
85
  end
86
86
  end
87
87
  # Support for backward compatibility
@@ -158,8 +158,8 @@ module JenkinsApi
158
158
  #
159
159
  class CrumbNotFound < NotFound
160
160
  def initialize(logger, message = "", log_level = Logger::ERROR)
161
- msg = "No crumb available on the server. #{message}"
162
- super(logger, msg)
161
+ message = "No crumb available on the server." if message.empty?
162
+ super(logger, message)
163
163
  end
164
164
  end
165
165
  # Support for backward compatibility
@@ -200,10 +200,10 @@ module JenkinsApi
200
200
  #
201
201
  class InternalServerError < ApiException
202
202
  def initialize(logger, message = "", log_level = Logger::ERROR)
203
- msg = "Internel Server Error. Perhaps the in-memory configuration of" +
204
- " Jenkins is different from the disk configuration." +
205
- " Please try to reload the configuration #{message}"
206
- super(logger, msg)
203
+ message = "Internel Server Error. Perhaps the in-memory configuration" +
204
+ " Jenkins is different from the disk configuration. Please try to" +
205
+ " reload the configuration" if message.empty?
206
+ super(logger, message)
207
207
  end
208
208
  end
209
209
  # Support for backward compatibility
@@ -214,10 +214,10 @@ module JenkinsApi
214
214
  #
215
215
  class ServiceUnavailable < ApiException
216
216
  def initialize(logger, message = "", log_level = Logger::ERROR)
217
- msg = "Jenkins is being reloaded or restarted. Please wait till" +
218
- " Jenkins is completely back online. This can be" +
219
- " programatically achieved by System#wait_for_ready #{message}"
220
- super(logger, msg)
217
+ message = "Jenkins is being reloaded or restarted. Please wait till" +
218
+ " Jenkins is completely back online. This can be" +
219
+ " programatically achieved by System#wait_for_ready" if message.empty?
220
+ super(logger, message)
221
221
  end
222
222
  end
223
223
  # Support for backward compatibility
@@ -227,11 +227,20 @@ module JenkinsApi
227
227
  #
228
228
  class CLIError < ApiException
229
229
  def initialize(logger, message = "", log_level = Logger::ERROR)
230
- msg = "Execute CLI Error. #{message}"
231
- super(logger, msg)
230
+ message = "Unable to execute the command." if message.empty?
231
+ super(logger, message)
232
232
  end
233
233
  end
234
234
  # Support for backward compatibility
235
235
  CLIException = CLIError
236
+
237
+ # Exception when a particular plugin is not found
238
+ #
239
+ class PluginNotFound < NotFound
240
+ def initialize(logger, message = "", log_level = Logger::ERROR)
241
+ message = "The specified plugin is not found" if message.empty?
242
+ super(logger, message)
243
+ end
244
+ end
236
245
  end
237
246
  end
@@ -197,7 +197,7 @@ module JenkinsApi
197
197
  # Update a job with params given as a hash instead of the xml. For the
198
198
  # parameter description see #create_or_update_freestyle
199
199
  #
200
- # @param params [Hash]
200
+ # @param params [Hash] parameters to update a freestyle project
201
201
  #
202
202
  # @see #create_or_update_freestyle
203
203
  # @see #update
@@ -334,7 +334,7 @@ module JenkinsApi
334
334
  # @option params [String] :name Name of the job
335
335
  # @option params [String] :notification_email Email address to send
336
336
  # @option params [Boolean] :notification_email_for_every_unstable
337
- # Send email notification email for every unstable build
337
+ # Send email notification email for every unstable build
338
338
  #
339
339
  def add_email_notification(params)
340
340
  raise "No job name specified" unless params[:name]
@@ -405,7 +405,9 @@ module JenkinsApi
405
405
 
406
406
  # Delete a job given the name
407
407
  #
408
- # @param [String] job_name
408
+ # @param job_name [String] the name of the job to delete
409
+ #
410
+ # @return [String] the response from the HTTP POST request
409
411
  #
410
412
  def delete(job_name)
411
413
  @logger.info "Deleting job '#{job_name}'"
@@ -424,7 +426,9 @@ module JenkinsApi
424
426
 
425
427
  # Wipe out the workspace for a job given the name
426
428
  #
427
- # @param [String] job_name
429
+ # @param job_name [String] the name of the job to wipe out the workspace
430
+ #
431
+ # @return [String] response from the HTTP POST request
428
432
  #
429
433
  def wipe_out_workspace(job_name)
430
434
  @logger.info "Wiping out the workspace of job '#{job_name}'"
@@ -436,8 +440,8 @@ module JenkinsApi
436
440
  # is specified. The build will be stopped only if it was
437
441
  # in 'running' state.
438
442
  #
439
- # @param [String] job_name
440
- # @param [Number] build_number
443
+ # @param job_name [String] the name of the job to stop the build
444
+ # @param build_number [Number] the build number to stop
441
445
  #
442
446
  def stop_build(job_name, build_number = 0)
443
447
  build_number = get_current_build_number(job_name) if build_number == 0
@@ -451,11 +455,15 @@ module JenkinsApi
451
455
  @client.api_post_request("/job/#{job_name}/#{build_number}/stop")
452
456
  end
453
457
  end
458
+ alias_method :stop, :stop_build
459
+ alias_method :abort, :stop_build
454
460
 
455
461
  # Re-create the same job
456
462
  # This is a hack to clear any existing builds
457
463
  #
458
- # @param [String] job_name
464
+ # @param job_name [String] the name of the job to recreate
465
+ #
466
+ # @return [String] the response from the HTTP POST request
459
467
  #
460
468
  def recreate(job_name)
461
469
  @logger.info "Recreating job '#{job_name}'"
@@ -466,8 +474,10 @@ module JenkinsApi
466
474
 
467
475
  # Copy a job
468
476
  #
469
- # @param [String] from_job_name
470
- # @param [String] to_job_name
477
+ # @param from_job_name [String] the name of the job to copy from
478
+ # @param to_job_name [String] the name of the job to copy to
479
+ #
480
+ # @return [String] the response from the HTTP POST request
471
481
  #
472
482
  def copy(from_job_name, to_job_name=nil)
473
483
  to_job_name = "copy_of_#{from_job_name}" if to_job_name.nil?
@@ -481,7 +491,7 @@ module JenkinsApi
481
491
  #
482
492
  # @param [String] job_name Name of the Jenkins job
483
493
  # @param [Number] build_num Specific build number to obtain the
484
- # console output from. Default is the recent build
494
+ # console output from. Default is the recent build
485
495
  # @param [Number] start start offset to get only a portion of the text
486
496
  # @param [String] mode Mode of text output. 'text' or 'html'
487
497
  #
@@ -520,6 +530,8 @@ module JenkinsApi
520
530
 
521
531
  # List all jobs on the Jenkins CI server
522
532
  #
533
+ # @return [Array<String>] the names of all jobs in jenkins
534
+ #
523
535
  def list_all
524
536
  response_json = @client.api_get_request("", "tree=jobs[name]")["jobs"]
525
537
  response_json.map { |job| job["name"] }.sort
@@ -527,7 +539,9 @@ module JenkinsApi
527
539
 
528
540
  # Checks if the given job exists in Jenkins
529
541
  #
530
- # @param [String] job_name
542
+ # @param job_name [String] the name of the job to check
543
+ #
544
+ # @return [Boolean] whether the job exists in jenkins or not
531
545
  #
532
546
  def exists?(job_name)
533
547
  list(job_name).include?(job_name)
@@ -536,8 +550,12 @@ module JenkinsApi
536
550
  # List all Jobs matching the given status
537
551
  # You can optionally pass in jobs list to filter the status from
538
552
  #
539
- # @param [String] status
540
- # @param [Array] jobs
553
+ # @param status [String] the job status to filter
554
+ # @param jobs [Array<String>] if specified this array will be used for
555
+ # filtering by the status otherwise the filtering will be done using
556
+ # all jobs available in jenkins
557
+ #
558
+ # @return [Array<String>] filtered jobs
541
559
  #
542
560
  def list_by_status(status, jobs = [])
543
561
  jobs = list_all if jobs.empty?
@@ -555,8 +573,10 @@ module JenkinsApi
555
573
 
556
574
  # List all jobs that match the given regex
557
575
  #
558
- # @param [String] filter - a regex
559
- # @param [Boolean] ignorecase
576
+ # @param filter [String] a regular expression or a string to filter jobs
577
+ # @param ignorecase [Boolean] whether to ignore case or not
578
+ #
579
+ # @return [Array<String>] jobs matching the given pattern
560
580
  #
561
581
  def list(filter, ignorecase = true)
562
582
  @logger.info "Obtaining jobs matching filter '#{filter}'"
@@ -574,6 +594,8 @@ module JenkinsApi
574
594
 
575
595
  # List all jobs on the Jenkins CI server along with their details
576
596
  #
597
+ # @return [Array<Hash>] the details of all jobs in jenkins
598
+ #
577
599
  def list_all_with_details
578
600
  @logger.info "Obtaining the details of all jobs"
579
601
  response_json = @client.api_get_request("")
@@ -582,7 +604,9 @@ module JenkinsApi
582
604
 
583
605
  # List details of a specific job
584
606
  #
585
- # @param [String] job_name
607
+ # @param job_name [String] the name of the job to obtain the details from
608
+ #
609
+ # @return [Hash] the details of the specified job
586
610
  #
587
611
  def list_details(job_name)
588
612
  @logger.info "Obtaining the details of '#{job_name}'"
@@ -591,7 +615,8 @@ module JenkinsApi
591
615
 
592
616
  # List upstream projects of a specific job
593
617
  #
594
- # @param [String] job_name
618
+ # @param job_name [String] the name of the job to obtain upstream
619
+ # projects for
595
620
  #
596
621
  def get_upstream_projects(job_name)
597
622
  @logger.info "Obtaining the upstream projects of '#{job_name}'"
@@ -601,7 +626,8 @@ module JenkinsApi
601
626
 
602
627
  # List downstream projects of a specific job
603
628
  #
604
- # @param [String] job_name
629
+ # @param job_name [String] the name of the job to obtain downstream
630
+ # projects for
605
631
  #
606
632
  def get_downstream_projects(job_name)
607
633
  @logger.info "Obtaining the down stream projects of '#{job_name}'"
@@ -660,6 +686,7 @@ module JenkinsApi
660
686
  response_json = @client.api_get_request("/job/#{job_name}")
661
687
  color_to_status(response_json["color"])
662
688
  end
689
+ alias_method :status, :get_current_build_status
663
690
 
664
691
  # Obtain the current build number of the given job
665
692
  # This function returns nil if there were no builds for the given job.
@@ -672,6 +699,7 @@ module JenkinsApi
672
699
  @logger.info "Obtaining the current build number of '#{job_name}'"
673
700
  @client.api_get_request("/job/#{job_name}")['nextBuildNumber'].to_i - 1
674
701
  end
702
+ alias_method :build_number, :get_current_build_number
675
703
 
676
704
  # Build a job given the name of the job
677
705
  # You can optionally pass in a list of params for Jenkins to use for
@@ -0,0 +1,460 @@
1
+ #
2
+ # Copyright (c) 2012-2013 Kannan Manickam <arangamani.kannan@gmail.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+
23
+ module JenkinsApi
24
+ class Client
25
+ # This classes communicates with the /pluginManager API for listing
26
+ # installed plugins, installing new plgins through hacks, and performing a
27
+ # lot of operations on installed plugins. It also gives the ability to
28
+ # obtain the details about available plugins in Jenkins update center by
29
+ # commmunicating with /updateCenter API.
30
+ #
31
+ class PluginManager
32
+
33
+ # Initializes a new PluginManager object.
34
+ #
35
+ # @param [Object] client a reference to Client
36
+ #
37
+ def initialize(client)
38
+ @client = client
39
+ @logger = @client.logger
40
+ end
41
+
42
+ # Returns a string representation of PluginManager class.
43
+ #
44
+ def to_s
45
+ "#<JenkinsApi::Client::PluginManager>"
46
+ end
47
+
48
+ # Defines a method to perform the given action on plugin(s)
49
+ #
50
+ # @param action [Symbol] the action to perform
51
+ # @param post_endpoint [Symbol] the endpoint in the POST request for the
52
+ # action
53
+ #
54
+ def self.plugin_action_method(action, post_endpoint)
55
+ define_method(action) do |plugins|
56
+ plugins = [plugins] unless plugins.is_a?(Array)
57
+ @logger.info "Performing '#{action}' on plugins: #{plugins.inspect}"
58
+ plugins.each do |plugin|
59
+ @client.api_post_request(
60
+ "/pluginManager/plugin/#{plugin}/#{post_endpoint}"
61
+ )
62
+ end
63
+ end
64
+ end
65
+
66
+ # Obtains the list of installed plugins from Jenkins along with their
67
+ # version numbers with optional filters
68
+ #
69
+ # @param filters [Hash] optional filters to apply. Use symbols for filter
70
+ # keys
71
+ #
72
+ # @option filters [Boolean] :active filter active/non-active plugins
73
+ # @option filters [Boolean] :bundled filter bundled/non-bundled plugins
74
+ # @option filters [Boolean] :deleted filter deleted/available plugins
75
+ # @option filters [Boolean] :downgradable filter downgradable plugins
76
+ # @option filters [Boolean] :enabled filter enabled/disabled plugins
77
+ # @option filters [Boolean] :hasUpdate filter plugins that has update
78
+ # available. Note that 'U' is capitalized in hasUpdate.
79
+ # @option filters [Boolean] :pinned filter pinned/un-pinned plugins
80
+ #
81
+ # @return [Hash<String, String>] installed plugins and their versions
82
+ # matching the filter provided. returns an empty hash if there are no
83
+ # plugins matched the filters or no plugins are installed in jenkins.
84
+ #
85
+ # @example Listing installed plugins from jenkins
86
+ # >> @client.plugin.list_installed
87
+ # => {
88
+ # "mailer" => "1.5",
89
+ # "external-monitor-job" => "1.1",
90
+ # "ldap" => "1.2"
91
+ # }
92
+ # >> @client.plugin.list_installed(true)
93
+ # => {}
94
+ #
95
+ # @example Listing installed plugins based on filters provided
96
+ # >> @client.plugin.list_installed(
97
+ # :active => true, :deleted => false, :bundled => false
98
+ # )
99
+ # => {
100
+ # "sourcemonitor" => "0.2",
101
+ # "sms-notification" => "1.0",
102
+ # "jquery" => "1.7.2-1",
103
+ # "simple-theme-plugin" => "0.3",
104
+ # "jquery-ui" => "1.0.2",
105
+ # "analysis-core" => "1.49"
106
+ # }
107
+ #
108
+ def list_installed(filters = {})
109
+ supported_filters = [
110
+ :active, :bundled, :deleted, :downgradable, :enabled, :hasUpdate,
111
+ :pinned
112
+ ]
113
+ unless filters.keys.all? { |filter| supported_filters.include?(filter) }
114
+ raise ArgumentError, "Unsupported filters specified." +
115
+ " Supported filters: #{supported_filters.inspect}"
116
+ end
117
+ tree_filters = filters.empty? ? "" : ",#{filters.keys.join(",")}"
118
+ plugins = @client.api_get_request(
119
+ "/pluginManager",
120
+ "tree=plugins[shortName,version#{tree_filters}]"
121
+ )["plugins"]
122
+ installed = Hash[plugins.map do |plugin|
123
+ if filters.keys.all? { |key| plugin[key.to_s] == filters[key] }
124
+ [plugin["shortName"], plugin["version"]]
125
+ end
126
+ end]
127
+ installed
128
+ end
129
+
130
+ # Obtains the details of a single installed plugin
131
+ #
132
+ # @param plugin [String] the plugin ID of the desired plugin
133
+ #
134
+ # @return [Hash] the details of the given installed plugin
135
+ #
136
+ # @example Obtain the information of an installed plugin
137
+ # >> @client.plugin.get_installed_info "ldap"
138
+ # => {
139
+ # "active"=>false,
140
+ # "backupVersion"=>"1.2",
141
+ # "bundled"=>true,
142
+ # "deleted"=>false,
143
+ # "dependencies"=>[],
144
+ # "downgradable"=>true,
145
+ # "enabled"=>false,
146
+ # "hasUpdate"=>false,
147
+ # "longName"=>"LDAP Plugin",
148
+ # "pinned"=>true,
149
+ # "shortName"=>"ldap",
150
+ # "supportsDynamicLoad"=>"MAYBE",
151
+ # "url"=>"http://wiki.jenkins-ci.org/display/JENKINS/LDAP+Plugin",
152
+ # "version"=>"1.5"
153
+ # }
154
+ #
155
+ def get_installed_info(plugin)
156
+ @logger.info "Obtaining the details of plugin: #{plugin}"
157
+ plugins = @client.api_get_request(
158
+ "/pluginManager",
159
+ "depth=1"
160
+ )["plugins"]
161
+ matched_plugin = plugins.select do |a_plugin|
162
+ a_plugin["shortName"] == plugin
163
+ end
164
+ if matched_plugin.empty?
165
+ raise Exceptions::PluginNotFound.new(
166
+ @logger,
167
+ "Plugin '#{plugin}' is not found"
168
+ )
169
+ else
170
+ matched_plugin.first
171
+ end
172
+ end
173
+
174
+ # List the available plugins from jenkins update center along with their
175
+ # version numbers
176
+ #
177
+ # @param filters [Hash] optional filters to filter available plugins.
178
+ #
179
+ # @option filters [Array] :category the category of the plugin to
180
+ # filter
181
+ # @option filters [Array] :dependency the dependency of the plugin to
182
+ # filter
183
+ #
184
+ # @return [Hash<String, String>] available plugins and their versions.
185
+ # returns an empty if no plugins are available.
186
+ #
187
+ # @example Listing available plugins from jenkins
188
+ # >> @client.plugin.list_available
189
+ # => {
190
+ # "accurev" => "0.6.18",
191
+ # "active-directory" => "1.33",
192
+ # "AdaptivePlugin" => "0.1",
193
+ # ...
194
+ # "zubhium" => "0.1.6"
195
+ # }
196
+ #
197
+ # @example Listing available plugins matching a particular category
198
+ # >> pp @client.plugin.list_available(:category => "ui")
199
+ # => {
200
+ # "all-changes"=>"1.3",
201
+ # "bruceschneier"=>"0.1",
202
+ # ...
203
+ # "xfpanel"=>"1.2.2"
204
+ # }
205
+ #
206
+ # @example Listing available plugins matching a particular dependency
207
+ # >> pp @client.plugin.list_available(:dependency => "git")
208
+ # => {
209
+ # "build-failure-analyzer"=>"1.5.0",
210
+ # "buildheroes"=>"0.2",
211
+ # ...
212
+ # "xpdev"=>"1.0"
213
+ # }
214
+ #
215
+ def list_available(filters = {})
216
+ supported_filters = [:category, :dependency]
217
+ filter_plural_map = {
218
+ :dependency => "dependencies",
219
+ :category => "categories"
220
+ }
221
+ unless filters.keys.all? { |filter| supported_filters.include?(filter) }
222
+ raise ArgumentError, "Unsupported filters specified." +
223
+ " Supported filters: #{supported_filters.inspect}"
224
+ end
225
+ # Compute the filters to be passed to the JSON tree parameter
226
+ tree_filters =
227
+ if filters.empty?
228
+ ""
229
+ else
230
+ ",#{filters.keys.map{ |key| filter_plural_map[key] }.join(",")}"
231
+ end
232
+
233
+ availables = @client.api_get_request(
234
+ "/updateCenter/coreSource",
235
+ "tree=availables[name,version#{tree_filters}]"
236
+ )["availables"]
237
+ Hash[availables.map do |plugin|
238
+ if filters.keys.all? do |key|
239
+ !plugin[filter_plural_map[key]].nil? &&
240
+ plugin[filter_plural_map[key]].include?(filters[key])
241
+ end
242
+ [plugin["name"], plugin["version"]]
243
+ end
244
+ end]
245
+ end
246
+
247
+ # Obtains the information about a plugin that is available in the Jenkins
248
+ # update center
249
+ #
250
+ # @param plugin [String] the plugin ID to obtain information for
251
+ #
252
+ # @return [Hash] the details of the given plugin
253
+ #
254
+ # @example Obtaining the details of a plugin available in jenkins
255
+ # >> @client.plugin.get_available_info "status-view"
256
+ # => {
257
+ # "name"=>"status-view",
258
+ # "sourceId"=>"default",
259
+ # "url"=>"http://updates.jenkins-ci.org/download/plugins/status-view/1.0/status-view.hpi",
260
+ # "version"=>"1.0",
261
+ # "categories"=>["ui"],
262
+ # "compatibleSinceVersion"=>nil,
263
+ # "compatibleWithInstalledVersion"=>true,
264
+ # "dependencies"=>{},
265
+ # "excerpt"=>"View type to show jobs filtered by the status of the last completed build.",
266
+ # "installed"=>nil, "neededDependencies"=>[],
267
+ # "requiredCore"=>"1.342",
268
+ # "title"=>"Status View Plugin",
269
+ # "wiki"=>"https://wiki.jenkins-ci.org/display/JENKINS/Status+View+Plugin"
270
+ # }
271
+ #
272
+ def get_available_info(plugin)
273
+ plugins = @client.api_get_request(
274
+ "/updateCenter/coreSource",
275
+ "depth=1"
276
+ )["availables"]
277
+ matched_plugin = plugins.select do |a_plugin|
278
+ a_plugin["name"] == plugin
279
+ end
280
+ if matched_plugin.empty?
281
+ raise Exceptions::PluginNotFound.new(
282
+ @logger,
283
+ "Plugin '#{plugin}' is not found"
284
+ )
285
+ else
286
+ matched_plugin.first
287
+ end
288
+ end
289
+
290
+ # List the available updates for plugins from jenkins update center
291
+ # along with their version numbers
292
+ #
293
+ # @return [Hash<String, String>] available plugin updates and their
294
+ # versions. returns an empty if no plugins are available.
295
+ #
296
+ # @example Listing available plugin updates from jenkins
297
+ # >> @client.plugin.list_updates
298
+ # => {
299
+ # "ldap" => "1.5",
300
+ # "ssh-slaves" => "0.27",
301
+ # "subversion" => "1".50
302
+ # }
303
+ #
304
+ def list_updates
305
+ updates = @client.api_get_request(
306
+ "/updateCenter/coreSource",
307
+ "tree=updates[name,version]"
308
+ )["updates"]
309
+ Hash[updates.map { |plugin| [plugin["name"], plugin["version"]] }]
310
+ end
311
+
312
+ # Installs a specific plugin or list of plugins. This method will install
313
+ # the latest available plugins that jenkins reports. The installation
314
+ # might not take place right away for some plugins and they might require
315
+ # restart of jenkins instances. This method makes a single POST request
316
+ # for the installation of multiple plugins. Updating plugins can be done
317
+ # the same way. When the install action is issued, it gets the latest
318
+ # version of the plugin if the plugin is outdated.
319
+ #
320
+ # @see Client#api_post_request
321
+ # @see #restart_required?
322
+ # @see System#restart
323
+ # @see #uninstall
324
+ #
325
+ # @param plugins [String, Array] a single plugin or a list of plugins to
326
+ # be installed
327
+ #
328
+ # @return [String] the HTTP code from the plugin install POST request
329
+ #
330
+ # @example Installing a plugin and restart jenkins if required
331
+ # >> @client.plugin.install "s3"
332
+ # => "302" # Response code from plugin installation POST
333
+ # >> @client.plugin.restart_required?
334
+ # => true # A restart is required for the installation completion
335
+ # >> @client.system.restart(true)
336
+ # => "302" # A force restart is performed
337
+ #
338
+ def install(plugins)
339
+ # Convert the input argument to an array if it is not already an array
340
+ plugins = [plugins] unless plugins.is_a?(Array)
341
+ @logger.info "Installing plugins: #{plugins.inspect}"
342
+
343
+ # Build the form data to post to jenkins
344
+ form_data = {}
345
+ plugins.each { |plugin| form_data["plugin.#{plugin}.default"] = "on" }
346
+ @client.api_post_request("/pluginManager/install", form_data)
347
+ end
348
+ alias_method :update, :install
349
+
350
+
351
+ # @!method uninstall(plugins)
352
+ #
353
+ # Uninstalls the specified plugin or list of plugins. Only the user
354
+ # installed plugins can be uninstalled. The plugins installed by default
355
+ # by jenkins (also known as bundled plugins) cannot be uninstalled. The
356
+ # call will succeed but the plugins wil still remain in jenkins installed.
357
+ # This method makes a POST request for every plugin requested - so it
358
+ # might lead to some delay if a big list is provided.
359
+ #
360
+ # @see Client#api_post_request
361
+ # @see #restart_required?
362
+ # @see System#restart
363
+ # @see #install
364
+ #
365
+ # @param plugins [String, Array] a single plugin or list of plugins to be
366
+ # uninstalled
367
+ #
368
+ plugin_action_method :uninstall, :doUninstall
369
+
370
+ # @!method downgrade(plugins)
371
+ #
372
+ # Downgrades the specified plugin or list of plugins. This method makes s
373
+ # POST request for every plugin specified - so it might lead to some
374
+ # delay if a big list is provided.
375
+ #
376
+ # @see Client#api_post_request
377
+ # @see #restart_required?
378
+ # @see System#restart
379
+ # @see #install
380
+ #
381
+ # @param [String, Array] a single plugin or list of plugins to be
382
+ # downgraded
383
+ #
384
+ plugin_action_method :downgrade, :downgrade
385
+
386
+ # @!method unpin(plugins)
387
+ #
388
+ # Unpins the specified plugin or list of plugins. This method makes a
389
+ # POST request for every plugin specified - so it might lead to some
390
+ # delay if a big list is provided.
391
+ #
392
+ # @see Client#api_post_request
393
+ # @see #restart_required?
394
+ # @see System#restart
395
+ #
396
+ # @param plugins [String, Array] a single plugin or list of plugins to be
397
+ # uninstalled
398
+ #
399
+ plugin_action_method :unpin, :unpin
400
+
401
+ # @!method enable(plugins)
402
+ #
403
+ # Enables the specified plugin or list of plugins. This method makes a
404
+ # POST request for every plugin specified - so it might lead to some
405
+ # delay if a big list is provided.
406
+ #
407
+ # @see Client#api_post_request
408
+ # @see #restart_required?
409
+ # @see System#restart
410
+ # @see #disable
411
+ #
412
+ # @param plugins [String, Array] a single plugin or list of plugins to be
413
+ # uninstalled
414
+ #
415
+ plugin_action_method :enable, :makeEnabled
416
+
417
+ # @!method disable(plugins)
418
+ #
419
+ # Disables the specified plugin or list of plugins. This method makes a
420
+ # POST request for every plugin specified - so it might lead to some
421
+ # delay if a big list is provided.
422
+ #
423
+ # @see Client#api_post_request
424
+ # @see #restart_required?
425
+ # @see System#restart
426
+ # @see #enable
427
+ #
428
+ # @param plugins [String, Array] a single plugin or list of plugins to be
429
+ # uninstalled
430
+ #
431
+ plugin_action_method :disable, :makeDisabled
432
+
433
+ # Requests the Jenkins plugin manager to check for updates by connecting
434
+ # to the update site.
435
+ #
436
+ # @see #list_updates
437
+ #
438
+ def check_for_updates
439
+ @client.api_post_request("/pluginManager/checkUpdates")
440
+ end
441
+
442
+ # Whether restart required for the completion of plugin
443
+ # installations/uninstallations
444
+ #
445
+ # @see Client#api_get_request
446
+ #
447
+ # @return [Boolean] whether restart is required for the completion for
448
+ # plugin installations/uninstallations.
449
+ #
450
+ def restart_required?
451
+ response = @client.api_get_request(
452
+ "/updateCenter",
453
+ "tree=restartRequiredForCompletion"
454
+ )
455
+ response["restartRequiredForCompletion"] ||
456
+ !list_installed(:deleted => true).empty?
457
+ end
458
+ end
459
+ end
460
+ end