1
|
module Service
|
2
|
class RegistryApi
|
3
|
DOCKER_HUB = 'https://index.docker.io/'.freeze
|
4
|
DEFAULTS = {
|
5
|
url: 'http://localhost:5000'.freeze,
|
6
|
connection: { omit_default_port: true,
|
7
|
headers: { "Content-Type" => "application/json" }
|
8
|
}
|
9
|
}
|
10
|
|
11
|
attr_accessor :config, :url
|
12
|
delegate :logger, :to => Rails
|
13
|
|
14
|
def initialize(params = {})
|
15
|
self.config = DEFAULTS.merge(params)
|
16
|
self.url = config[:url]
|
17
|
|
18
|
Docker.logger = logger if Rails.env.development? || Rails.env.test?
|
19
|
end
|
20
|
|
21
|
def connection
|
22
|
@connection ||= ::Docker::Connection.new(url, default_connection_options.merge(credentials))
|
23
|
end
|
24
|
|
25
|
def get(path, params = nil)
|
26
|
response = connection.get('/'.freeze, params,
|
27
|
connection.options.merge({ path: "#{path}" }))
|
28
|
response = parse_json(response)
|
29
|
response
|
30
|
end
|
31
|
|
32
|
|
33
|
|
34
|
def search(query)
|
35
|
get('/v1/search'.freeze, { q: query })
|
36
|
rescue => e
|
37
|
logger.warn "API v1 - Search failed #{e.backtrace}"
|
38
|
{ 'results' => catalog(query) }
|
39
|
end
|
40
|
|
41
|
|
42
|
def catalog(query)
|
43
|
get('/v2/_catalog'.freeze)['repositories'].select do |image|
|
44
|
image =~ /^#{query}/
|
45
|
end.map { |image_name| { 'name' => image_name } }
|
46
|
end
|
47
|
|
48
|
def tags(image_name, query = nil)
|
49
|
result = get_tags(image_name)
|
50
|
result = result.keys.map { |t| {'name' => t.to_s } } if result.is_a? Hash
|
51
|
result = filter_tags(result, query) if query
|
52
|
result
|
53
|
end
|
54
|
|
55
|
def ok?
|
56
|
get('/v1/'.freeze).match("Docker Registry API")
|
57
|
rescue => e
|
58
|
logger.warn "API v1 - Ping failed #{e.backtrace}"
|
59
|
get('/v2/'.freeze).is_a? Hash
|
60
|
end
|
61
|
|
62
|
def self.docker_hub
|
63
|
@@docker_hub ||= new(url: DOCKER_HUB)
|
64
|
end
|
65
|
|
66
|
private
|
67
|
|
68
|
def default_connection_options
|
69
|
@default_connection_options ||= DEFAULTS[:connection].tap do |defaults|
|
70
|
defaults[:ssl_verify_peer] = config.fetch(:verify_ssl, true)
|
71
|
end
|
72
|
end
|
73
|
|
74
|
def parse_json(string)
|
75
|
JSON.parse(string)
|
76
|
rescue => e
|
77
|
logger.warn "JSON parsing failed: #{e.backtrace}"
|
78
|
string
|
79
|
end
|
80
|
|
81
|
def get_tags(image_name)
|
82
|
get("/v1/repositories/#{image_name}/tags")
|
83
|
rescue => e
|
84
|
Foreman::Logging.exception("API v1 - Repository images request failed", e)
|
85
|
tags_v2(image_name)
|
86
|
end
|
87
|
|
88
|
def tags_v2(image_name)
|
89
|
get("/v2/#{image_name}/tags/list")['tags'].map { |tag| { 'name' => tag } }
|
90
|
rescue Docker::Error::NotFoundError
|
91
|
[]
|
92
|
rescue Excon::Error::Found => e
|
93
|
if e.response.status == 302
|
94
|
new_path = URI.parse(e.response.headers['Location']).path
|
95
|
get(new_path)['tags'].map { |tag| { 'name' => tag } }
|
96
|
else
|
97
|
::Foreman.exception "Could not load tags using v2", e
|
98
|
[]
|
99
|
end
|
100
|
end
|
101
|
|
102
|
def credentials
|
103
|
{ user: config.fetch(:user, nil),
|
104
|
password: config.fetch(:password, nil) }
|
105
|
end
|
106
|
|
107
|
def filter_tags(result, query)
|
108
|
result.select do |tag_name|
|
109
|
tag_name['name'] =~ /^#{query}/
|
110
|
end
|
111
|
end
|
112
|
end
|
113
|
end
|