docker_registry2 1.12.0 → 1.13.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 869a69624555c180f16132300dfc203787a4e3ffd616f49f26ec7f199b469f1a
4
- data.tar.gz: cab7c1746af1ef6956726f42524da542f2ebf9a2fe4a56cc793148aa04a285d4
3
+ metadata.gz: 7d2c4b1b61074554ed7e15a5d3c662ebeaae19594cb8c4e3e3ba3dc78e67f0c5
4
+ data.tar.gz: a1dea179ace034fff0914bbc2a1eb6bcd0dd24cd274f01379a6ffd8215e56c7d
5
5
  SHA512:
6
- metadata.gz: 953669f5e94fef460fe7a3d866964aa76c87a29b2e2285ecb9a85a7058c43317ea1a683aa96e4c23bda450f48805edbc5c681f601ce00a81ae1e93020f6e02c0
7
- data.tar.gz: 02fbf888184e024bdf49a62b8b2e4161ae02d1df7f265bbf93aa5e4244fde50221e4fd93e085c291f4333c2ba691ffcc469d016832091c8cb3a286bea39978ba
6
+ metadata.gz: 7778fc311ce302caf634887a0801490043e7550d819dde8926312b0496134736dfeec474b045ecc5e74a7f29b7195a5977c52b530496edf6a54d187be0ce039e
7
+ data.tar.gz: 7ff181d7d3b7fe9ba0f87e2cdf342044a1e4442e55fe760ea5cf351994f41c0bf5d6316b33d1e920533b5ee7032dcb10a4230b5ca5d469dbedbe47919eb4063d
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'registry/version'
5
6
 
@@ -7,24 +8,29 @@ Gem::Specification.new do |spec|
7
8
  spec.name = 'docker_registry2'
8
9
  spec.version = DockerRegistry2::VERSION
9
10
  spec.authors = [
10
- 'Avi Deitcher https://github.com/deitch',
11
- 'Jonathan Hurter https://github.com/johnsudaar',
12
- 'Dmitry Fleytman https://github.com/dmitryfleytman',
13
- 'Grey Baker https://github.com/greysteil'
14
- ]
11
+ 'Avi Deitcher https://github.com/deitch',
12
+ 'Jonathan Hurter https://github.com/johnsudaar',
13
+ 'Dmitry Fleytman https://github.com/dmitryfleytman',
14
+ 'Grey Baker https://github.com/greysteil'
15
+ ]
15
16
  spec.summary = 'Docker v2 registry HTTP API client'
16
17
  spec.description = 'Docker v2 registry HTTP API client with support for token authentication'
17
18
  spec.homepage = 'https://github.com/deitch/docker_registry2'
18
19
  spec.license = 'MIT'
19
20
 
