docker-cake 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/docker-cake.gemspec +1 -1
- data/lib/docker_cake.rb +10 -1
- data/lib/docker_cake/cli.rb +45 -9
- data/lib/docker_cake/registry_api_client.rb +53 -25
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb308277d72c9e12243d29048577958bcb3143fb
|
4
|
+
data.tar.gz: 0bebde5d78dddf58e42f16a86266b05fecf0f030
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46f7c1023778cbb0d8aa626d6df4676f700d5b66ff55c0065f4f0fc2158f1efc4b3dd1410e9cb2bbd4c581e1dab80f9b1c7e72ba7c4fe0ee7f04564bcba61268
|
7
|
+
data.tar.gz: bc7334825b0e6c7c4a59ef374af115bfc76f80bc34d4bdc52fceebfba26202ec06a3e6b4525b7c8a64aebd2d19a1357a17d09408c011264608eb070968e6adac
|
data/docker-cake.gemspec
CHANGED
data/lib/docker_cake.rb
CHANGED
@@ -7,7 +7,7 @@ class DockerCake
|
|
7
7
|
attr_accessor :registry
|
8
8
|
|
9
9
|
def initialize(url: nil, user: nil, password: nil)
|
10
|
-
@registry ||= RegistryApiClient.new(user: user, password: password)
|
10
|
+
@registry ||= RegistryApiClient.new(user: user, password: password, url: url)
|
11
11
|
end
|
12
12
|
|
13
13
|
def repo_info(name, tag = 'latest')
|
@@ -17,10 +17,19 @@ class DockerCake
|
|
17
17
|
|
18
18
|
def compare_versions(repo_name, filter: /.+/, max: 10)
|
19
19
|
tags = registry.tags(repo_name, false)['tags']
|
20
|
+
|
21
|
+
if ENV['DEBUG']
|
22
|
+
puts "Found tags: #{tags.join(", ")}"
|
23
|
+
end
|
24
|
+
|
20
25
|
tags.select! {|t| t =~ filter}
|
21
26
|
|
22
27
|
selected_tags = tags.last(max)
|
23
28
|
|
29
|
+
if ENV['DEBUG']
|
30
|
+
puts "Analyzing #{selected_tags.size} tags: #{selected_tags.join(", ")}..."
|
31
|
+
end
|
32
|
+
|
24
33
|
manifests = {}
|
25
34
|
procs = selected_tags.map do |tag|
|
26
35
|
lambda { manifests[tag] = registry.manifest_layers(repo_name, tag) }
|
data/lib/docker_cake/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'socket'
|
2
3
|
require_relative '../docker_cake'
|
3
4
|
|
4
5
|
options = {}
|
@@ -49,13 +50,48 @@ if !repo || repo == ''
|
|
49
50
|
exit(1)
|
50
51
|
end
|
51
52
|
|
52
|
-
connect_options = {
|
53
|
-
|
53
|
+
connect_options = {
|
54
|
+
user: options[:user],
|
55
|
+
password: options[:password],
|
56
|
+
url: options[:url]
|
57
|
+
}
|
54
58
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
begin
|
60
|
+
if options[:layers]
|
61
|
+
DockerCake.new(connect_options).repo_info(repo, tag || 'latest')
|
62
|
+
else
|
63
|
+
opts = {}
|
64
|
+
opts[:max] = options[:max].to_i if options[:max]
|
65
|
+
DockerCake.new(connect_options).compare_versions(repo, opts)
|
66
|
+
end
|
67
|
+
rescue RegistryApiClient::RegistryAuthenticationException => e
|
68
|
+
puts "Permission denied, make sure username and password are correct"
|
69
|
+
puts "Server response: #{e.message}"
|
70
|
+
puts "Example:"
|
71
|
+
puts " docker-cake bob/private_repo -u bob -p ***"
|
72
|
+
exit 1
|
73
|
+
rescue RegistryApiClient::HTTP::NotFound => e
|
74
|
+
if options[:layers]
|
75
|
+
puts "Repository or tag not found"
|
76
|
+
else
|
77
|
+
puts "Repository not found"
|
78
|
+
end
|
79
|
+
puts "Example:"
|
80
|
+
puts " docker-cake library/ruby"
|
81
|
+
exit 1
|
82
|
+
rescue RegistryApiClient::JsonError => error
|
83
|
+
message = error.message.size > 300 ? error.message[0..300] + "..." : error.message
|
84
|
+
puts "Error parsing JSON response: #{message} (#{(error.parent || error).class})"
|
85
|
+
body = error.response.size > 300 ? error.response[0..300] + "..." : error.response
|
86
|
+
puts "Server response body: #{body}"
|
87
|
+
if ENV['DEBUG']
|
88
|
+
headers = error.response.headers.to_a.map {|pair| pair.join(": ")}.join("\n ")
|
89
|
+
puts "Headers:\n #{headers}"
|
90
|
+
end
|
91
|
+
exit 1
|
92
|
+
rescue SocketError => e
|
93
|
+
puts "Can not connect to registery:"
|
94
|
+
puts "#{e.class}: #{e.message}"
|
95
|
+
puts e.backtrace if ENV['DEBUG']
|
96
|
+
exit 1
|
97
|
+
end
|
@@ -26,6 +26,16 @@ class RegistryApiClient
|
|
26
26
|
class InvalidMethod < Exception
|
27
27
|
end
|
28
28
|
|
29
|
+
class JsonError < Exception
|
30
|
+
attr_reader :response
|
31
|
+
attr_reader :parent
|
32
|
+
def initialize(message, response = nil)
|
33
|
+
super(message)
|
34
|
+
@parent = message if message.is_a?(Exception)
|
35
|
+
@response = response
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
29
39
|
class Waiter
|
30
40
|
def initialize
|
31
41
|
@queue = Queue.new
|
@@ -70,6 +80,7 @@ class RegistryApiClient
|
|
70
80
|
# @option options [#to_s] :user User name for basic authentication
|
71
81
|
# @option options [#to_s] :password Password for basic authentication
|
72
82
|
def initialize(url: DEFAULT_REGISTRY, user: nil, password: nil)
|
83
|
+
url = url || DEFAULT_REGISTRY
|
73
84
|
@url = url
|
74
85
|
uri = URI.parse(url)
|
75
86
|
@base_uri = "#{uri.scheme}://#{uri.host}:#{uri.port}"
|
@@ -112,7 +123,11 @@ class RegistryApiClient
|
|
112
123
|
def tags(repo, withHashes = false)
|
113
124
|
response = http_get("/v2/#{repo}/tags/list")
|
114
125
|
# parse the response
|
115
|
-
resp =
|
126
|
+
resp = begin
|
127
|
+
JSON.parse(response)
|
128
|
+
rescue JSON::ParserError => e
|
129
|
+
raise JsonError.new(e, response)
|
130
|
+
end
|
116
131
|
# do we include the hashes?
|
117
132
|
if withHashes then
|
118
133
|
useGet = false
|
@@ -200,7 +215,9 @@ class RegistryApiClient
|
|
200
215
|
end
|
201
216
|
end
|
202
217
|
|
203
|
-
threads.
|
218
|
+
threads.each do |t|
|
219
|
+
t.alive? && t.join
|
220
|
+
end
|
204
221
|
|
205
222
|
if errors.size > 0
|
206
223
|
raise errors.first
|
@@ -232,9 +249,9 @@ class RegistryApiClient
|
|
232
249
|
else
|
233
250
|
return req_no_auth(type, url, stream: stream, manifest: manifest)
|
234
251
|
end
|
235
|
-
rescue SocketError => e
|
236
|
-
p e
|
237
|
-
raise RegistryUnknownException
|
252
|
+
# rescue SocketError => e
|
253
|
+
# p e
|
254
|
+
# raise RegistryUnknownException
|
238
255
|
rescue HTTP::Unauthorized => e
|
239
256
|
header = e.response.headers[:www_authenticate]
|
240
257
|
method = header.downcase.split(' ')[0]
|
@@ -279,11 +296,11 @@ class RegistryApiClient
|
|
279
296
|
headers: {Accept: manifest || @manifest_format},
|
280
297
|
block_response: block
|
281
298
|
)
|
282
|
-
rescue SocketError
|
283
|
-
raise RegistryUnknownException
|
284
|
-
rescue HTTP::Unauthorized
|
285
|
-
raise RegistryAuthenticationException
|
286
|
-
rescue MethodNotAllowed
|
299
|
+
# rescue SocketError
|
300
|
+
# raise RegistryUnknownException
|
301
|
+
rescue HTTP::Unauthorized => error
|
302
|
+
raise RegistryAuthenticationException.new(error)
|
303
|
+
rescue HTTP::MethodNotAllowed
|
287
304
|
raise InvalidMethod
|
288
305
|
end
|
289
306
|
return response
|
@@ -303,11 +320,11 @@ class RegistryApiClient
|
|
303
320
|
headers: {Authorization: 'Bearer ' + token, Accept: manifest || @manifest_format},
|
304
321
|
block_response: block
|
305
322
|
)
|
306
|
-
rescue SocketError
|
307
|
-
raise RegistryUnknownException
|
308
|
-
rescue HTTP::Unauthorized
|
309
|
-
raise RegistryAuthenticationException
|
310
|
-
rescue MethodNotAllowed
|
323
|
+
# rescue SocketError
|
324
|
+
# raise RegistryUnknownException
|
325
|
+
rescue HTTP::Unauthorized => e
|
326
|
+
raise RegistryAuthenticationException.new(e)
|
327
|
+
rescue HTTP::MethodNotAllowed
|
311
328
|
raise InvalidMethod
|
312
329
|
end
|
313
330
|
|
@@ -324,7 +341,12 @@ class RegistryApiClient
|
|
324
341
|
if AUTH_CACHE[scope].is_a?(String)
|
325
342
|
return AUTH_CACHE[scope]
|
326
343
|
elsif AUTH_CACHE[scope].is_a?(PubSub)
|
327
|
-
|
344
|
+
result = AUTH_CACHE[scope].wait
|
345
|
+
if result.is_a?(Exception)
|
346
|
+
raise result
|
347
|
+
else
|
348
|
+
return result
|
349
|
+
end
|
328
350
|
else
|
329
351
|
AUTH_CACHE[scope] = PubSub.new
|
330
352
|
end
|
@@ -343,9 +365,10 @@ class RegistryApiClient
|
|
343
365
|
user: @user,
|
344
366
|
password: @password
|
345
367
|
)
|
346
|
-
rescue HTTP::Unauthorized
|
368
|
+
rescue HTTP::Unauthorized => error
|
347
369
|
# bad authentication
|
348
|
-
|
370
|
+
AUTH_CACHE[scope].notify(error)
|
371
|
+
raise RegistryAuthenticationException.new(error)
|
349
372
|
end
|
350
373
|
# now save the web token
|
351
374
|
token = JSON.parse(response)["token"]
|
@@ -373,7 +396,7 @@ class RegistryApiClient
|
|
373
396
|
module HTTP
|
374
397
|
extend self
|
375
398
|
|
376
|
-
class
|
399
|
+
class ResponseError < Exception
|
377
400
|
attr_accessor :response
|
378
401
|
def initialize(message, response)
|
379
402
|
super(message)
|
@@ -381,12 +404,13 @@ class RegistryApiClient
|
|
381
404
|
end
|
382
405
|
end
|
383
406
|
|
384
|
-
class
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
407
|
+
class Unauthorized < ResponseError
|
408
|
+
end
|
409
|
+
|
410
|
+
class MethodNotAllowed < ResponseError
|
411
|
+
end
|
412
|
+
|
413
|
+
class NotFound < ResponseError
|
390
414
|
end
|
391
415
|
|
392
416
|
def execute(method:, url:, headers: {}, user: nil, password: nil, block_response: nil, body: nil, query: nil)
|
@@ -439,6 +463,10 @@ class RegistryApiClient
|
|
439
463
|
raise MethodNotAllowed.new(http_resp.body, response)
|
440
464
|
end
|
441
465
|
|
466
|
+
if http_resp.code.to_s == '404'
|
467
|
+
raise NotFound.new(http_resp.body, response)
|
468
|
+
end
|
469
|
+
|
442
470
|
return response
|
443
471
|
end
|
444
472
|
end
|