Project

General

Profile

Download (2.88 KB) Statistics
| Branch: | Tag: | Revision:

foreman-docker / app / models / service / registry_api.rb @ c69bcf98

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
    attr_accessor :config, :url
11
    delegate :logger, :to => Rails
12

    
13
    def initialize(params = {})
14
      self.config = DEFAULTS.merge(params)
15
      self.url = config[:url]
16

    
17
      Docker.logger = logger if Rails.env.development? || Rails.env.test?
18
    end
19

    
20
    def connection
21
      @connection ||= ::Docker::Connection.new(url, default_connection_options.merge(credentials))
22
    end
23

    
24
    def get(path, params = nil)
25
      response = connection.get('/'.freeze, params,
26
                                connection.options.merge({ path: "#{path}" }))
27
      response = parse_json(response)
28
      response
29
    end
30

    
31
    # Since the Registry API v2 does not support a search the v1 endpoint is used
32
    # Newer registries will fail, the v2 catalog endpoint is used
33
    def search(query)
34
      get('/v1/search'.freeze, { q: query })
35
    rescue => e
36
      logger.warn "API v1 - Search failed #{e.backtrace}"
37
      { 'results' => catalog(query) }
38
    end
39

    
40
    # Some Registries might have this endpoint not implemented/enabled
41
    def catalog(query)
42
      get('/v2/_catalog'.freeze)['repositories'].select do |image|
43
        image =~ /^#{query}/
44
      end.map { |image_name| { 'name' => image_name } }
45
    end
46

    
47
    def tags(image_name, query = nil)
48
      result = get_tags(image_name)
49
      result = result.keys.map { |t| {'name' => t.to_s } } if result.is_a? Hash
50
      result = filter_tags(result, query) if query
51
      result
52
    end
53

    
54
    def ok?
55
      get('/v1/'.freeze).match("Docker Registry API")
56
    rescue => e
57
      logger.warn "API v1 - Ping failed #{e.backtrace}"
58
      get('/v2/'.freeze).is_a? Hash
59
    end
60

    
61
    def self.docker_hub
62
      @@docker_hub ||= new(url: DOCKER_HUB)
63
    end
64

    
65
    private
66

    
67
    def default_connection_options
68
      @default_connection_options ||= DEFAULTS[:connection].tap do |defaults|
69
        defaults[:ssl_verify_peer] = config.fetch(:verify_ssl, true)
70
      end
71
    end
72

    
73
    def parse_json(string)
74
      JSON.parse(string)
75
    rescue => e
76
      logger.warn "JSON parsing failed: #{e.backtrace}"
77
      string
78
    end
79

    
80
    def get_tags(image_name)
81
      get("/v1/repositories/#{image_name}/tags")
82
    rescue => e
83
      logger.warn "API v1 - Repository images request failed #{e.backtrace}"
84
      tags_v2(image_name)
85
    end
86

    
87
    def tags_v2(image_name)
88
      get("/v2/#{image_name}/tags/list")['tags'].map { |tag| { 'name' => tag } }
89
    rescue Docker::Error::NotFoundError
90
      []
91
    end
92

    
93
    def credentials
94
      { user: config.fetch(:user, nil),
95
        password: config.fetch(:password, nil) }
96
    end
97

    
98
    def filter_tags(result, query)
99
      result.select do |tag_name|
100
        tag_name['name'] =~ /^#{query}/
101
      end
102
    end
103
  end
104
end