20
- spec.files = %w{README.md} + Dir.glob("*.gemspec") + Dir.glob("{lib}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
21
- spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
22
- spec.test_files = spec.files.grep(/^(test|spec|features)\//)
21
+ spec.files = %w[README.md] + Dir.glob('*.gemspec') + Dir.glob('{lib}/**/*', File::FNM_DOTMATCH).reject do |f|
22
+ File.directory?(f)
23
+ end
24
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
26
  spec.require_paths = ['lib']
24
27
 
25
28
  spec.add_development_dependency 'bundler'
26
29
  spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rspec', '~> 3'
27
31
  spec.add_development_dependency 'rubocop', '>= 0.26.0'
32
+ spec.add_development_dependency 'vcr', '~> 6'
33
+ spec.add_development_dependency 'webmock'
28
34
 
29
35
  spec.add_dependency 'rest-client', '>= 1.8.0'
30
36
  end
@@ -1,13 +1,14 @@
1
- require File.dirname(__FILE__) + '/registry/version'
2
- require File.dirname(__FILE__) + '/registry/registry'
3
- require File.dirname(__FILE__) + '/registry/exceptions'
4
- require File.dirname(__FILE__) + '/registry/manifest'
5
- require File.dirname(__FILE__) + '/registry/blob'
1
+ # frozen_string_literal: true
6
2
 
3
+ require "#{File.dirname(__FILE__)}/registry/version"
4
+ require "#{File.dirname(__FILE__)}/registry/registry"
5
+ require "#{File.dirname(__FILE__)}/registry/exceptions"
6
+ require "#{File.dirname(__FILE__)}/registry/manifest"
7
+ require "#{File.dirname(__FILE__)}/registry/blob"
7
8
 
8
9
  module DockerRegistry2
9
- def self.connect(uri="https://registry.hub.docker.com",opts={})
10
- @reg = DockerRegistry2::Registry.new(uri,opts)
10
+ def self.connect(uri = 'https://registry.hub.docker.com', opts = {})
11
+ @reg = DockerRegistry2::Registry.new(uri, opts)
11
12
  end
12
13
 
13
14
  def self.search(query = '')
@@ -18,7 +19,7 @@ module DockerRegistry2
18
19
  @reg.tags(repository)
19
20
  end
20
21
 
21
- def self.manifest(repository,tag)
22
- @reg.manifest(repository,tag)
22
+ def self.manifest(repository, tag)
23
+ @reg.manifest(repository, tag)
23
24
  end
24
25
  end
data/lib/registry/blob.rb CHANGED
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DockerRegistry2
2
4
  class Blob
3
5
  attr_reader :body, :headers
4
-
6
+
5
7
  def initialize(headers, body)
6
8
  @headers = headers
7
9
  @body = body
@@ -1,32 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DockerRegistry2
2
4
  class Exception < RuntimeError
3
-
4
5
  end
5
-
6
- class RegistryAuthenticationException < Exception
6
+
7
+ class RegistryAuthenticationException < StandardError
7
8
  end
8
9
 
9
- class RegistryAuthorizationException < Exception
10
+ class RegistryAuthorizationException < StandardError
10
11
  end
11
12
 
12
- class RegistryUnknownException < Exception
13
+ class RegistryUnknownException < StandardError
13
14
  end
14
15
 
15
- class RegistrySSLException < Exception
16
+ class RegistrySSLException < StandardError
16
17
  end
17
18
 
18
- class RegistryVersionException < Exception
19
+ class RegistryVersionException < StandardError
19
20
  end
20
-
21
- class ReauthenticatedException < Exception
21
+
22
+ class ReauthenticatedException < StandardError
22
23
  end
23
-
24
- class UnknownRegistryException < Exception
24
+
25
+ class UnknownRegistryException < StandardError
25
26
  end
26
27
 
27
- class NotFound < Exception
28
+ class NotFound < StandardError
28
29
  end
29
30
 
30
- class InvalidMethod < Exception
31
+ class InvalidMethod < StandardError
31
32
  end
32
- end
33
+ end
@@ -1,9 +1,8 @@
1
- module DockerRegistry2
1
+ # frozen_string_literal: true
2
2
 
3
+ module DockerRegistry2
4
+ # Manifest class represents a manfiest or index in an OCI registry
3
5
  class Manifest < Hash
4
6
  attr_accessor :body, :headers
5
- def initialize
6
- super
7
- end
8
7
  end
9
8
  end
@@ -1,288 +1,295 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
  require 'rest-client'
3
5
  require 'json'
4
6
 
5
- class DockerRegistry2::Registry
6
- # @param [#to_s] base_uri Docker registry base URI
7
- # @param [Hash] options Client options
8
- # @option options [#to_s] :user User name for basic authentication
9
- # @option options [#to_s] :password Password for basic authentication
10
- # @option options [#to_s] :open_timeout Time to wait for a connection with a registry.
11
- # It is ignored if http_options[:open_timeout] is also specified.
12
- # @option options [#to_s] :read_timeout Time to wait for data from a registry.
13
- # It is ignored if http_options[:read_timeout] is also specified.
14
- # @option options [Hash] :http_options Extra options for RestClient::Request.execute.
15
- def initialize(uri, options = {})
16
- @uri = URI.parse(uri)
17
- @base_uri = "#{@uri.scheme}://#{@uri.host}:#{@uri.port}"
18
- @user = options[:user]
19
- @password = options[:password]
20
- @http_options = options[:http_options] || {}
21
- @http_options[:open_timeout] ||= options[:open_timeout] || 2
22
- @http_options[:read_timeout] ||= options[:read_timeout] || 5
23
- end
7
+ module DockerRegistry2
8
+ class Registry
9
+ # @param [#to_s] base_uri Docker registry base URI
10
+ # @param [Hash] options Client options
11
+ # @option options [#to_s] :user User name for basic authentication
12
+ # @option options [#to_s] :password Password for basic authentication
13
+ # @option options [#to_s] :open_timeout Time to wait for a connection with a registry.
14
+ # It is ignored if http_options[:open_timeout] is also specified.
15
+ # @option options [#to_s] :read_timeout Time to wait for data from a registry.
16
+ # It is ignored if http_options[:read_timeout] is also specified.
17
+ # @option options [Hash] :http_options Extra options for RestClient::Request.execute.
18
+ def initialize(uri, options = {})
19
+ @uri = URI.parse(uri)
20
+ @base_uri = "#{@uri.scheme}://#{@uri.host}:#{@uri.port}"
21
+ @user = options[:user]
22
+ @password = options[:password]
23
+ @http_options = options[:http_options] || {}
24
+ @http_options[:open_timeout] ||= options[:open_timeout] || 2
25
+ @http_options[:read_timeout] ||= options[:read_timeout] || 5
26
+ end
24
27
 
25
- def doget(url)
26
- return doreq "get", url
27
- end
28
+ def doget(url)
29
+ doreq 'get', url
30
+ end
28
31
 
29
- def doput(url,payload=nil)
30
- return doreq "put", url, nil, payload
31
- end
32
+ def doput(url, payload = nil)
33
+ doreq 'put', url, nil, payload
34
+ end
32
35
 
33
- def dodelete(url)
34
- return doreq "delete", url
35
- end
36
+ def dodelete(url)
37
+ doreq 'delete', url
38
+ end
36
39
 
37
- def dohead(url)
38
- return doreq "head", url
39
- end
40
+ def dohead(url)
41
+ doreq 'head', url
42
+ end
43
+
44
+ # When a result set is too large, the Docker registry returns only the first items and adds a Link header in the
45
+ # response with the URL of the next page. See <https://docs.docker.com/registry/spec/api/#pagination>. This method
46
+ # iterates over the pages and calls the given block with each response.
47
+ def paginate_doget(url)
48
+ while url
49
+ response = doget(url)
50
+ yield response
40
51
 
41
- # When a result set is too large, the Docker registry returns only the first items and adds a Link header in the
42
- # response with the URL of the next page. See <https://docs.docker.com/registry/spec/api/#pagination>. This method
43
- # iterates over the pages and calls the given block with each response.
44
- def paginate_doget(url)
45
- while url
46
- response = doget(url)
47
- yield response
52
+ break unless (link = response.headers[:link])
48
53
 
49
- if (link = response.headers[:link])
50
54
  url = parse_link_header(link)[:next]
51
- else
52
- break
55
+
53
56
  end
54
57
  end
55
- end
56
58
 
57
- def search(query = '')
58
- all_repos = []
59
- paginate_doget "/v2/_catalog" do |response|
60
- # parse the response
61
- repos = JSON.parse(response)["repositories"]
62
- if query.strip.length > 0
63
- re = Regexp.new query
64
- repos = repos.find_all {|e| re =~ e }
59
+ def search(query = '')
60
+ all_repos = []
61
+ paginate_doget '/v2/_catalog' do |response|
62
+ # parse the response
63
+ repos = JSON.parse(response)['repositories']
64
+ if query.strip.length.positive?
65
+ re = Regexp.new query
66
+ repos = repos.find_all { |e| re =~ e }
67
+ end
68
+ all_repos += repos
65
69
  end
66
- all_repos += repos
70
+ all_repos
67
71
  end
68
- all_repos
69
- end
70
72
 
71
- def tags(repo,count=nil,last="",withHashes = false, auto_paginate: false)
72
- #create query params
73
- params = []
74
- params.push(["last",last]) if last && last != ""
75
- params.push(["n",count]) unless count.nil?
76
-
77
- query_vars = ""
78
- query_vars = "?#{URI.encode_www_form(params)}" if params.length > 0
79
-
80
- response = doget "/v2/#{repo}/tags/list#{query_vars}"
81
- # parse the response
82
- resp = JSON.parse response
83
- # parse out next page link if necessary
84
- resp["last"] = last(response.headers[:link]) if response.headers[:link]
85
-
86
- # do we include the hashes?
87
- if withHashes
88
- useGet = false
89
- resp["hashes"] = {}
90
- resp["tags"].each do |tag|
91
- if useGet
92
- head = doget "/v2/#{repo}/manifests/#{tag}"
93
- else
94
- begin
95
- head = dohead "/v2/#{repo}/manifests/#{tag}"
96
- rescue DockerRegistry2::InvalidMethod
97
- # in case we are in a registry pre-2.3.0, which did not support manifest HEAD
98
- useGet = true
73
+ def tags(repo, count = nil, last = '', withHashes = false, auto_paginate: false)
74
+ # create query params
75
+ params = []
76
+ params.push(['last', last]) if last && last != ''
77
+ params.push(['n', count]) unless count.nil?
78
+
79
+ query_vars = ''
80
+ query_vars = "?#{URI.encode_www_form(params)}" if params.length.positive?
81
+
82
+ response = doget "/v2/#{repo}/tags/list#{query_vars}"
83
+ # parse the response
84
+ resp = JSON.parse response
85
+ # parse out next page link if necessary
86
+ resp['last'] = last(response.headers[:link]) if response.headers[:link]
87
+
88
+ # do we include the hashes?
89
+ if withHashes
90
+ useGet = false
91
+ resp['hashes'] = {}
92
+ resp['tags'].each do |tag|
93
+ if useGet
99
94
  head = doget "/v2/#{repo}/manifests/#{tag}"
95
+ else
96
+ begin
97
+ head = dohead "/v2/#{repo}/manifests/#{tag}"
98
+ rescue DockerRegistry2::InvalidMethod
99
+ # in case we are in a registry pre-2.3.0, which did not support manifest HEAD
100
+ useGet = true
101
+ head = doget "/v2/#{repo}/manifests/#{tag}"
102
+ end
100
103
  end
104
+ resp['hashes'][tag] = head.headers[:docker_content_digest]
101
105
  end
102
- resp["hashes"][tag] = head.headers[:docker_content_digest]
103
106
  end
104
- end
105
107
 
106
- return resp unless auto_paginate
108
+ return resp unless auto_paginate
107
109
 
108
- while (last_tag = resp.delete("last"))
109
- additional_tags = tags(repo, count, last_tag, withHashes)
110
- resp["last"] = additional_tags["last"]
111
- resp["tags"] += additional_tags["tags"]
112
- resp["tags"] = resp["tags"].uniq
113
- resp["hashes"].merge!(additional_tags["hashes"]) if withHashes
110
+ while (last_tag = resp.delete('last'))
111
+ additional_tags = tags(repo, count, last_tag, withHashes)
112
+ resp['last'] = additional_tags['last']
113
+ resp['tags'] += additional_tags['tags']
114
+ resp['tags'] = resp['tags'].uniq
115
+ resp['hashes'].merge!(additional_tags['hashes']) if withHashes
116
+ end
117
+
118
+ resp
114
119
  end
115
120
 
116
- resp
117
- end
121
+ def manifest(repo, tag)
122
+ # first get the manifest
123
+ response = doget "/v2/#{repo}/manifests/#{tag}"
124
+ parsed = JSON.parse response.body
125
+ manifest = DockerRegistry2::Manifest[parsed]
126
+ manifest.body = response.body
127
+ manifest.headers = response.headers
128
+ manifest
129
+ end
118
130
 
119
- def manifest(repo,tag)
120
- # first get the manifest
121
- response = doget "/v2/#{repo}/manifests/#{tag}"
122
- parsed = JSON.parse response.body
123
- manifest = DockerRegistry2::Manifest[parsed]
124
- manifest.body = response.body
125
- manifest.headers = response.headers
126
- manifest
127
- end
131
+ def blob(repo, digest, outpath = nil)
132
+ blob_url = "/v2/#{repo}/blobs/#{digest}"
133
+ if outpath.nil?
134
+ response = doget(blob_url)
135
+ DockerRegistry2::Blob.new(response.headers, response.body)
136
+ else
137
+ File.open(outpath, 'w') do |fd|
138
+ doreq('get', blob_url, fd)
139
+ end
128
140
 
129
- def blob(repo, digest, outpath=nil)
130
- blob_url = "/v2/#{repo}/blobs/#{digest}"
131
- if outpath.nil?
132
- response = doget(blob_url)
133
- DockerRegistry2::Blob.new(response.headers, response.body)
134
- else
135
- File.open(outpath, 'w') do |fd|
136
- doreq('get', blob_url, fd)
141
+ outpath
137
142
  end
138
-
139
- outpath
140
143
  end
141
- end
142
144
 
143
- def digest(repo, tag)
144
- tag_path = "/v2/#{repo}/manifests/#{tag}"
145
- dohead(tag_path).headers[:docker_content_digest]
146
- rescue DockerRegistry2::InvalidMethod
147
- # Pre-2.3.0 registries didn't support manifest HEAD requests
148
- doget(tag_path).headers[:docker_content_digest]
149
- end
145
+ def digest(repo, tag)
146
+ tag_path = "/v2/#{repo}/manifests/#{tag}"
147
+ dohead(tag_path).headers[:docker_content_digest]
148
+ rescue DockerRegistry2::InvalidMethod
149
+ # Pre-2.3.0 registries didn't support manifest HEAD requests
150
+ doget(tag_path).headers[:docker_content_digest]
151
+ end
150
152
 
151
- def rmtag(image, tag)
152
- # TODO: Need full response back. Rewrite other manifests() calls without JSON?
153
- reference = doget("/v2/#{image}/manifests/#{tag}").headers[:docker_content_digest]
153
+ def rmtag(image, tag)
154
+ # TODO: Need full response back. Rewrite other manifests() calls without JSON?
155
+ reference = doget("/v2/#{image}/manifests/#{tag}").headers[:docker_content_digest]
154
156
 
155
- return dodelete("/v2/#{image}/manifests/#{reference}").code
156
- end
157
+ dodelete("/v2/#{image}/manifests/#{reference}").code
158
+ end
157
159
 
158
- def pull(repo, tag, dir)
159
- # make sure the directory exists
160
- FileUtils.mkdir_p dir
161
- # get the manifest
162
- m = manifest repo, tag
163
- # puts "pulling #{repo}:#{tag} into #{dir}"
164
- # manifest can contain multiple manifests one for each API version
165
- downloaded_layers = []
166
- downloaded_layers += _pull_v2(repo, m, dir) if m['schemaVersion'] == 2
167
- downloaded_layers += _pull_v1(repo, m, dir) if m['schemaVersion'] == 1
168
- # return downloaded_layers
169
- downloaded_layers
170
- end
160
+ def pull(repo, tag, dir)
161
+ # make sure the directory exists
162
+ FileUtils.mkdir_p dir
163
+ # get the manifest
164
+ m = manifest repo, tag
165
+ # puts "pulling #{repo}:#{tag} into #{dir}"
166
+ # manifest can contain multiple manifests one for each API version
167
+ downloaded_layers = []
168
+ downloaded_layers += _pull_v2(repo, m, dir) if m['schemaVersion'] == 2
169
+ downloaded_layers += _pull_v1(repo, m, dir) if m['schemaVersion'] == 1
170
+ # return downloaded_layers
171
+ downloaded_layers
172
+ end
171
173
 
172
- def _pull_v2(repo, manifest, dir)
173
- # make sure the directory exists
174
- FileUtils.mkdir_p dir
175
- return false unless manifest['schemaVersion'] == 2
176
- # pull each of the layers
177
- manifest['layers'].each do |layer|
178
- # define path of file to save layer in
179
- layer_file = "#{dir}/#{layer['digest']}"
180
- # skip layer if we already got it
181
- next if File.file? layer_file
182
- # download layer
183
- # puts "getting layer (v2) #{layer['digest']}"
184
- blob(repo, layer['digest'], layer_file)
185
- layer_file
174
+ def _pull_v2(repo, manifest, dir)
175
+ # make sure the directory exists
176
+ FileUtils.mkdir_p dir
177
+ return false unless manifest['schemaVersion'] == 2
178
+
179
+ # pull each of the layers
180
+ manifest['layers'].each do |layer|
181
+ # define path of file to save layer in
182
+ layer_file = "#{dir}/#{layer['digest']}"
183
+ # skip layer if we already got it
184
+ next if File.file? layer_file
185
+
186
+ # download layer
187
+ # puts "getting layer (v2) #{layer['digest']}"
188
+ blob(repo, layer['digest'], layer_file)
189
+ layer_file
190
+ end
186
191
  end
187
- end
188
192
 
189
- def _pull_v1(repo, manifest, dir)
190
- # make sure the directory exists
191
- FileUtils.mkdir_p dir
192
- return false unless manifest['schemaVersion'] == 1
193
- # pull each of the layers
194
- manifest['fsLayers'].each do |layer|
195
- # define path of file to save layer in
196
- layer_file = "#{dir}/#{layer['blobSum']}"
197
- # skip layer if we already got it
198
- next if File.file? layer_file
199
- # download layer
200
- # puts "getting layer (v1) #{layer['blobSum']}"
201
- blob(repo, layer['blobSum'], layer_file)
202
- # return layer file
203
- layer_file
193
+ def _pull_v1(repo, manifest, dir)
194
+ # make sure the directory exists
195
+ FileUtils.mkdir_p dir
196
+ return false unless manifest['schemaVersion'] == 1
197
+
198
+ # pull each of the layers
199
+ manifest['fsLayers'].each do |layer|
200
+ # define path of file to save layer in
201
+ layer_file = "#{dir}/#{layer['blobSum']}"
202
+ # skip layer if we already got it
203
+ next if File.file? layer_file
204
+
205
+ # download layer
206
+ # puts "getting layer (v1) #{layer['blobSum']}"
207
+ blob(repo, layer['blobSum'], layer_file)
208
+ # return layer file
209
+ layer_file
210
+ end
204
211
  end
205
- end
206
212
 
207
- def push(manifest,dir)
208
- end
213
+ def push(manifest, dir); end
209
214
 
210
- def tag(repo,tag,newrepo,newtag)
211
- manifest = manifest(repo, tag)
215
+ def tag(repo, tag, newrepo, newtag)
216
+ manifest = manifest(repo, tag)
217
+
218
+ raise DockerRegistry2::RegistryVersionException unless manifest['schemaVersion'] == 2
212
219
 
213
- if manifest['schemaVersion'] == 2
214
220
  doput "/v2/#{newrepo}/manifests/#{newtag}", manifest.to_json
215
- else
216
- raise DockerRegistry2::RegistryVersionException
217
221
  end
218
- end
219
222
 
220
- def copy(repo,tag,newregistry,newrepo,newtag)
221
- end
223
+ def copy(repo, tag, newregistry, newrepo, newtag); end
222
224
 
223
- # gets the size of a particular blob, given the repo and the content-addressable hash
224
- # usually unneeded, since manifest includes it
225
- def blob_size(repo,blobSum)
226
- response = dohead "/v2/#{repo}/blobs/#{blobSum}"
227
- Integer(response.headers[:content_length],10)
228
- end
225
+ # gets the size of a particular blob, given the repo and the content-addressable hash
226
+ # usually unneeded, since manifest includes it
227
+ def blob_size(repo, blobSum)
228
+ response = dohead "/v2/#{repo}/blobs/#{blobSum}"
229
+ Integer(response.headers[:content_length], 10)
230
+ end
231
+
232
+ # Parse the value of the Link HTTP header and return a Hash whose keys are the rel values turned into symbols, and
233
+ # the values are URLs. For example, `{ next: '/v2/_catalog?n=100&last=x' }`.
234
+ def parse_link_header(header)
235
+ last = ''
236
+ parts = header.split(',')
237
+ links = {}
238
+
239
+ # Parse each part into a named link
240
+ parts.each do |part, _index|
241
+ section = part.split(';')
242
+ url = section[0][/<(.*)>/, 1]
243
+ name = section[1][/rel="?([^"]*)"?/, 1].to_sym
244
+ links[name] = url
245
+ end
229
246
 
230
- # Parse the value of the Link HTTP header and return a Hash whose keys are the rel values turned into symbols, and
231
- # the values are URLs. For example, `{ next: '/v2/_catalog?n=100&last=x' }`.
232
- def parse_link_header(header)
233
- last=''
234
- parts = header.split(',')
235
- links = Hash.new
236
-
237
- # Parse each part into a named link
238
- parts.each do |part, index|
239
- section = part.split(';')
240
- url = section[0][/<(.*)>/,1]
241
- name = section[1][/rel="?([^"]*)"?/,1].to_sym
242
- links[name] = url
247
+ links
243
248
  end
