jenkins_api_client2 1.9.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.
- checksums.yaml +7 -0
- data/bin/jenkinscli2 +5 -0
- data/java_deps/jenkins-cli.jar +0 -0
- data/jenkins_api_client2.gemspec +30 -0
- data/lib/jenkins_api_client2/build_queue.rb +262 -0
- data/lib/jenkins_api_client2/cli/base.rb +84 -0
- data/lib/jenkins_api_client2/cli/helper.rb +61 -0
- data/lib/jenkins_api_client2/cli/job.rb +133 -0
- data/lib/jenkins_api_client2/cli/node.rb +97 -0
- data/lib/jenkins_api_client2/cli/system.rb +65 -0
- data/lib/jenkins_api_client2/client.rb +855 -0
- data/lib/jenkins_api_client2/exceptions.rb +246 -0
- data/lib/jenkins_api_client2/job.rb +1966 -0
- data/lib/jenkins_api_client2/node.rb +353 -0
- data/lib/jenkins_api_client2/plugin_manager.rb +460 -0
- data/lib/jenkins_api_client2/plugin_settings/base.rb +11 -0
- data/lib/jenkins_api_client2/plugin_settings/collection.rb +39 -0
- data/lib/jenkins_api_client2/plugin_settings/hipchat.rb +53 -0
- data/lib/jenkins_api_client2/plugin_settings/workspace_cleanup.rb +35 -0
- data/lib/jenkins_api_client2/root.rb +67 -0
- data/lib/jenkins_api_client2/system.rb +134 -0
- data/lib/jenkins_api_client2/urihelper.rb +18 -0
- data/lib/jenkins_api_client2/user.rb +131 -0
- data/lib/jenkins_api_client2/version.rb +36 -0
- data/lib/jenkins_api_client2/view.rb +313 -0
- data/lib/jenkins_api_client2.rb +52 -0
- metadata +170 -0
@@ -0,0 +1,353 @@
|
|
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
|
+
require 'jenkins_api_client2/urihelper'
|
24
|
+
|
25
|
+
module JenkinsApi
|
26
|
+
class Client
|
27
|
+
# This class communicates with Jenkins "/computer" API to obtain details
|
28
|
+
# about nodes or slaves connected to the Jenkins.
|
29
|
+
#
|
30
|
+
class Node
|
31
|
+
include JenkinsApi::UriHelper
|
32
|
+
|
33
|
+
# General attributes of a node.
|
34
|
+
# This allows the following methods to be called on this node object.
|
35
|
+
# These methods are defined using define_method and are prefixed
|
36
|
+
# with get_.
|
37
|
+
#
|
38
|
+
# def get_busyExecutors
|
39
|
+
# def get_displayName
|
40
|
+
# def get_totalExecutors
|
41
|
+
#
|
42
|
+
GENERAL_ATTRIBUTES = [
|
43
|
+
"busyExecutors",
|
44
|
+
"displayName",
|
45
|
+
"totalExecutors"
|
46
|
+
].freeze
|
47
|
+
|
48
|
+
# Properties of a node.
|
49
|
+
# The following methods are defined to be called on the node object
|
50
|
+
# and are prefixed with is_ and end with ? as they return true or false.
|
51
|
+
#
|
52
|
+
# def is_idle?(node_name)
|
53
|
+
# def is_jnlpAgent?(node_name)
|
54
|
+
# def is_launchSupported?(node_name)
|
55
|
+
# def is_manualLaunchAllowed?(node_name)
|
56
|
+
# def is_offline?(node_name)
|
57
|
+
# def is_temporarilyOffline?(node_name)
|
58
|
+
#
|
59
|
+
NODE_PROPERTIES = [
|
60
|
+
"idle",
|
61
|
+
"jnlpAgent",
|
62
|
+
"launchSupported",
|
63
|
+
"manualLaunchAllowed",
|
64
|
+
"offline",
|
65
|
+
"temporarilyOffline"
|
66
|
+
].freeze
|
67
|
+
|
68
|
+
# Node specific attributes.
|
69
|
+
# The following methods are defined using define_method.
|
70
|
+
# These methods are prefixed with get_node_.
|
71
|
+
#
|
72
|
+
# def get_node_numExecutors(node_name)
|
73
|
+
# def get_node_icon(node_name)
|
74
|
+
# def get_node_displayName(node_name)
|
75
|
+
# def get_node_loadStatistics(node_name)
|
76
|
+
# def get_node_monitorData(node_name)
|
77
|
+
# def get_node_offlineCause(node_name)
|
78
|
+
# def get_node_oneOffExecutors(node_name)
|
79
|
+
#
|
80
|
+
NODE_ATTRIBUTES = [
|
81
|
+
"numExecutors",
|
82
|
+
"icon",
|
83
|
+
"displayName",
|
84
|
+
"loadStatistics",
|
85
|
+
"monitorData",
|
86
|
+
"offlineCause",
|
87
|
+
"oneOffExecutors"
|
88
|
+
].freeze
|
89
|
+
|
90
|
+
# Initializes a new node object
|
91
|
+
#
|
92
|
+
# @param client [Client] the client object
|
93
|
+
#
|
94
|
+
# @return [Node] the node object
|
95
|
+
#
|
96
|
+
def initialize(client)
|
97
|
+
@client = client
|
98
|
+
@logger = @client.logger
|
99
|
+
end
|
100
|
+
|
101
|
+
# Gives the string representation of the Object
|
102
|
+
#
|
103
|
+
def to_s
|
104
|
+
"#<JenkinsApi::Client::Node>"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Creates a new node with the specified parameters
|
108
|
+
#
|
109
|
+
# @param [Hash] params parameters for creating a dumb slave
|
110
|
+
# * +:name+ name of the slave
|
111
|
+
# * +:description+ description of the new slave
|
112
|
+
# * +:executors+ number of executors
|
113
|
+
# * +:remote_fs+ Remote FS root
|
114
|
+
# * +:labels+ comma separated list of labels
|
115
|
+
# * +:mode+ mode of the slave: normal, exclusive
|
116
|
+
# * +:slave_host+ Hostname/IP of the slave
|
117
|
+
# * +:slave_port+ Slave port
|
118
|
+
# * +:private_key_file+ Private key file of master
|
119
|
+
# * +:credentials_id+ Id for credential in Jenkins
|
120
|
+
#
|
121
|
+
# @example Create a Dumb Slave
|
122
|
+
# create_dumb_slave(
|
123
|
+
# :name => "slave1",
|
124
|
+
# :slave_host => "10.10.10.10",
|
125
|
+
# :private_key_file => "/root/.ssh/id_rsa",
|
126
|
+
# :executors => 10,
|
127
|
+
# :labels => "slave, ruby"
|
128
|
+
# )
|
129
|
+
#
|
130
|
+
def create_dumb_slave(params)
|
131
|
+
unless params[:name] && params[:slave_host] && params[:private_key_file]
|
132
|
+
raise ArgumentError, "Name, slave host, and private key file are" +
|
133
|
+
" required for creating a slave."
|
134
|
+
end
|
135
|
+
|
136
|
+
@logger.info "Creating a dumb slave '#{params[:name]}'"
|
137
|
+
@logger.debug "Creating a dumb slave with params: #{params.inspect}"
|
138
|
+
default_params = {
|
139
|
+
:description => "Automatically created through jenkins_api_client2",
|
140
|
+
:executors => 2,
|
141
|
+
:remote_fs => "/var/jenkins",
|
142
|
+
:labels => params[:name],
|
143
|
+
:slave_port => 22,
|
144
|
+
:mode => "normal",
|
145
|
+
:private_key_file => "",
|
146
|
+
:credentials_id => ""
|
147
|
+
}
|
148
|
+
|
149
|
+
params = default_params.merge(params)
|
150
|
+
labels = params[:labels].split(/\s*,\s*/).join(" ")
|
151
|
+
mode = params[:mode].upcase
|
152
|
+
|
153
|
+
post_params = {
|
154
|
+
"name" => params[:name],
|
155
|
+
"type" => "hudson.slaves.DumbSlave$DescriptorImpl",
|
156
|
+
"json" => {
|
157
|
+
"name" => params[:name],
|
158
|
+
"nodeDescription" => params[:description],
|
159
|
+
"numExecutors" => params[:executors],
|
160
|
+
"remoteFS" => params[:remote_fs],
|
161
|
+
"labelString" => labels,
|
162
|
+
"mode" => mode,
|
163
|
+
"type" => "hudson.slaves.DumbSlave$DescriptorImpl",
|
164
|
+
"retentionStrategy" => {
|
165
|
+
"stapler-class" => "hudson.slaves.RetentionStrategy$Always"
|
166
|
+
},
|
167
|
+
"nodeProperties" => {
|
168
|
+
"stapler-class-bag" => "true"
|
169
|
+
},
|
170
|
+
"launcher" => {
|
171
|
+
"stapler-class" => "hudson.plugins.sshslaves.SSHLauncher",
|
172
|
+
"host" => params[:slave_host],
|
173
|
+
"port" => params[:slave_port],
|
174
|
+
"username" => params[:slave_user],
|
175
|
+
"privatekey" => params[:private_key_file],
|
176
|
+
"credentialsId" => params[:credentials_id]
|
177
|
+
}
|
178
|
+
}.to_json
|
179
|
+
}
|
180
|
+
@logger.debug "Modified params posted to create slave:" +
|
181
|
+
" #{post_params.inspect}"
|
182
|
+
@client.api_post_request("/computer/doCreateItem", post_params)
|
183
|
+
end
|
184
|
+
|
185
|
+
def create_dump_slave(params)
|
186
|
+
@logger.warn '[DEPRECATED] Please use create_dumb_slave instead.'
|
187
|
+
create_dumb_slave(params)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Deletes the specified node
|
191
|
+
#
|
192
|
+
# @param [String] node_name Name of the node to delete
|
193
|
+
#
|
194
|
+
def delete(node_name)
|
195
|
+
@logger.info "Deleting node '#{node_name}'"
|
196
|
+
if list.include?(node_name)
|
197
|
+
@client.api_post_request("/computer/#{path_encode node_name}/doDelete")
|
198
|
+
else
|
199
|
+
raise "The specified node '#{node_name}' doesn't exist in Jenkins."
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Deletes all slaves from Jenkins. The master will be the only node alive
|
204
|
+
# after the exection of this call.
|
205
|
+
#
|
206
|
+
# @note This method will remove all slaves from Jenkins. Please use with
|
207
|
+
# caution.
|
208
|
+
#
|
209
|
+
def delete_all!
|
210
|
+
@logger.info "Deleting all nodes (except master) from jenkins"
|
211
|
+
list.each { |node| delete(node) unless node == "master" }
|
212
|
+
end
|
213
|
+
|
214
|
+
# This method returns two lists 1) nodes online 2) nodes offline
|
215
|
+
#
|
216
|
+
# @param [String] filter a regex to filter node names
|
217
|
+
# @param [Bool] ignorecase whether to be case sensitive or not
|
218
|
+
#
|
219
|
+
def online_offline_lists(filter = nil, ignorecase = true)
|
220
|
+
@logger.info "Obtaining nodes from jenkins matching filter '#{filter}'"
|
221
|
+
offline_node_names = []
|
222
|
+
online_node_names = []
|
223
|
+
response_json = @client.api_get_request("/computer")
|
224
|
+
response_json["computer"].each do |computer|
|
225
|
+
if computer["displayName"] =~ /#{filter}/i
|
226
|
+
if computer["offline"] == true
|
227
|
+
offline_node_names << computer["displayName"]
|
228
|
+
else
|
229
|
+
online_node_names << computer["displayName"]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
return online_node_names, offline_node_names
|
234
|
+
end
|
235
|
+
|
236
|
+
# This method lists all nodes
|
237
|
+
#
|
238
|
+
# @param [String] filter a regex to filter node names
|
239
|
+
# @param [Bool] ignorecase whether to be case sensitive or not
|
240
|
+
#
|
241
|
+
def list(filter = nil, ignorecase = true, slaveonly = false)
|
242
|
+
@logger.info "Obtaining nodes from jenkins matching filter '#{filter}'"
|
243
|
+
node_names = []
|
244
|
+
response_json = @client.api_get_request("/computer")
|
245
|
+
response_json["computer"].each do |computer|
|
246
|
+
if computer["displayName"] =~ /#{filter}/i
|
247
|
+
unless slaveonly && computer["displayName"] == "master"
|
248
|
+
node_names << computer["displayName"]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
node_names
|
253
|
+
end
|
254
|
+
|
255
|
+
# Identifies the index of a node name in the array node nodes
|
256
|
+
#
|
257
|
+
# @param [String] node_name name of the node
|
258
|
+
#
|
259
|
+
def index(node_name)
|
260
|
+
response_json = @client.api_get_request("/computer")
|
261
|
+
response_json["computer"].each_with_index do |computer, index|
|
262
|
+
return index if computer["displayName"] == node_name
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Defines methods for general node attributes.
|
267
|
+
#
|
268
|
+
GENERAL_ATTRIBUTES.each do |meth_suffix|
|
269
|
+
define_method("get_#{meth_suffix}") do
|
270
|
+
@logger.info "Obtaining '#{meth_suffix}' attribute from jenkins"
|
271
|
+
response_json = @client.api_get_request("/computer", "tree=#{path_encode meth_suffix}[*[*[*]]]")
|
272
|
+
response_json["#{meth_suffix}"]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Defines methods for node properties.
|
277
|
+
#
|
278
|
+
NODE_PROPERTIES.each do |meth_suffix|
|
279
|
+
define_method("is_#{meth_suffix}?") do |node_name|
|
280
|
+
@logger.info "Obtaining '#{meth_suffix}' property of '#{node_name}'"
|
281
|
+
node_name = "(master)" if node_name == "master"
|
282
|
+
response_json = @client.api_get_request("/computer/#{path_encode node_name}", "tree=#{path_encode meth_suffix}")
|
283
|
+
resp = response_json["#{meth_suffix}"].to_s
|
284
|
+
resp =~ /False/i ? false : true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# Defines methods for node specific attributes.
|
289
|
+
NODE_ATTRIBUTES.each do |meth_suffix|
|
290
|
+
define_method("get_node_#{meth_suffix}") do |node_name|
|
291
|
+
@logger.info "Obtaining '#{meth_suffix}' attribute of '#{node_name}'"
|
292
|
+
node_name = "(master)" if node_name == "master"
|
293
|
+
response_json = @client.api_get_request("/computer/#{path_encode node_name}", "tree=#{path_encode meth_suffix}[*[*[*]]]")
|
294
|
+
response_json["#{meth_suffix}"]
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Changes the mode of a slave node in Jenkins
|
299
|
+
#
|
300
|
+
# @param [String] node_name name of the node to change mode for
|
301
|
+
# @param [String] mode mode to change to
|
302
|
+
#
|
303
|
+
def change_mode(node_name, mode)
|
304
|
+
@logger.info "Changing the mode of '#{node_name}' to '#{mode}'"
|
305
|
+
mode = mode.upcase
|
306
|
+
xml = get_config(node_name)
|
307
|
+
n_xml = Nokogiri::XML(xml)
|
308
|
+
desc = n_xml.xpath("//mode").first
|
309
|
+
desc.content = "#{mode.upcase}"
|
310
|
+
xml_modified = n_xml.to_xml
|
311
|
+
post_config(node_name, xml_modified)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Obtains the configuration of node from Jenkins server
|
315
|
+
#
|
316
|
+
# @param [String] node_name name of the node
|
317
|
+
#
|
318
|
+
def get_config(node_name)
|
319
|
+
@logger.info "Obtaining the config.xml of node '#{node_name}'"
|
320
|
+
node_name = "(master)" if node_name == "master"
|
321
|
+
@client.get_config("/computer/#{path_encode node_name}")
|
322
|
+
end
|
323
|
+
|
324
|
+
# Posts the given config.xml to the Jenkins node
|
325
|
+
#
|
326
|
+
# @param [String] node_name name of the node
|
327
|
+
# @param [String] xml Config.xml of the node
|
328
|
+
#
|
329
|
+
def post_config(node_name, xml)
|
330
|
+
@logger.info "Posting the config.xml of node '#{node_name}'"
|
331
|
+
node_name = "(master)" if node_name == "master"
|
332
|
+
@client.post_config("/computer/#{path_encode node_name}/config.xml", xml)
|
333
|
+
end
|
334
|
+
|
335
|
+
# Toggles the temporarily offline state of the Jenkins node
|
336
|
+
#
|
337
|
+
# @param [String] node_name name of the node
|
338
|
+
# @param [String] reason Offline reason why the node is offline
|
339
|
+
#
|
340
|
+
def toggle_temporarilyOffline(node_name, reason="")
|
341
|
+
@logger.info "Toggling the temporarily offline status of of node '#{node_name}' with reason '#{reason}'"
|
342
|
+
node_name = "(master)" if node_name == "master"
|
343
|
+
previous_state = is_temporarilyOffline?(node_name)
|
344
|
+
@client.api_post_request("/computer/#{path_encode node_name}/toggleOffline?offlineMessage=#{path_encode reason}")
|
345
|
+
new_state = is_temporarilyOffline?(node_name)
|
346
|
+
if new_state == previous_state
|
347
|
+
raise "The specified node '#{node_name}' was unable to change offline state."
|
348
|
+
end
|
349
|
+
new_state
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|