class DockerRegistry2::Registry
Public Class Methods
new(uri, options = {})
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 @option options [#to_s] :open_timeout Time to wait for a connection with a registry.
It is ignored if http_options[:open_timeout] is also specified.
@option options [#to_s] :read_timeout Time to wait for data from a registry.
It is ignored if http_options[:read_timeout] is also specified.
@option options [Hash] :http_options Extra options for RestClient::Request.execute.
# File lib/registry/registry.rb, line 15 def initialize(uri, options = {}) @uri = URI.parse(uri) @base_uri = "#{@uri.scheme}://#{@uri.host}:#{@uri.port}" @user = options[:user] @password = options[:password] @http_options = options[:http_options] || {} @http_options[:open_timeout] ||= options[:open_timeout] || 2 @http_options[:read_timeout] ||= options[:read_timeout] || 5 end
Public Instance Methods
_pull_v1(repo, manifest, dir)
click to toggle source
# File lib/registry/registry.rb, line 174 def _pull_v1(repo, manifest, dir) # make sure the directory exists FileUtils.mkdir_p dir return false unless manifest['schemaVersion'] == 1 # pull each of the layers manifest['fsLayers'].each do |layer| # define path of file to save layer in layer_file = "#{dir}/#{layer['blobSum']}" # skip layer if we already got it next if File.file? layer_file # download layer # puts "getting layer (v1) #{layer['blobSum']}" blob(repo, layer['blobSum'], layer_file) # return layer file layer_file end end
_pull_v2(repo, manifest, dir)
click to toggle source
# File lib/registry/registry.rb, line 157 def _pull_v2(repo, manifest, dir) # make sure the directory exists FileUtils.mkdir_p dir return false unless manifest['schemaVersion'] == 2 # pull each of the layers manifest['layers'].each do |layer| # define path of file to save layer in layer_file = "#{dir}/#{layer['digest']}" # skip layer if we already got it next if File.file? layer_file # download layer # puts "getting layer (v2) #{layer['digest']}" blob(repo, layer['digest'], layer_file) layer_file end end
blob(repo, digest, outpath=nil)
click to toggle source
# File lib/registry/registry.rb, line 114 def blob(repo, digest, outpath=nil) blob_url = "/v2/#{repo}/blobs/#{digest}" if outpath.nil? response = doget(blob_url) DockerRegistry2::Blob.new(response.headers, response.body) else File.open(outpath, 'w') do |fd| doreq('get', blob_url, fd) end outpath end end
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/registry/registry.rb, line 210 def blob_size(repo,blobSum) response = dohead "/v2/#{repo}/blobs/#{blobSum}" Integer(response.headers[:content_length],10) end
copy(repo,tag,newregistry,newrepo,newtag)
click to toggle source
# File lib/registry/registry.rb, line 205 def copy(repo,tag,newregistry,newrepo,newtag) end
digest(repo, tag)
click to toggle source
# File lib/registry/registry.rb, line 128 def digest(repo, tag) tag_path = "/v2/#{repo}/manifests/#{tag}" dohead(tag_path).headers[:docker_content_digest] rescue DockerRegistry2::InvalidMethod # Pre-2.3.0 registries didn't support manifest HEAD requests doget(tag_path).headers[:docker_content_digest] end
dodelete(url)
click to toggle source
# File lib/registry/registry.rb, line 33 def dodelete(url) return doreq "delete", url end
doget(url)
click to toggle source
# File lib/registry/registry.rb, line 25 def doget(url) return doreq "get", url end
dohead(url)
click to toggle source
# File lib/registry/registry.rb, line 37 def dohead(url) return doreq "head", url end
doput(url,payload=nil)
click to toggle source
# File lib/registry/registry.rb, line 29 def doput(url,payload=nil) return doreq "put", url, nil, payload end
last(header)
click to toggle source
# File lib/registry/registry.rb, line 215 def last(header) last='' parts = header.split(',') links = Hash.new # Parse each part into a named link parts.each do |part, index| section = part.split(';') url = section[0][/<(.*)>/,1] name = section[1][/rel="(.*)"/,1].to_sym links[name] = url end if links[:next] query=URI(links[:next]).query last=URI::decode_www_form(query).to_h["last"] end last end
manifest(repo,tag)
click to toggle source
# File lib/registry/registry.rb, line 104 def manifest(repo,tag) # first get the manifest response = doget "/v2/#{repo}/manifests/#{tag}" parsed = JSON.parse response.body manifest = DockerRegistry2::Manifest[parsed] manifest.body = response.body manifest.headers = response.headers manifest end
manifest_sum(manifest)
click to toggle source
# File lib/registry/registry.rb, line 235 def manifest_sum(manifest) size = 0 manifest["layers"].each { |layer| size += layer["size"] } size end
ping()
click to toggle source
# File lib/registry/registry.rb, line 41 def ping doget '/v2/' end
pull(repo, tag, dir)
click to toggle source
# File lib/registry/registry.rb, line 143 def pull(repo, tag, dir) # make sure the directory exists FileUtils.mkdir_p dir # get the manifest m = manifest repo, tag # puts "pulling #{repo}:#{tag} into #{dir}" # manifest can contain multiple manifests one for each API version downloaded_layers = [] downloaded_layers += _pull_v2(repo, m, dir) if m['schemaVersion'] == 2 downloaded_layers += _pull_v1(repo, m, dir) if m['schemaVersion'] == 1 # return downloaded_layers downloaded_layers end
push(manifest,dir)
click to toggle source
# File lib/registry/registry.rb, line 192 def push(manifest,dir) end
rmtag(image, tag)
click to toggle source
# File lib/registry/registry.rb, line 136 def rmtag(image, tag) # TODO: Need full response back. Rewrite other manifests() calls without JSON? reference = doget("/v2/#{image}/manifests/#{tag}").headers[:docker_content_digest] return dodelete("/v2/#{image}/manifests/#{reference}").code end
search(query = '')
click to toggle source
# File lib/registry/registry.rb, line 45 def search(query = '') response = doget "/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
tag(repo,tag,newrepo,newtag)
click to toggle source
# File lib/registry/registry.rb, line 195 def tag(repo,tag,newrepo,newtag) manifest = manifest(repo, tag) if manifest['schemaVersion'] == 2 doput "/v2/#{newrepo}/manifests/#{newtag}", manifest.to_json else raise DockerRegistry2::RegistryVersionException end end
Private Instance Methods
authenticate_bearer(header)
click to toggle source
# File lib/registry/registry.rb, line 333 def authenticate_bearer(header) # get the parts we need target = split_auth_header(header) # 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 = RestClient::Request.execute(@http_options.merge( method: :get, url: uri.to_s, headers: {params: target[:params]}, user: @user, password: @password, )) rescue RestClient::Unauthorized, RestClient::Forbidden # bad authentication raise DockerRegistry2::RegistryAuthenticationException rescue RestClient::NotFound => error raise DockerRegistry2::NotFound, error end # now save the web token result = JSON.parse(response) return result["token"] || result["access_token"] end
do_basic_req(type, url, stream=nil, payload=nil)
click to toggle source
# File lib/registry/registry.rb, line 277 def do_basic_req(type, url, stream=nil, payload=nil) begin block = stream.nil? ? nil : proc { |response| response.read_body do |chunk| stream.write chunk end } response = RestClient::Request.execute(@http_options.merge( method: type, url: @base_uri+url, user: @user, password: @password, headers: headers(payload: payload), block_response: block, payload: payload )) rescue SocketError raise DockerRegistry2::RegistryUnknownException rescue RestClient::Unauthorized raise DockerRegistry2::RegistryAuthenticationException rescue RestClient::MethodNotAllowed raise DockerRegistry2::InvalidMethod rescue RestClient::NotFound => error raise DockerRegistry2::NotFound, error end return response end
do_bearer_req(type, url, header, stream=false, payload=nil)
click to toggle source
# File lib/registry/registry.rb, line 305 def do_bearer_req(type, url, header, stream=false, payload=nil) token = authenticate_bearer(header) begin block = stream.nil? ? nil : proc { |response| response.read_body do |chunk| stream.write chunk end } response = RestClient::Request.execute(@http_options.merge( method: type, url: @base_uri+url, headers: headers(payload: payload, bearer_token: token), block_response: block, payload: payload )) rescue SocketError raise DockerRegistry2::RegistryUnknownException rescue RestClient::Unauthorized raise DockerRegistry2::RegistryAuthenticationException rescue RestClient::MethodNotAllowed raise DockerRegistry2::InvalidMethod rescue RestClient::NotFound => error raise DockerRegistry2::NotFound, error end return response end
doreq(type,url,stream=nil,payload=nil)
click to toggle source
# File lib/registry/registry.rb, line 244 def doreq(type,url,stream=nil,payload=nil) begin block = stream.nil? ? nil : proc { |response| response.read_body do |chunk| stream.write chunk end } response = RestClient::Request.execute(@http_options.merge( method: type, url: @base_uri+url, headers: headers(payload: payload), block_response: block, payload: payload )) rescue SocketError raise DockerRegistry2::RegistryUnknownException rescue RestClient::NotFound => error raise DockerRegistry2::NotFound, error rescue RestClient::Unauthorized => e header = e.response.headers[:www_authenticate] method = header.downcase.split(' ')[0] case method when 'basic' response = do_basic_req(type, url, stream, payload) when 'bearer' response = do_bearer_req(type, url, header, stream, payload) else raise DockerRegistry2::RegistryUnknownException end end return response end
headers(payload: nil, bearer_token: nil)
click to toggle source
# File lib/registry/registry.rb, line 374 def headers(payload: nil, bearer_token: nil) headers={} headers['Authorization']="Bearer #{bearer_token}" unless bearer_token.nil? headers['Accept']='application/vnd.docker.distribution.manifest.v2+json, application/json' if payload.nil? headers['Content-Type']='application/vnd.docker.distribution.manifest.v2+json' unless payload.nil? headers end
split_auth_header(header = '')
click to toggle source
# File lib/registry/registry.rb, line 360 def split_auth_header(header = '') h = Hash.new h = {params: {}} header.scan(/([\w]+)\=\"([^"]+)\"/) do |entry| case entry[0] when 'realm' h[:realm] = entry[1] else h[:params][entry[0]] = entry[1] end end h end