244
249
 
245
- links
246
- end
250
+ def last(header)
251
+ links = parse_link_header(header)
252
+ if links[:next]
253
+ query = URI(links[:next]).query
254
+ link_key = @uri.host.eql?('quay.io') ? 'next_page' : 'last'
255
+ last = URI.decode_www_form(query).to_h[link_key]
247
256
 
248
- def last(header)
249
- links = parse_link_header(header)
250
- if links[:next]
251
- query=URI(links[:next]).query
252
- link_key = @uri.host.eql?('quay.io') ? 'next_page' : 'last'
253
- last=URI::decode_www_form(query).to_h[link_key]
257
+ end
258
+ last
259
+ end
254
260
 
261
+ def manifest_sum(manifest)
262
+ size = 0
263
+ manifest['layers'].each do |layer|
264
+ size += layer['size']
265
+ end
266
+ size
255
267
  end
256
- last
257
- end
258
268
 
259
- def manifest_sum(manifest)
260
- size = 0
261
- manifest["layers"].each { |layer|
262
- size += layer["size"]
263
- }
264
- size
265
- end
269
+ private
266
270
 
267
- private
268
- def doreq(type,url,stream=nil,payload=nil)
271
+ def doreq(type, url, stream = nil, payload = nil)
269
272
  begin
