jenkins2 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,105 +0,0 @@
1
- require 'cgi'
2
-
3
- module Jenkins2
4
- class Client
5
- module NodeCommands
6
- # Connects a node.
7
- # +node+:: Node name, <tt>(master)</tt> for master.
8
- def connect_node( node: '(master)' )
9
- api_request( :post, "/computer/#{node}/launchSlaveAgent" )
10
- end
11
-
12
- # Creates a new node, by providing node definition XML.
13
- # Keyword parameters:
14
- # +node+:: Node name.
15
- # +xml_config+:: New configuration in xml format.
16
- def create_node( node: nil, xml_config: nil )
17
- xml_config = STDIN.read if xml_config.nil?
18
- api_request( :post, "/computer/doCreateItem", :raw ) do |req|
19
- req.form_data = { 'name' => node, type: "hudson.slaves.DumbSlave$DescriptorImpl",
20
- json: {}.to_json }
21
- end
22
- update_node( node: node, xml_config: xml_config )
23
- end
24
-
25
- # Deletes a node
26
- # +node+:: Node name. Master cannot be deleted.
27
- def delete_node( node: nil )
28
- raise ArgumentError, 'node must be provided' if node.nil?
29
- api_request( :post, "/computer/#{node}/doDelete" )
30
- end
31
-
32
- # Disconnects a node.
33
- # +node+:: Node name, <tt>(master)</tt> for master.
34
- # +message+:: Reason why the node is being disconnected.
35
- def disconnect_node( node: '(master)', message: nil )
36
- api_request( :post, "/computer/#{node}/doDisconnect" ) do |req|
37
- req.body = "offlineMessage=#{CGI::escape message}" unless message.nil?
38
- end
39
- end
40
-
41
- # Returns the node definition XML.
42
- # +node+:: Node name, <tt>(master)</tt> for master.
43
- def get_node_xml( node: '(master)' )
44
- api_request( :get, "/computer/#{node}/config.xml", :body )
45
- end
46
-
47
- # Returns the node state
48
- # +node+:: Node name, <tt>(master)</tt> for master.
49
- def get_node( node: '(master)' )
50
- api_request( :get, "/computer/#{node}/api/json" )
51
- end
52
-
53
- # Sets node temporarily offline. Does nothing, if node is already offline.
54
- # +node+:: Node name, or <tt>(master)</tt> for master.
55
- # +message+:: Record the note about this node is being disconnected.
56
- def offline_node( node: '(master)', message: nil )
57
- if node_online?( node: node )
58
- api_request( :post, "/computer/#{node}/toggleOffline" ) do |req|
59
- req.body = "offlineMessage=#{CGI::escape message}" unless message.nil?
60
- end
61
- end
62
- end
63
-
64
- # Sets node back online, if node is temporarily offline.
65
- # +node+:: Node name, <tt>(master)</tt> for master.
66
- def online_node( node: '(master)' )
67
- api_request( :post, "/computer/#{node}/toggleOffline" ) unless node_online?( node: node )
68
- end
69
-
70
- # Updates the node definition XML
71
- # Keyword parameters:
72
- # +node+:: Node name, <tt>(master)</tt> for master.
73
- # +xml_config+:: New configuration in xml format.
74
- def update_node( node: '(master)', xml_config: nil )
75
- xml_config = STDIN.read if xml_config.nil?
76
- api_request( :post, "/computer/#{node}/config.xml", :body ) do |req|
77
- req.body = xml_config
78
- end
79
- end
80
-
81
- # Waits for node to become idle or until +max_wait_minutes+ pass.
82
- # +node+:: Node name, <tt>(master)</tt> for master.
83
- # +max_wait_minutes+:: Maximum wait time in minutes. Default 60.
84
- def wait_node_idle( node: '(master)', max_wait_minutes: 60 )
85
- Jenkins2::Wait.wait( max_wait_minutes: max_wait_minutes ){ node_idle? node: node }
86
- end
87
-
88
- def node_idle?( node: '(master)' )
89
- get_node( node: node )['idle']
90
- end
91
-
92
- # Checks if node is online (= not temporarily offline )
93
- # +node+:: Node name. Use <tt>(master)</tt> for master.
94
- def node_online?( node: '(master)' )
95
- !get_node( node: node )['temporarilyOffline']
96
- end
97
-
98
- # Checks if node is connected, i.e. Master connected and launched client on it.
99
- # +node+:: Node name. Use <tt>(master)</tt> for master.
100
- def node_connected?( node: '(master)' )
101
- !get_node( node: node )['offline']
102
- end
103
- end
104
- end
105
- end
@@ -1,53 +0,0 @@
1
- module Jenkins2
2
- class Client
3
- module PluginCommands
4
- # Installs plugins by short name (like +thinBackup+).
5
- # +names+:: List of short names.
6
- def install_plugins( *names )
7
- api_request( :post, '/pluginManager/install' ) do |req|
8
- req.form_data = names.flatten.inject({}) do |memo,obj|
9
- memo.merge "plugin.#{obj}.default" => 'on'
10
- end.merge( 'dynamicLoad' => 'Install without restart' )
11
- end
12
- end
13
-
14
- # Installs a plugin by uploading *.hpi or *.jpi file.
15
- # +plugin_file+:: A *.hpi or *.jpi file itself ( not some path )
16
- def upload_plugin( plugin_file )
17
- api_request( :post, '/pluginManager/uploadPlugin' ) do |req|
18
- req.body = plugin_file
19
- req.content_type = 'multipart/form-data'
20
- end
21
- end
22
-
23
- # Lists installed plugins
24
- def list_plugins
25
- api_request( :get, '/pluginManager/api/json?depth=1' )['plugins']
26
- end
27
-
28
- # Checks, if all of the plugins from the passed list are installed
29
- # +names+:: List of short names of plugins (like +thinBackup+).
30
- def plugins_installed?( *names )
31
- plugins = list_plugins
32
- return false if plugins.nil?
33
- names.flatten.all? do |name|
34
- plugins.detect{|p| p['shortName'] == name and !p['deleted'] }
35
- end
36
- end
37
-
38
- # Uninstalls a plugin
39
- # +name+:: Plugin short name
40
- def uninstall_plugin( name )
41
- api_request( :post, "/pluginManager/plugin/#{name}/doUninstall" ) do |req|
42
- req.form_data = { 'Submit' => 'Yes', 'json' => '{}' }
43
- end
44
- end
45
-
46
- def wait_plugins_installed( *names, max_wait_minutes: 2 )
47
- Wait.wait( max_wait_minutes: max_wait_minutes ) do
48
- plugins_installed? names
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,51 +0,0 @@
1
- require 'optparse'
2
-
3
- module Jenkins2
4
- class CommandParser < OptionParser
5
- attr_reader :command_name
6
-
7
- def command( key, desc, &block )
8
- sw = OptionParser::Switch::NoArgument.new( key, nil, [key], nil, nil, [desc],
9
- Proc.new{ OptionParser.new( &block ) } ), [], [key]
10
- commands[key.to_s] = sw[0]
11
- end
12
-
13
- def parse!( argv=default_argv )
14
- @command_name = argv.detect{|c| commands.has_key? c }
15
- if command_name
16
- #create temporary parser with option definitions from both: globalparse and subparse
17
- OptionParser.new do |parser|
18
- parser.instance_variable_set(:@stack,
19
- commands[command_name.to_s].block.call.instance_variable_get(:@stack) + @stack)
20
- end.parse! argv
21
- else
22
- super( argv )
23
- end
24
- end
25
-
26
- def commands
27
- @commands ||= {}
28
- end
29
-
30
- private
31
- def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
32
- super(to, width, max, indent, &blk)
33
- if command_name and commands.has_key?( command_name )
34
- to << "Command:\n"
35
- commands[command_name].summarize( {}, {}, width, max, indent ) do |l|
36
- to << (l.index($/, -1) ? l : l + $/)
37
- end
38
- to << "Command options:\n"
39
- commands[command_name].block.call.summarize( to, width, max, indent, &blk )
40
- else
41
- to << "Commands:\n"
42
- commands.each do |name, command|
43
- command.summarize( {}, {}, width, max, indent ) do |l|
44
- to << (l.index($/, -1) ? l : l + $/)
45
- end
46
- end
47
- end
48
- to
49
- end
50
- end
51
- end
@@ -1,221 +0,0 @@
1
- require 'optparse/uri'
2
- require_relative 'cmdparse'
3
- require_relative 'client'
4
- require_relative 'log'
5
-
6
- module Jenkins2
7
- class CommandLine
8
- attr_accessor :global_options
9
- attr_accessor :command_options
10
-
11
- def initialize( args )
12
- @global_options = OptionParser::OptionMap.new
13
- @log_options = { verbose: 0, log: STDOUT }
14
- @command_options = OptionParser::OptionMap.new
15
- global = CommandParser.new 'Usage: jenkins [global-options] <command> [options]' do |opts|
16
- opts.separator ''
17
- opts.separator "Global options (accepted by all commands):"
18
- opts.on '-s', '--server URL', ::URI, 'Jenkins Server Url' do |opt|
19
- @global_options[:server] = opt
20
- end
21
- opts.on '-u', '--user USER', 'Jenkins API user' do |opt|
22
- @global_options[:user] = opt
23
- end
24
- opts.on '-k', '--key KEY', 'Jenkins API key' do |opt|
25
- @global_options[:key] = opt
26
- end
27
- opts.on '-c', '--config [PATH]', 'Use configuration file. Instead of providing '\
28
- 'server, user and key through command line, you can do that with configuration file. '\
29
- 'File format is json: { "server": "http://jenkins.example.com", "user": "admin", '\
30
- '"key": "123456" }. Options provided in command line will overwrite ones from '\
31
- 'configuration file. Program looks for ~/.jenkins2.json if no PATH is provided.' do |opt|
32
- @global_options[:config] = opt || ::File.join( ENV['HOME'], '.jenkins2.json' )
33
- end
34
- opts.on '-l', '--log FILE', 'Log file. Prints to standard out, if not provided' do |opt|
35
- @log_options[:log] = opt
36
- end
37
- opts.on '-v', '--verbose', 'Print more info. Up to -vvv. Prints only errors by default.' do
38
- @log_options[:verbose] += 1
39
- end
40
- opts.on '-h', '--help', 'Show help' do
41
- @global_options[:help] = true
42
- end
43
- opts.on '-V', '--version', 'Show version' do
44
- puts VERSION
45
- exit
46
- end
47
-
48
- opts.separator ''
49
- opts.separator 'For command specific options run: jenkins2 --help <command>'
50
- opts.separator ''
51
- opts.command 'version', 'Outputs the current version of Jenkins'
52
- opts.command 'prepare-for-shutdown', 'Stop executing new builds, so that the system can '\
53
- 'be eventually shut down safely.'
54
- opts.command 'cancel-shutdown', 'Cancel the effect of "prepare-for-shutshow" command.'
55
- opts.command 'wait-nodes-idle', 'Wait for all nodes to become idle. Is expected to be '\
56
- 'called after "prepare_for_shutdown", otherwise new builds will still be run.' do |cmd|
57
- cmd.on '-m', '--max-wait-minutes INT', Integer, 'Wait for INT minutes at most. '\
58
- 'Default 60' do |opt|
59
- @command_options[:max_wait_minutes] = opt
60
- end
61
- end
62
- opts.command 'offline-node', 'Stop using a node for performing builds temporarily, until '\
63
- 'the next "online-node" command.' do |cmd|
64
- cmd.on '-n', '--node NAME', 'Name of the node or empty for master' do |opt|
65
- @command_options[:node] = opt
66
- end
67
- cmd.on '-m', '--message MESSAGE', 'Record the note about why you are '\
68
- 'disconnecting this node' do |opt|
69
- @command_options[:message] = opt
70
- end
71
- end
72
- opts.command 'online-node', 'Resume using a node for performing builds, to cancel out '\
73
- 'the earlier "offline-node" command.' do |cmd|
74
- cmd.on '-n', '--node [NAME]', 'Name of the node or empty for master' do |opt|
75
- @command_options[:node] = opt
76
- end
77
- end
78
- opts.command 'connect-node', 'Connects a node, i.e. starts Jenkins slave on a node.' do |cmd|
79
- cmd.on '-n', '--node [NAME]', 'Name of the node or empty for master' do |opt|
80
- @command_options[:node] = opt
81
- end
82
- end
83
- opts.command 'create-node', 'Creates a new node from XML' do |cmd|
84
- cmd.on '-n', '--node NAME', 'Name of the new node' do |opt|
85
- @command_options[:node] = opt
86
- end
87
- cmd.on '-x', '--xml FILE', 'Path to XML configuration file' do |opt|
88
- @command_options[:xml] = IO.read opt
89
- end
90
- end
91
- opts.command 'delete-node', 'Deletes a node' do |cmd|
92
- cmd.on '-n', '--node NAME', 'Node name' do |opt|
93
- @command_options[:node] = opt
94
- end
95
- end
96
- opts.command 'create-credential', 'Creates credential.' do |cmd|
97
- cmd.on '-S', '--scope SCOPE', 'GLOBAL or SYSTEM scope' do |opt|
98
- @command_options[:scope] = opt
99
- end
100
- cmd.on '-i', '--id ID', 'Unique Id of credential. Will be generated automactically, if '\
101
- 'not provided' do |opt|
102
- @command_options[:id] = opt
103
- end
104
- cmd.on '-d', '--description DESC', 'Human readable text, what this credential is used for.' do |opt|
105
- @command_options[:description] = opt
106
- end
107
- cmd.on '-n', '--username NAME', 'Username for Username-Password or SSH credential' do |opt|
108
- @command_options[:username] = opt
109
- end
110
- cmd.on '-p', '--password PASS', 'Password in plain text for Username-Password credential' do |opt|
111
- @command_options[:password] = opt
112
- end
113
- cmd.on '-f', '--private-key FILE', 'Path to private key file for SSH credential' do |opt|
114
- @command_options[:private_key] = IO.read( opt ).gsub( "\n", "\\n" )
115
- end
116
- cmd.on '-P', '--passphrase PHRASE', 'Passphrase for the private key for SSH credential' do |opt|
117
- @command_options[:passphrase] = opt
118
- end
119
- cmd.on '-e', '--secret SECRET', 'Some secret text for Secret credential' do |opt|
120
- @command_options[:secret] = opt
121
- end
122
- cmd.on '-F', '--secret-file FILE', 'Path to secret file for Secret File credential' do |opt|
123
- @command_options[:filename] = File.basename opt
124
- @command_options[:content] = IO.read opt
125
- end
126
- end
127
- opts.command 'delete-credential', 'Deletes credential.' do |cmd|
128
- cmd.on '-i', '--id ID', 'Credential id' do |opt|
129
- @command_options[:id] = opt
130
- end
131
- end
132
- opts.command 'get-credential', 'Returns credential as json.' do |cmd|
133
- cmd.on '-i', '--id ID', 'Credential id' do |opt|
134
- @command_options[:id] = opt
135
- end
136
- end
137
- opts.command 'list-credentials', 'Lists all credentials.'
138
- opts.command 'disconnect-node', 'Disconnects a node.' do |cmd|
139
- cmd.on '-n', '--node [NAME]', 'Name of the node or empty for master' do |opt|
140
- @command_options[:node] = opt
141
- end
142
- cmd.on '-m', '--message MESSAGE', 'Reason, why the node is being disconnected.' do |opt|
143
- @command_options[:message] = opt
144
- end
145
- end
146
- opts.command 'wait-node-idle', 'Wait for the node to become idle. Make sure you run '\
147
- '"offline-node" first.' do |cmd|
148
- cmd.on '-n', '--node [NAME]', 'Name of the node or empty for master' do |opt|
149
- @command_options[:node] = opt
150
- end
151
- cmd.on '-m', '--max-wait-minutes INT', Integer, 'Wait for INT minutes at most. '\
152
- 'Default 60' do |opt|
153
- @command_options[:max_wait_minutes] = opt
154
- end
155
- end
156
- opts.command 'get-node-xml', 'Returns the node definition XML.' do |cmd|
157
- cmd.on '-n', '--node [NAME]', 'Name of the node or empty for master' do |opt|
158
- @command_options[:node] = opt
159
- end
160
- end
161
- opts.command 'get-node', 'Returns the node state.' do |cmd|
162
- cmd.on '-n', '--node [NAME]', 'Name of the node or empty for master' do |opt|
163
- @command_options[:node] = opt
164
- end
165
- end
166
- opts.command 'update-node', 'Updates the node definition XML from stdin or file.' do |cmd|
167
- cmd.on '-n', '--node [NAME]', 'Name of the node or empty for master' do |opt|
168
- @command_options[:node] = opt
169
- end
170
- cmd.on '-x', '--xml-config FILE', 'File to read definition from. Omit this to read from stdin' do |opt|
171
- @command_options[:xml_config] = IO.read( opt )
172
- end
173
- end
174
- opts.command 'build', 'Starts a build.' do |cmd|
175
- cmd.on '-j', '--job NAME', 'Name of the job' do |opt|
176
- @command_options[:job] = opt
177
- end
178
- cmd.on '-p', '--params KEY=VALUE[,KEY=VALUE...]', Array, 'Build parameters, where keys are'\
179
- ' names of variables' do |opt|
180
- @command_options[:params] = opt.collect{|i| i.split( '=', 2 ) }.to_h
181
- end
182
- end
183
- opts.command 'install-plugin', 'Installs a plugin from url or by short name. '\
184
- 'Provide either --url or --name.' do |cmd|
185
- cmd.on '-u', '--uri URI', ::URI, 'Uri to install plugin from.' do |opt|
186
- @command_options[:uri] = opt
187
- end
188
- cmd.on '-n', '--name SHORTNAME', 'Plugin short name (like thinBackup).' do |opt|
189
- @command_options[:name] = opt
190
- end
191
- end
192
- opts.command 'list-plugins', 'Lists installed plugins'
193
- end
194
- begin
195
- global.parse!( args )
196
- @global_options[:command] = global.command_name
197
- if @global_options[:config]
198
- from_file = JSON.parse( IO.read( @global_options[:config] ), symbolize_names: true )
199
- @global_options = from_file.merge( @global_options )
200
- end
201
- Log.init @log_options
202
- if @global_options[:help]
203
- Log.unknown { global.help }
204
- exit
205
- end
206
- raise OptionParser::MissingArgument, :command unless @global_options[:command]
207
- rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
208
- Log.fatal { e.message }
209
- Log.fatal { global.help }
210
- exit 1
211
- end
212
- Log.debug { "Options: #{@global_options}\nUnparsed args: #{ARGV}" }
213
- end
214
-
215
- def run
216
- jc = Client.new( @global_options )
217
- jc.send( @global_options[:command].gsub( '-', '_' ), @command_options )
218
- end
219
- end
220
- end
221
-
@@ -1,36 +0,0 @@
1
- require 'net/http'
2
-
3
- require_relative 'log'
4
-
5
- module Jenkins2
6
- module Try
7
- # Tries a block several times, if raised exception is <tt>Net::HTTPFatalError</tt>,
8
- # <tt>Errno::ECONNREFUSED</tt> or <tt>Net::ReadTimeout</tt>.
9
- # +retries+:: Number of retries.
10
- # +retry_delay+:: Seconds to sleep, before attempting next retry.
11
- # +&block+:: Code to run inside <tt>retry</tt> loop.
12
- #
13
- # Returns the result of a block, if it eventually succeeded or throws the exception, thown by
14
- # the block on last try.
15
- #
16
- # Note that this is both a method of module Try, so you can <tt>include Jenkins::Try</tt>
17
- # into your classes so they have a #try method, as well as a module method, so you can call it
18
- # directly as ::try().
19
- def try( retries: 3, retry_delay: 5, &block )
20
- yield
21
- rescue Errno::ECONNREFUSED, Net::HTTPFatalError, Net::ReadTimeout => e
22
- i ||= 0
23
- unless ( i += 1 ) == retries
24
- Log.warn { "Received error: #{e}." }
25
- Log.warn { "Retry request in #{retry_delay} seconds." }
26
- sleep retry_delay
27
- retry
28
- end
29
- Log.error { "Received error: #{e}." }
30
- Log.error { "Reached maximum number of retries (#{retries}). Give up." }
31
- raise e
32
- end
33
-
34
- module_function :try
35
- end
36
- end