docker-cake 0.1 → 0.2
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 +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
|