270
- block = stream.nil? ? nil : proc { |response|
271
- response.read_body do |chunk|
272
- stream.write chunk
273
- end
274
- }
273
+ block = if stream.nil?
274
+ nil
275
+ else
276
+ proc { |response|
277
+ response.read_body do |chunk|
278
+ stream.write chunk
279
+ end
280
+ }
281
+ end
275
282
  response = RestClient::Request.execute(@http_options.merge(
276
- method: type,
277
- url: @base_uri+url,
278
- headers: headers(payload: payload),
279
- block_response: block,
280
- payload: payload
281
- ))
283
+ method: type,
284
+ url: @base_uri + url,
285
+ headers: headers(payload: payload),
286
+ block_response: block,
287
+ payload: payload
288
+ ))
282
289
  rescue SocketError
283
290
  raise DockerRegistry2::RegistryUnknownException
284
- rescue RestClient::NotFound => error
285
- raise DockerRegistry2::NotFound, error
291
+ rescue RestClient::NotFound
292
+ raise DockerRegistry2::NotFound, "Image not found at #{@uri.host}"
286
293
  rescue RestClient::Unauthorized => e
287
294
  header = e.response.headers[:www_authenticate]
288
295
  method = header.to_s.downcase.split(' ')[0]
@@ -295,96 +302,102 @@ class DockerRegistry2::Registry
295
302
  raise DockerRegistry2::RegistryUnknownException
