class RegistryApiClient
Constants
- AUTH_CACHE
- DEFAULT_MANIFEST
- DEFAULT_REGISTRY
- FAT_MANIFEST
Public Class Methods
new(url: DEFAULT_REGISTRY, user: nil, password: nil)
click to toggle source
@param [#to_s] base_uri Docker registry base URI @param [Hash] options Client options @option options [#to_s] :user User name for basic authentication @option options [#to_s] :password Password for basic authentication
# File lib/docker_cake/registry_api_client.rb, line 82 def initialize(url: DEFAULT_REGISTRY, user: nil, password: nil) url = url || DEFAULT_REGISTRY @url = url uri = URI.parse(url) @base_uri = "#{uri.scheme}://#{uri.host}:#{uri.port}" @user = user @password = password @manifest_format = "application/vnd.docker.distribution.manifest.v2+json" #@manifest_format = "application/vnd.docker.distribution.manifest.list.v2+json" #@manifest_format = "application/vnd.docker.container.image.v1+json" # make a ping connection #ping end
Public Instance Methods
blob_size(repo, blobSum)
click to toggle source
gets the size of a particular blob, given the repo and the content-addressable hash usually unneeded, since manifest includes it
# File lib/docker_cake/registry_api_client.rb, line 231 def blob_size(repo, blobSum) response = http_head("/v2/#{repo}/blobs/#{blobSum}") Integer(response.headers[:content_length], 10) end
http_delete(url)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 100 def http_delete(url) http_req("delete", url) end
http_get(url, manifest: nil, auth: nil, auth_header: nil)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 96 def http_get(url, manifest: nil, auth: nil, auth_header: nil) http_req("get", url, manifest: manifest, auth: auth, auth_header: auth_header) end
http_head(url)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 104 def http_head(url) http_req("head", url) end
in_parallel(procs = {})
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 196 def in_parallel(procs = {}) threads = [] result = {} errors = [] procs.each do |key, fun| # handle array if fun == nil && key.is_a?(Proc) fun = key key = result.size end result[key] = nil threads << Thread.new do begin result[key] = fun.call rescue => error puts "#{error.class}: #{error.message}" puts error.backtrace errors << error end end end threads.each do |t| t.alive? && t.join end if errors.size > 0 raise errors.first end result end
manifest(repo, tag, manifest: nil)
click to toggle source
combines small output and fat output to get layer names and sizes
# File lib/docker_cake/registry_api_client.rb, line 155 def manifest(repo, tag, manifest: nil) if @url == DEFAULT_REGISTRY auth_header = %{Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:#{repo}:pull"} end JSON.parse(http_get("/v2/#{repo}/manifests/#{tag}", manifest: manifest, auth: :bearer, auth_header: auth_header)) end
manifest_layers(repo, tag)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 162 def manifest_layers(repo, tag) basic_response = nil fat_response = nil resp = in_parallel( basic: lambda { manifest(repo, tag) }, fat: lambda { manifest(repo, tag, manifest: DEFAULT_MANIFEST) } ) unless resp[:basic]['layers'] puts "Strange response" p resp[:basic] end basic = resp[:basic]['layers'] || [] fat_response = resp[:fat] result = [] fat_response['history'].each_with_index do |info, index| result[index] = JSON.parse(info['v1Compatibility']) result[index]['blobSum'] = fat_response['fsLayers'][index]['blobSum'] result[index]['size'] = basic.detect do |layer| layer['digest'] == result[index]['blobSum'] end result[index]['size'] = result[index]['size']['size'] if result[index]['size'] end # require 'irb' # binding.irb result.reverse end
manifest_sum(manifest)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 236 def manifest_sum(manifest) size = 0 manifest["layers"].each do |layer| size += layer["size"] end size end
ping()
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 108 def ping response = http_get('/v2/') end
search(query = '')
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 112 def search(query = '') response = http_get "/v2/_catalog" # parse the response repos = JSON.parse(response)["repositories"] if query.strip.length > 0 re = Regexp.new query repos = repos.find_all {|e| re =~ e } end return repos end
Private Instance Methods
authenticate_bearer(header)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 336 def authenticate_bearer(header) # get the parts we need target = split_auth_header(header) scope = target[:params][:scope] if AUTH_CACHE[scope].is_a?(String) return AUTH_CACHE[scope] elsif AUTH_CACHE[scope].is_a?(PubSub) result = AUTH_CACHE[scope].wait if result.is_a?(Exception) raise result else return result end else AUTH_CACHE[scope] = PubSub.new end # did we have a username and password? if defined? @user and @user.to_s.strip.length != 0 target[:params][:account] = @user end # authenticate against the realm uri = URI.parse(target[:realm]) begin response = HTTP.execute( method: :get, url: uri.to_s, query: target[:params], user: @user, password: @password ) rescue HTTP::Unauthorized => error # bad authentication AUTH_CACHE[scope].notify(error) raise RegistryAuthenticationException.new(error) end # now save the web token token = JSON.parse(response)["token"] AUTH_CACHE[scope].notify(token) AUTH_CACHE[scope] = token return token end
do_basic_req(type, url, stream: nil, manifest: nil)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 284 def do_basic_req(type, url, stream: nil, manifest: nil) begin block = stream.nil? ? nil : proc { |response| response.read_body do |chunk| stream.write chunk end } response = HTTP.execute( method: type, url: @base_uri + url, user: @user, password: @password, headers: {Accept: manifest || @manifest_format}, block_response: block ) # rescue SocketError # raise RegistryUnknownException rescue HTTP::Unauthorized => error raise RegistryAuthenticationException.new(error) rescue HTTP::MethodNotAllowed raise InvalidMethod end return response end
do_bearer_req(type, url, header, stream: false, manifest: nil)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 309 def do_bearer_req(type, url, header, stream: false, manifest: nil) token = authenticate_bearer(header) begin block = stream.nil? ? nil : proc { |response| response.read_body do |chunk| stream.write chunk end } response = HTTP.execute( method: type, url: @base_uri + url, headers: {Authorization: 'Bearer ' + token, Accept: manifest || @manifest_format}, block_response: block ) # rescue SocketError # raise RegistryUnknownException rescue HTTP::Unauthorized => e raise RegistryAuthenticationException.new(e) rescue HTTP::MethodNotAllowed raise InvalidMethod end return response end
http_req(type, url, stream: nil, manifest: nil, auth: nil, auth_header: nil)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 245 def http_req(type, url, stream: nil, manifest: nil, auth: nil, auth_header: nil) begin if auth == :bearer && auth_header return do_bearer_req(type, url, auth_header, stream: stream, manifest: manifest) else return req_no_auth(type, url, stream: stream, manifest: manifest) end # rescue SocketError => e # p e # raise RegistryUnknownException rescue HTTP::Unauthorized => e header = e.response.headers[:www_authenticate] method = header.downcase.split(' ')[0] case method when 'basic' response = do_basic_req(type, url, stream: stream, manifest: manifest) when 'bearer' response = do_bearer_req(type, url, header, stream: stream, manifest: manifest) else raise RegistryUnknownException end end return response end
req_no_auth(type, url, stream: nil, manifest: nil)
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 270 def req_no_auth(type, url, stream: nil, manifest: nil) block = stream.nil? ? nil : proc do |response| response.read_body do |chunk| stream.write chunk end end response = HTTP.execute( method: type, url: @base_uri + url, headers: {Accept: manifest || @manifest_format}, block_response: block ) end
split_auth_header(header = '')
click to toggle source
# File lib/docker_cake/registry_api_client.rb, line 380 def split_auth_header(header = '') h = Hash.new h = {params: {}} header.split(/[\s,]+/).each {|entry| p = entry.split('=') case p[0] when 'Bearer' when 'realm' h[:realm] = p[1].gsub(/(^\"|\"$)/,'') else h[:params][p[0]] = p[1].gsub(/(^\"|\"$)/,'') end } h end