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.
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+
5
+ module Jenkins2
6
+ class CLI
7
+ class InstallPlugin < CLI
8
+ def self.description
9
+ 'Installs a plugin either from a file, an URL, standard input or from update center.'
10
+ end
11
+
12
+ private
13
+
14
+ def add_options
15
+ parser.on '-n', '--name SHORTNAME', 'Plugin short name (like thinBackup).' do |n|
16
+ options[:name] = n
17
+ end
18
+ parser.on '--source URI', 'If this points to a local file, it will be '\
19
+ 'installed. If this is an URL, the file will be downloaded and installed. If it '\
20
+ 'the "-" sting, the file will be read from standard input and "--name" must be '\
21
+ 'specified.' do |s|
22
+ options[:source] = s
23
+ end
24
+ end
25
+
26
+ def run
27
+ case options[:source]
28
+ when nil, ''
29
+ jc.plugins.install options[:name]
30
+ when '-'
31
+ jc.plugins.upload(ARGF.read, options[:name])
32
+ else
33
+ open(options[:source], 'rb') do |f|
34
+ jc.plugins.upload(f.read, options[:name] || File.basename(options[:source]))
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ class ListPlugins < CLI
41
+ def self.description
42
+ 'Lists all installed plugins.'
43
+ end
44
+
45
+ private
46
+
47
+ def run
48
+ jc.plugins(depth: 1).plugins.collect do |pl|
49
+ "#{pl.shortName} (#{pl.version})"
50
+ end.join("\n")
51
+ end
52
+ end
53
+
54
+ class UninstallPlugin < CLI
55
+ def self.description
56
+ 'Uninstalls a plugin.'
57
+ end
58
+
59
+ private
60
+
61
+ def add_options
62
+ parser.separator 'Mandatory arguments:'
63
+ parser.on '-n', '--name SHORTNAME', 'Plugin short name (like thinBackup).' do |n|
64
+ options[:name] = n
65
+ end
66
+ end
67
+
68
+ def mandatory_arguments
69
+ super + [:name]
70
+ end
71
+
72
+ def run
73
+ jc.plugins.plugin(options[:name]).uninstall
74
+ end
75
+ end
76
+
77
+ class ShowPlugin < CLI
78
+ def self.description
79
+ 'Show plugin info.'
80
+ end
81
+
82
+ private
83
+
84
+ def add_options
85
+ parser.separator 'Mandatory arguments:'
86
+ parser.on '-n', '--name SHORTNAME', 'Plugin short name (like thinBackup).' do |n|
87
+ options[:name] = n
88
+ end
89
+ end
90
+
91
+ def mandatory_arguments
92
+ super + [:name]
93
+ end
94
+
95
+ def run
96
+ pl = jc.plugins.plugin(options[:name]).subject
97
+ "#{pl.shortName} (#{pl.version}) - #{pl.longName}"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jenkins2
4
+ class CLI
5
+ class SafeRestart < CLI
6
+ def self.description
7
+ 'Safely restart Jenkins.'
8
+ end
9
+
10
+ private
11
+
12
+ def run
13
+ jc.restart
14
+ end
15
+ end
16
+
17
+ class Restart < CLI
18
+ def self.description
19
+ 'Restart Jenkins.'
20
+ end
21
+
22
+ private
23
+
24
+ def run
25
+ jc.restart!
26
+ end
27
+ end
28
+
29
+ class QuietDown < CLI
30
+ def self.description
31
+ 'Put Jenkins into the quiet mode, wait for existing builds to be completed.'
32
+ end
33
+
34
+ private
35
+
36
+ def run
37
+ jc.quiet_down
38
+ end
39
+ end
40
+
41
+ class CancelQuietDown < CLI
42
+ def self.description
43
+ 'Cancel previously issued quiet-down command.'
44
+ end
45
+
46
+ private
47
+
48
+ def run
49
+ jc.cancel_quiet_down
50
+ end
51
+ end
52
+
53
+ class Version < CLI
54
+ def self.description
55
+ 'Jenkins version.'
56
+ end
57
+
58
+ private
59
+
60
+ def run
61
+ jc.version
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jenkins2
4
+ class CLI
5
+ class WhoAmI < CLI
6
+ def self.description
7
+ 'Reports your credentials.'
8
+ end
9
+
10
+ def run
11
+ r = jc.me.subject
12
+ %w[id fullName description].collect{|p| "#{p}: #{r[p]}" }.join "\n"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jenkins2
4
+ class CLI
5
+ class CreateView < CLI
6
+ def self.description
7
+ 'Creates a new view by reading stdin as a XML configuration.'
8
+ end
9
+
10
+ private
11
+
12
+ def add_options
13
+ parser.separator 'Mandatory arguments:'
14
+ parser.on '-n', '--name NAME', 'Name of the view.' do |n|
15
+ options[:name] = n
16
+ end
17
+ end
18
+
19
+ def mandatory_arguments
20
+ super + [:name]
21
+ end
22
+
23
+ def run
24
+ jc.view(options[:name]).create($stdin.read)
25
+ end
26
+ end
27
+
28
+ class DeleteView < CLI
29
+ def self.description
30
+ 'Delete view(s).'
31
+ end
32
+
33
+ private
34
+
35
+ def add_options
36
+ parser.separator 'Mandatory arguments:'
37
+ parser.on '-n', '--name X,Y,..', Array, 'View names to delete.' do |n|
38
+ options[:name] = n
39
+ end
40
+ end
41
+
42
+ def mandatory_arguments
43
+ super + [:name]
44
+ end
45
+
46
+ def run
47
+ options[:name].all? do |name|
48
+ jc.view(name).delete
49
+ end
50
+ end
51
+ end
52
+
53
+ class GetView < CLI
54
+ def self.description
55
+ 'Dumps the view definition XML to stdout.'
56
+ end
57
+
58
+ private
59
+
60
+ def add_options
61
+ parser.separator 'Mandatory arguments:'
62
+ parser.on '-n', '--name NAME', 'Name of the view.' do |n|
63
+ options[:name] = n
64
+ end
65
+ end
66
+
67
+ def mandatory_arguments
68
+ super + [:name]
69
+ end
70
+
71
+ def run
72
+ jc.view(options[:name]).config_xml
73
+ end
74
+ end
75
+
76
+ class UpdateView < CLI
77
+ def self.description
78
+ 'Updates the view definition XML from stdin. The opposite of the get-view command.'
79
+ end
80
+
81
+ private
82
+
83
+ def add_options
84
+ parser.separator 'Mandatory arguments:'
85
+ parser.on '-n', '--name NAME', 'Name of the view.' do |n|
86
+ options[:name] = n
87
+ end
88
+ end
89
+
90
+ def mandatory_arguments
91
+ super + [:name]
92
+ end
93
+
94
+ def run
95
+ jc.view(options[:name]).update($stdin.read)
96
+ end
97
+ end
98
+
99
+ class AddJobToView < CLI
100
+ def self.description
101
+ 'Adds jobs to view.'
102
+ end
103
+
104
+ private
105
+
106
+ def add_options
107
+ parser.separator 'Mandatory arguments:'
108
+ parser.on '-n', '--name NAME', 'Name of the view.' do |n|
109
+ options[:name] = n
110
+ end
111
+ parser.on '-j', '--job X,Y,..', Array, 'Job name(s) to add.' do |j|
112
+ options[:job] = j
113
+ end
114
+ end
115
+
116
+ def mandatory_arguments
117
+ super + %i[name job]
118
+ end
119
+
120
+ def run
121
+ options[:job].all? do |job|
122
+ jc.view(options[:name]).add_job(job)
123
+ end
124
+ end
125
+ end
126
+
127
+ class RemoveJobFromView < CLI
128
+ def self.description
129
+ 'Removes jobs from view.'
130
+ end
131
+
132
+ private
133
+
134
+ def add_options
135
+ parser.separator 'Mandatory arguments:'
136
+ parser.on '-n', '--name NAME', 'Name of the view.' do |n|
137
+ options[:name] = n
138
+ end
139
+ parser.on '-j', '--job X,Y,..', Array, 'Job name(s) to remove.' do |j|
140
+ options[:job] = j
141
+ end
142
+ end
143
+
144
+ def mandatory_arguments
145
+ super + %i[name job]
146
+ end
147
+
148
+ def run
149
+ options[:job].all? do |job|
150
+ jc.view(options[:name]).remove_job(job)
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'openssl'
5
+ require 'json'
6
+
7
+ module Jenkins2
8
+ class Connection
9
+ # Creates a "connection" to Jenkins.
10
+ # Parameter:
11
+ # +server+:: Jenkins Server URL.
12
+ def initialize(url)
13
+ @server = url
14
+ @crumb = nil
15
+ end
16
+
17
+ # Add basic auth to existing connection. Returns self.
18
+ # Parameters:
19
+ # +user+:: Jenkins API user.
20
+ # +key+:: Jenkins API key.
21
+ def basic_auth(user, key)
22
+ @user, @key = user, key
23
+ self
24
+ end
25
+
26
+ def get_json(path, params={}, &block)
27
+ get(::File.join(path, 'api/json'), params, &block)
28
+ end
29
+
30
+ def get(path, params={}, &block)
31
+ api_request(Net::HTTP::Get, build_uri(path, params), &block)
32
+ end
33
+
34
+ def head(path, params={}, &block)
35
+ api_request(Net::HTTP::Head, build_uri(path, params), &block)
36
+ end
37
+
38
+ def post(path, body=nil, params={}, &block)
39
+ headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
40
+ api_request(Net::HTTP::Post, build_uri(path, params), body, headers, &block)
41
+ end
42
+
43
+ def build_uri(relative_or_absolute, params={})
44
+ result = ::URI.parse relative_or_absolute
45
+ result = ::URI.parse ::File.join(@server.to_s, relative_or_absolute) unless result.absolute?
46
+ result.query = ::URI.encode_www_form params
47
+ result
48
+ end
49
+
50
+ def api_request(method, uri, body=nil, headers=nil)
51
+ req = method.new(URI(uri), headers)
52
+ req.basic_auth @user, @key if @user and @key
53
+ req.body = body
54
+ yield req if block_given?
55
+ Net::HTTP.start(req.uri.hostname, req.uri.port,
56
+ use_ssl: req.uri.scheme == 'https', verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
57
+ begin
58
+ req[@crumb['crumbRequestField']] = @crumb['crumb'] if @crumb
59
+ Log.debug{ "Request uri: #{req.uri}" }
60
+ Log.debug{ "Request content_type: #{req.content_type}, body: #{req.body}" }
61
+ response = http.request req
62
+ handle_response response
63
+ rescue Jenkins2::NoValidCrumbMatcher
64
+ update_crumbs
65
+ retry
66
+ end
67
+ end
68
+ end
69
+
70
+ def update_crumbs
71
+ @crumb = JSON.parse(get_json('/crumbIssuer').body)
72
+ end
73
+
74
+ def handle_response(response)
75
+ Log.debug{ "Response: #{response.code}, #{response.body}" }
76
+ case response
77
+ when Net::HTTPNotFound
78
+ raise Jenkins2::NotFoundError, response
79
+ when Net::HTTPBadRequest
80
+ raise Jenkins2::BadRequestError, response
81
+ when Net::HTTPServiceUnavailable
82
+ raise Jenkins2::ServiceUnavailableError, response
83
+ when Net::HTTPClientError, Net::HTTPServerError # 4XX, 5XX
84
+ response.value
85
+ else
86
+ response
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jenkins2
4
+ class BadRequestError < Net::HTTPError
5
+ def initialize(res)
6
+ if res.body.nil? or res.body.empty?
7
+ super('', res)
8
+ elsif (match = res.body.match('<h1>Error</h1><p>(.*)</p>'))
9
+ super(match[1], res)
10
+ elsif (match = res.body.match('<h2>HTTP ERROR 404</h2>\n<p>(.*) Reason:'))
11
+ super(match[1], res)
12
+ end
13
+ end
14
+ end
15
+
16
+ class NotFoundError < Net::HTTPError
17
+ def initialize(res)
18
+ super(res.body.match('<h2>HTTP ERROR 404</h2>\n<p>(.*) Reason:')[1], res)
19
+ end
20
+ end
21
+
22
+ class ServiceUnavailableError < Net::HTTPError
23
+ def initialize(res)
24
+ if res.body.nil? or res.body.empty?
25
+ super('', res)
26
+ else
27
+ super(res.body.match('<h1[^>]*>\n\s+([^<]+)')[1], res)
28
+ end
29
+ end
30
+ end
31
+
32
+ class NoValidCrumbMatcher
33
+ def self.===(exception)
34
+ exception.message == '403 "No valid crumb was included in the request"'
35
+ end
36
+ end
37
+ end