296
303
  end
297
304
  end
298
- return response
305
+ response
299
306
  end
300
307
 
301
- def do_basic_req(type, url, stream=nil, payload=nil)
308
+ def do_basic_req(type, url, stream = nil, payload = nil)
302
309
  begin
303
- block = stream.nil? ? nil : proc { |response|
304
- response.read_body do |chunk|
305
- stream.write chunk
306
- end
307
- }
310
+ block = if stream.nil?
311
+ nil
312
+ else
313
+ proc { |response|
314
+ response.read_body do |chunk|
315
+ stream.write chunk
316
+ end
317
+ }
318
+ end
308
319
  response = RestClient::Request.execute(@http_options.merge(
309
- method: type,
310
- url: @base_uri+url,
311
- user: @user,
312
- password: @password,
313
- headers: headers(payload: payload),
314
- block_response: block,
315
- payload: payload
316
- ))
320
+ method: type,
321
+ url: @base_uri + url,
322
+ user: @user,
323
+ password: @password,
324
+ headers: headers(payload: payload),
325
+ block_response: block,
326
+ payload: payload
327
+ ))
317
328
  rescue SocketError
318
329
  raise DockerRegistry2::RegistryUnknownException
319
330
  rescue RestClient::Unauthorized
320
331
  raise DockerRegistry2::RegistryAuthenticationException
