jenkins2 0.1.0 → 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.
@@ -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