Skip to content
This repository was archived by the owner on Sep 12, 2018. It is now read-only.

Commit 13c1fcb

Browse files
committed
Merge pull request #889 from docker/legacy-work
Bump to 0.9.1
2 parents 8a2bd65 + 3981f1e commit 13c1fcb

File tree

12 files changed

+72
-36
lines changed

12 files changed

+72
-36
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Docker-registry
22

3+
## 0.9.1
4+
5+
* fixed database initialization issues
6+
* move away from rsa to m2crypto
7+
* s3 enhancements and bug fixes
8+
* enforce additional checks on image ids
9+
310
## 0.9.0
411

512
* "loose" dependencies mechanism (DEPS=loose environment var will let you install without strictly versioned dependencies)

README.md

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ As the documentation evolves with different registry versions, be sure that befo
1313
* check which version of the registry you are running
1414
* switch to the corresponding tag to access the README that matches your product version
1515

16-
The stable, released version is the [0.9.0 tag](https://github.com/docker/docker-registry/tree/0.9.0).
16+
The stable, released version is the [0.9.1 tag](https://github.com/docker/docker-registry/tree/0.9.1).
1717

1818
Please also have a quick look at the [FAQ](FAQ.md) before reporting bugs.
1919

@@ -224,11 +224,7 @@ common:
224224
On initialization, the `SQLAlchemyIndex` class checks the database
225225
version. If the database doesn't exist yet (or does exist, but lacks
226226
a `version` table), the `SQLAlchemyIndex` creates the database and
227-
required tables. To avoid several Gunicorn workers racing to create
228-
the database, you should launch your registry with
229-
[--preload][gunicorn-preload]. For example:
230-
231-
$ docker run -e GUNICORN_OPTS=[--preload] -p 5000:5000 registry
227+
required tables.
232228

233229
## Mirroring Options
234230

@@ -369,5 +365,4 @@ Read [contributing](CONTRIBUTING.md)
369365
[search-endpoint]: http://docs.docker.com/reference/api/docker-io_api/#search
370366
[SQLAlchemy]: http://docs.sqlalchemy.org/
371367
[create_engine]: http://docs.sqlalchemy.org/en/latest/core/engines.html#sqlalchemy.create_engine
372-
[gunicorn-preload]: http://gunicorn-docs.readthedocs.org/en/latest/settings.html#preload-app
373368

docker_registry/images.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ def _valid_bytes_range(bytes_range):
172172
@app.route('/v1/images/<image_id>/layer', methods=['GET'])
173173
@toolkit.requires_auth
174174
@require_completion
175+
@toolkit.valid_image_id
175176
@set_cache_headers
176177
@mirroring.source_lookup(cache=True, stream=True)
177178
def get_image_layer(image_id, headers):
@@ -193,6 +194,7 @@ def get_image_layer(image_id, headers):
193194

194195
@app.route('/v1/images/<image_id>/layer', methods=['PUT'])
195196
@toolkit.requires_auth
197+
@toolkit.valid_image_id
196198
def put_image_layer(image_id):
197199
client_version = toolkit.docker_client_version()
198200
if client_version and client_version < (0, 10):
@@ -227,6 +229,7 @@ def put_image_layer(image_id):
227229

228230
@app.route('/v1/images/<image_id>/checksum', methods=['PUT'])
229231
@toolkit.requires_auth
232+
@toolkit.valid_image_id
230233
def put_image_checksum(image_id):
231234
checksum = flask.request.headers.get('X-Docker-Checksum-Payload')
232235
if checksum is None:
@@ -256,6 +259,7 @@ def put_image_checksum(image_id):
256259

257260
@app.route('/v1/images/<image_id>/json', methods=['GET'])
258261
@toolkit.requires_auth
262+
@toolkit.valid_image_id
259263
@require_completion
260264
@set_cache_headers
261265
@mirroring.source_lookup(cache=True, stream=False)
@@ -274,6 +278,7 @@ def get_image_json(image_id, headers):
274278

275279
@app.route('/v1/images/<image_id>/ancestry', methods=['GET'])
276280
@toolkit.requires_auth
281+
@toolkit.valid_image_id
277282
@require_completion
278283
@set_cache_headers
279284
@mirroring.source_lookup(cache=True, stream=False)
@@ -325,6 +330,7 @@ def load_checksums(image_id):
325330

326331
@app.route('/v1/images/<image_id>/json', methods=['PUT'])
327332
@toolkit.requires_auth
333+
@toolkit.valid_image_id
328334
def put_image_json(image_id):
329335
data = None
330336
try:
@@ -372,6 +378,7 @@ def put_image_json(image_id):
372378

373379
@app.route('/v1/images/<image_id>/files', methods=['GET'])
374380
@toolkit.requires_auth
381+
@toolkit.valid_image_id
375382
@require_completion
376383
@set_cache_headers
377384
def get_image_files(image_id, headers):
@@ -392,6 +399,7 @@ def get_image_files(image_id, headers):
392399

393400
@app.route('/v1/images/<image_id>/diff', methods=['GET'])
394401
@toolkit.requires_auth
402+
@toolkit.valid_image_id
395403
@require_completion
396404
@set_cache_headers
397405
def get_image_diff(image_id, headers):

docker_registry/server/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
__credits__ = []
66

77
__license__ = 'Apache 2.0'
8-
__version__ = '0.9.0'
8+
__version__ = '0.9.1'
99
__maintainer__ = __author__
1010
__email__ = '[email protected]'
1111
__status__ = 'Production'

docker_registry/toolkit.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import random
99
import re
1010
import string
11+
import time
1112
import urllib
1213

1314
import flask
@@ -25,6 +26,17 @@
2526
logger = logging.getLogger(__name__)
2627
_re_docker_version = re.compile('docker/([^\s]+)')
2728
_re_authorization = re.compile(r'(\w+)[:=][\s"]?([^",]+)"?')
29+
_re_hex_image_id = re.compile(r'^([a-f0-9]{16}|[a-f0-9]{64})$')
30+
31+
32+
def valid_image_id(f):
33+
@functools.wraps(f)
34+
def wrapper(*args, **kwargs):
35+
image_id = kwargs.get('image_id', '')
36+
if _re_hex_image_id.match(image_id):
37+
return f(*args, **kwargs)
38+
return api_error("Invalid image ID", 404)
39+
return wrapper
2840

2941

3042
def docker_client_version():
@@ -299,14 +311,23 @@ def exclusive_lock(f):
299311
@functools.wraps(f)
300312
def wrapper(*args, **kwargs):
301313
lock_path = os.path.join(
302-
'/var/lock', 'registry.{0}.lock'.format(f.func_name)
314+
'./', 'registry.{0}.lock'.format(f.func_name)
303315
)
304316
if os.path.exists(lock_path):
317+
x = 0
318+
while os.path.exists(lock_path) and x < 100:
319+
logger.warn('Another process is creating the search database')
320+
x += 1
321+
time.sleep(1)
322+
if x == 100:
323+
raise Exception('Timedout waiting for db init')
305324
return
306325
lock_file = open(lock_path, 'w')
307326
lock_file.close()
308-
result = f(*args, **kwargs)
309-
os.remove(lock_path)
327+
try:
328+
result = f(*args, **kwargs)
329+
finally:
330+
os.remove(lock_path)
310331
return result
311332
return wrapper
312333

requirements/main.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
blinker==1.3
22
Flask==0.10.1
33
gevent==1.0.1
4-
gunicorn==19.1
4+
gunicorn==19.1.1
55
PyYAML==3.11
66
requests==2.3.0
77
M2Crypto==0.22.3

tests/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def gen_random_string(self, length=16):
3535
return ''.join([random.choice(string.ascii_uppercase + string.digits)
3636
for x in range(length)]).lower()
3737

38+
def gen_hex_string(self, length=16):
39+
return ''.join([random.choice(string.hexdigits)
40+
for x in range(length)]).lower()
41+
3842
def set_image_checksum(self, image_id, checksum):
3943
headers = {'X-Docker-Checksum-Payload': checksum}
4044
url = '/v1/images/{0}/checksum'.format(image_id)

tests/test_images.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
class TestImages(base.TestCase):
1515

1616
def test_unset_nginx_accel_redirect_layer(self):
17-
image_id = self.gen_random_string()
17+
image_id = self.gen_hex_string()
1818
layer_data = self.gen_random_string(1024)
1919
self.upload_image(image_id, parent_id=None, layer=layer_data)
2020
resp = self.http_client.get('/v1/images/{0}/layer'.format(image_id))
2121
self.assertEqual(layer_data, resp.data)
2222

2323
def test_nginx_accel_redirect_layer(self):
24-
image_id = self.gen_random_string()
24+
image_id = self.gen_hex_string()
2525
layer_data = self.gen_random_string(1024)
2626
self.upload_image(image_id, parent_id=None, layer=layer_data)
2727

@@ -48,8 +48,8 @@ def test_nginx_accel_redirect_layer(self):
4848
images.cfg._config.pop('nginx_x_accel_redirect')
4949

5050
def test_simple(self):
51-
image_id = self.gen_random_string()
52-
parent_id = self.gen_random_string()
51+
image_id = self.gen_hex_string()
52+
parent_id = self.gen_hex_string()
5353
layer_data = self.gen_random_string(1024)
5454
self.upload_image(parent_id, parent_id=None, layer=layer_data)
5555
self.upload_image(image_id, parent_id=parent_id, layer=layer_data)
@@ -67,7 +67,7 @@ def test_notfound(self):
6767
self.assertEqual(resp.status_code, 404, resp.data)
6868

6969
def test_bytes_range(self):
70-
image_id = self.gen_random_string()
70+
image_id = self.gen_hex_string()
7171
layer_data = self.gen_random_string(1024)
7272
b = random.randint(0, len(layer_data) / 2)
7373
bytes_range = (b, random.randint(b + 1, len(layer_data) - 1))
@@ -88,7 +88,7 @@ def before_put_image_json_handler_not_ok(self, sender, image_json):
8888
return "Not ok"
8989

9090
def test_before_put_image_json_ok(self):
91-
image_id = self.gen_random_string()
91+
image_id = self.gen_hex_string()
9292
json_obj = {
9393
'id': image_id
9494
}
@@ -100,7 +100,7 @@ def test_before_put_image_json_ok(self):
100100
self.assertEqual(resp.status_code, 200, resp.data)
101101

102102
def test_before_put_image_json_not_ok(self):
103-
image_id = self.gen_random_string()
103+
image_id = self.gen_hex_string()
104104
json_obj = {
105105
'id': image_id
106106
}

tests/test_mirrors.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ def mock_lookup_source(path, stream=False, source=None):
1717
resp.status_code = 200
1818
resp._content_consumed = True
1919
# resp.headers['X-Fake-Source-Header'] = 'foobar'
20-
if path.endswith('0145/layer'):
20+
if path.endswith('01451234/layer'):
2121
resp._content = "abcdef0123456789xxxxxx=-//"
22-
elif path.endswith('0145/json'):
23-
resp._content = ('{"id": "cafebabe0145","created":"2014-02-03T16:47:06'
24-
'.615279788Z"}')
25-
elif path.endswith('0145/ancestry'):
26-
resp._content = '["cafebabe0145"]'
22+
elif path.endswith('01451234/json'):
23+
resp._content = ('{"id": "cafebabe01451234",'
24+
'"created":"2014-02-03T16:47:06.615279788Z"}')
25+
elif path.endswith('01451234/ancestry'):
26+
resp._content = '["cafebabe01451234"]'
2727
elif path.endswith('test/tags'):
28-
resp._content = '{"latest": "cafebabe0145", "0.1.2": "cafebabe0145"}'
28+
resp._content = ('{"latest": "cafebabe01451234", "0.1.2": '
29+
'"cafebabe01451234"}')
2930
else:
3031
resp.status_code = 404
3132

@@ -52,24 +53,24 @@ def test_is_mirror(self):
5253
@mock.patch('docker_registry.lib.mirroring.lookup_source',
5354
mock_lookup_source)
5455
def test_source_lookup(self):
55-
resp = self.http_client.get('/v1/images/cafebabe0145/layer')
56+
resp = self.http_client.get('/v1/images/cafebabe01451234/layer')
5657
self.assertEqual(resp.status_code, 200)
5758
self.assertEqual(resp.data, "abcdef0123456789xxxxxx=-//")
5859

59-
resp_2 = self.http_client.get('/v1/images/cafebabe0145/json')
60+
resp_2 = self.http_client.get('/v1/images/cafebabe01451234/json')
6061
self.assertEqual(resp_2.status_code, 200)
6162
# Note(dmp): unicode patch XXX not applied assume requests does the job
6263
json_data = json.loads(resp_2.data)
6364
assert 'id' in json_data
6465
assert 'created' in json_data
65-
self.assertEqual(json_data['id'], 'cafebabe0145')
66+
self.assertEqual(json_data['id'], 'cafebabe01451234')
6667

67-
resp_3 = self.http_client.get('/v1/images/cafebabe0145/ancestry')
68+
resp_3 = self.http_client.get('/v1/images/cafebabe01451234/ancestry')
6869
self.assertEqual(resp_3.status_code, 200)
6970
# Note(dmp): unicode patch XXX not applied assume requests does the job
7071
json_data_2 = json.loads(resp_3.data)
7172
self.assertEqual(len(json_data_2), 1)
72-
self.assertEqual(json_data_2[0], 'cafebabe0145')
73+
self.assertEqual(json_data_2[0], 'cafebabe01451234')
7374

7475
resp_4 = self.http_client.get('/v1/images/doe587e8157/json')
7576
self.assertEqual(resp_4.status_code, 404)
@@ -81,7 +82,7 @@ def test_source_lookup_tag(self):
8182
self.assertEqual(resp.status_code, 200)
8283
self.assertEqual(
8384
resp.data,
84-
'{"latest": "cafebabe0145", "0.1.2": "cafebabe0145"}'
85+
'{"latest": "cafebabe01451234", "0.1.2": "cafebabe01451234"}'
8586
)
8687

8788
resp_2 = self.http_client.get('/v1/repositories/testing/bogus/tags')

tests/test_tags.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class TestTags(base.TestCase):
1111
def test_simple(self, repos_name=None):
1212
if repos_name is None:
1313
repos_name = self.gen_random_string()
14-
image_id = self.gen_random_string()
14+
image_id = self.gen_hex_string()
1515
layer_data = self.gen_random_string(1024)
1616
self.upload_image(image_id, parent_id=None, layer=layer_data)
1717

0 commit comments

Comments
 (0)