321
332
  rescue RestClient::MethodNotAllowed
322
333
  raise DockerRegistry2::InvalidMethod
323
- rescue RestClient::NotFound => error
324
- raise DockerRegistry2::NotFound, error
334
+ rescue RestClient::NotFound => e
335
+ raise DockerRegistry2::NotFound, e
325
336
  end
326
- return response
337
+ response
327
338
  end
328
339
 
329
- def do_bearer_req(type, url, header, stream=false, payload=nil)
340
+ def do_bearer_req(type, url, header, stream = false, payload = nil)
330
341
  token = authenticate_bearer(header)
331
342
  begin
332
- block = stream.nil? ? nil : proc { |response|
333
- response.read_body do |chunk|
334
- stream.write chunk
335
- end
336
- }
343
+ block = if stream.nil?
344
+ nil
345
+ else
346
+ proc { |response|
347
+ response.read_body do |chunk|
348
+ stream.write chunk
349
+ end
350
+ }
351
+ end
337
352
  response = RestClient::Request.execute(@http_options.merge(
338
- method: type,
339
- url: @base_uri+url,
340
- headers: headers(payload: payload, bearer_token: token),
341
- block_response: block,
342
- payload: payload
343
- ))
353
+ method: type,
354
+ url: @base_uri + url,
355
+ headers: headers(payload: payload, bearer_token: token),
356
+ block_response: block,
357
+ payload: payload
358
+ ))
344
359
  rescue SocketError
345
360
  raise DockerRegistry2::RegistryUnknownException
346
361
  rescue RestClient::Unauthorized
347
362
  raise DockerRegistry2::RegistryAuthenticationException
348
363
  rescue RestClient::MethodNotAllowed
349
364
  raise DockerRegistry2::InvalidMethod
350
- rescue RestClient::NotFound => error
351
- raise DockerRegistry2::NotFound, error
365
+ rescue RestClient::NotFound => e
366
+ raise DockerRegistry2::NotFound, e
352
367
  end
353
368
 
354
- return response
369
+ response
355
370
  end
356
371
 
357
372
  def authenticate_bearer(header)
358
373
  # get the parts we need
359
374
  target = split_auth_header(header)
360
375
  # did we have a username and password?
361
- if defined? @user and @user.to_s.strip.length != 0
362
- target[:params][:account] = @user
363
- end
376
+ target[:params][:account] = @user if defined? @user && !@user.to_s.strip.empty?
364
377
  # authenticate against the realm
365
378
  uri = URI.parse(target[:realm])
366
379
  begin
367
380
  response = RestClient::Request.execute(@http_options.merge(
368
- method: :get,
369
- url: uri.to_s, headers: {params: target[:params]},
370
- user: @user,
371
- password: @password,
372
- ))
381
+ method: :get,
382
+ url: uri.to_s, headers: { params: target[:params] },
383
+ user: @user,
384
+ password: @password
385
+ ))
373
386
  rescue RestClient::Unauthorized, RestClient::Forbidden
374
387
  # bad authentication
375
388
  raise DockerRegistry2::RegistryAuthenticationException
376
- rescue RestClient::NotFound => error
377
- raise DockerRegistry2::NotFound, error
389
+ rescue RestClient::NotFound => e
390
+ raise DockerRegistry2::NotFound, e
378
391
  end
379
392
  # now save the web token
380
393
  result = JSON.parse(response)
381
- return result["token"] || result["access_token"]
394
+ result['token'] || result['access_token']
382
395
  end
383
396
 
384
397
  def split_auth_header(header = '')
385
- h = Hash.new
386
- h = {params: {}}
387
- header.scan(/([\w]+)\=\"([^"]+)\"/) do |entry|
398
+ h = {}
399
+ h = { params: {} }
400
+ header.scan(/(\w+)="([^"]+)"/) do |entry|
388
401
  case entry[0]
389
402
  when 'realm'
390
403
  h[:realm] = entry[1]
@@ -396,11 +409,12 @@ class DockerRegistry2::Registry
396
409
  end
397
410
 
398
411
  def headers(payload: nil, bearer_token: nil)
399
- headers={}
400
- headers['Authorization']="Bearer #{bearer_token}" unless bearer_token.nil?
401
- headers['Accept']='application/vnd.docker.distribution.manifest.v2+json, application/json' if payload.nil?
402
- headers['Content-Type']='application/vnd.docker.distribution.manifest.v2+json' unless payload.nil?
412
+ headers = {}
413
+ headers['Authorization'] = "Bearer #{bearer_token}" unless bearer_token.nil?
414
+ headers['Accept'] = 'application/vnd.docker.distribution.manifest.v2+json,application/vnd.docker.distribution.manifest.list.v2+json,application/json' if payload.nil?
415
+ headers['Content-Type'] = 'application/vnd.docker.distribution.manifest.v2+json' unless payload.nil?
403
416
 
404
417
  headers
405
418
  end
419
+ end
406
420
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DockerRegistry2
2
- VERSION = '1.12.0'
4
+ VERSION = '1.13.0'
3
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker_registry2
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Avi Deitcher https://github.com/deitch
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2022-08-23 00:00:00.000000000 Z
14
+ date: 2022-12-23 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -41,6 +41,20 @@ dependencies:
41
41
  - - "~>"
42
42
  - !ruby/object:Gem::Version
43
43
  version: '10.0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: rspec
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '3'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '3'
44
58
  - !ruby/object:Gem::Dependency
45
59
  name: rubocop
46
60
  requirement: !ruby/object:Gem::Requirement
@@ -55,6 +69,34 @@ dependencies:
55
69
  - - ">="
56
70
  - !ruby/object:Gem::Version
57
71
  version: 0.26.0
72
+ - !ruby/object:Gem::Dependency
73
+ name: vcr
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '6'
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '6'
86
+ - !ruby/object:Gem::Dependency
87
+ name: webmock
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
58
100
  - !ruby/object:Gem::Dependency
59
101
  name: rest-client
60
102
  requirement: !ruby/object:Gem::Requirement
@@ -102,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
144
  - !ruby/object:Gem::Version
103
145
  version: '0'
104
146
  requirements: []
105
- rubygems_version: 3.1.6
147
+ rubygems_version: 3.3.26
106
148
  signing_key:
107
149
  specification_version: 4
108
150
  summary: Docker v2 registry HTTP API client