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

Commit 42244e0

Browse files
committed
Merge pull request #380 from dotcloud/0.7-fixes
0.7 fixes
2 parents 0f1523b + 42600ad commit 42244e0

File tree

10 files changed

+215
-19
lines changed

10 files changed

+215
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Major architecture rehaul, with potentially breaking changes:
1010
* updated and cleaned-up Dockerfile now uses latest Ubuntu LTS
1111
* largely enhanced configuration mechanism (setup-configs.sh is no more)
1212
* cookies are no longer used
13+
* CORS is now enabled on a number of endpoints
1314

1415
* [BUGFIX] unicode issues. Depending on the storage driver you are using, you may encounter unicode issues on already published content (likely garbled content).
1516
* [BUGFIX] content-length fix for bytes ranges

CONTRIBUTE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ Pretty much:
9999
* you have to use the provided test suite
100100
* all the tests must pass
101101

102+
### Word of warning
102103

104+
Drivers are expected to receive bytes and to return bytes.
105+
Don't try to decode or encode content.
103106

104107
## API documentation
105108

config/config_mirror.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# The `common' part is automatically included (and possibly overriden by all
22
# other flavors)
33
common:
4-
secret_key: REPLACEME
54
mirroring:
65
source: https://registry-1.docker.io
76
source_index: https://index.docker.io

config/config_sample.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ openstack:
100100
<<: *glance
101101

102102
# This flavor stores the images in Glance (to integrate with openstack)
103-
# and tags in Swift.
103+
# and tags in Swift.
104104
glance-swift: &glance-swift
105105
<<: *swift
106106
storage: glance
@@ -128,8 +128,6 @@ elliptics:
128128
dev: &dev
129129
<<: *local
130130
search_backend: _env:SEARCH_BACKEND:sqlalchemy
131-
# gunicorn will set this automatically if unset in dev mode
132-
secret_key: _env:SECRET_KEY:secret
133131

134132
# This flavor is automatically used by unit tests
135133
test:

depends/docker-registry-core/docker_registry/core/driver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def fetch(name):
235235
except ImportError:
236236
raise NotImplementedError(
237237
"""You requested storage driver docker_registry.drivers.%s
238-
which is not installed. Try `pip install docker_registry-drivers-%s`
238+
which is not installed. Try `pip install docker-registry-driver-%s`
239239
or check your configuration. The following are currently
240240
available on your system: %s"""
241241
% (name, name, available())

docker_registry/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from . import toolkit
1616
from .lib import config
1717

18-
VERSION = '0.6.9'
18+
VERSION = '0.7.0'
1919
app = flask.Flask('docker-registry')
2020
cfg = config.load()
2121
loglevel = getattr(logging, cfg.get('loglevel', 'INFO').upper())

setup.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
requirements.insert(0, 'argparse>=1.2.1')
2020
requirements.insert(0, 'importlib>=1.0.3')
2121

22+
packages = ['docker_registry',
23+
'docker_registry.drivers',
24+
'docker_registry.lib',
25+
'docker_registry.storage',
26+
'docker_registry.lib.index']
27+
2228
setuptools.setup(
2329
name='docker-registry',
2430
# TODO: Load the version programatically, which is currently available in
@@ -28,7 +34,7 @@
2834
description='Registry server for Docker',
2935
long_description=open('README.md').read(),
3036
namespace_packages=['docker_registry', 'docker_registry.drivers'],
31-
packages=setuptools.find_packages(),
37+
packages=packages,
3238
license=open('LICENSE').read(),
3339
author='Docker Registry Contributors',
3440
author_email='[email protected]',

tests/requirements-tox.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
Flask==0.9
1+
Flask==0.10.1
22
Flask-cors==1.3.0
3-
requests==1.2.0
4-
rsa==3.1.2
5-
PyYAML==3.10
3+
requests==2.3.0
4+
rsa==3.1.4
5+
PyYAML==3.11
66
blinker==1.3
7-
gevent==0.13.8
8-
sqlalchemy==0.9.2
9-
bugsnag==1.3.1
7+
gevent==1.0.1
8+
sqlalchemy==0.9.4
9+
bugsnag==2.0.1
1010
gunicorn==18.0

tests/test_layers.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import backports.lzma as lzma
4+
import os
5+
import random
6+
import string
7+
import tarfile
8+
9+
import base
10+
from docker_registry.lib import layers
11+
from docker_registry import storage
12+
13+
from docker_registry.core import compat
14+
json = compat.json
15+
StringIO = compat.StringIO
16+
17+
18+
# from mock import patch
19+
# from mockredis import mock_strict_redis_client
20+
21+
22+
def comp(n, f, *args, **kwargs):
23+
return (f(*args, **kwargs) for i in xrange(n))
24+
25+
26+
def rndstr(length=5):
27+
palette = string.ascii_uppercase + string.digits
28+
return ''.join(comp(length, random.choice, palette))
29+
30+
31+
def _get_tarfile(filenames):
32+
tfobj = StringIO()
33+
tar = tarfile.TarFile(fileobj=tfobj, mode='w')
34+
data = rndstr(512)
35+
for filename in filenames:
36+
tarinfo = tarfile.TarInfo(filename)
37+
tarinfo.size = len(data)
38+
io = StringIO()
39+
io.write(data)
40+
io.seek(0)
41+
tar.addfile(tarinfo, io)
42+
tfobj.seek(0)
43+
return tfobj
44+
45+
46+
def _get_xzfile(filenames):
47+
tar_data = _get_tarfile(filenames)
48+
lzma_fobj = StringIO()
49+
xz_file = lzma.open(lzma_fobj, 'w')
50+
xz_file.write(tar_data.read())
51+
xz_file.close()
52+
lzma_fobj.seek(0)
53+
return lzma_fobj
54+
55+
56+
class TestLayers(base.TestCase):
57+
58+
def setUp(self):
59+
self.store = storage.load(kind='file')
60+
self.filenames = list(comp(5, rndstr))
61+
62+
def test_tar_archive(self):
63+
tfobj = _get_tarfile(self.filenames)
64+
65+
archive = layers.Archive(tfobj)
66+
tar = tarfile.open(fileobj=archive)
67+
members = tar.getmembers()
68+
for tarinfo in members:
69+
assert tarinfo.name in self.filenames
70+
71+
def test_xz_archive(self):
72+
tfobj = _get_xzfile(self.filenames)
73+
archive = layers.Archive(tfobj)
74+
tar = tarfile.open(fileobj=archive)
75+
members = tar.getmembers()
76+
for tarinfo in members:
77+
assert tarinfo.name in self.filenames
78+
79+
def test_info_serialization(self):
80+
tfobj = _get_tarfile(self.filenames)
81+
archive = layers.Archive(tfobj)
82+
tar = tarfile.open(fileobj=archive)
83+
members = tar.getmembers()
84+
for tarinfo in members:
85+
sinfo = layers.serialize_tar_info(tarinfo)
86+
assert sinfo[0] in self.filenames
87+
assert sinfo[1:] == ('f', False, 512, 0, 420, 0, 0)
88+
89+
def test_tar_serialization(self):
90+
tfobj = _get_tarfile(self.filenames)
91+
archive = layers.Archive(tfobj)
92+
tar = tarfile.open(fileobj=archive)
93+
infos = layers.read_tarfile(tar)
94+
for tarinfo in infos:
95+
assert tarinfo[0] in self.filenames
96+
assert tarinfo[1:] == ('f', False, 512, 0, 420, 0, 0)
97+
98+
def test_layer_cache(self):
99+
layer_id = rndstr(16)
100+
layers.set_image_files_cache(layer_id, "{}")
101+
fetched_json = layers.get_image_files_cache(layer_id)
102+
assert fetched_json == "{}"
103+
104+
def test_tar_from_fobj(self):
105+
tfobj = _get_tarfile(self.filenames)
106+
files = layers.get_image_files_from_fobj(tfobj)
107+
for file in files:
108+
assert file[0] in self.filenames
109+
assert file[1:] == ('f', False, 512, 0, 420, 0, 0)
110+
111+
def test_get_image_files_json_cached(self):
112+
layer_id = rndstr(16)
113+
layers.set_image_files_cache(layer_id, "{}")
114+
files_json = layers.get_image_files_json(layer_id)
115+
assert files_json == "{}"
116+
117+
def test_get_image_files_json(self):
118+
layer_id = rndstr(16)
119+
tfobj = _get_tarfile(self.filenames)
120+
121+
layer_path = self.store.image_layer_path(layer_id)
122+
layer_path = os.path.join(self.store._root_path, layer_path)
123+
path_parts = layer_path.split(os.sep)
124+
layer_parent = os.path.join(*path_parts[:-1])
125+
os.makedirs(layer_parent)
126+
127+
with open(layer_path, 'w') as fobj:
128+
fobj.write(tfobj.read())
129+
130+
files_json = layers.get_image_files_json(layer_id)
131+
file_infos = json.loads(files_json)
132+
for info in file_infos:
133+
assert info[0] in self.filenames
134+
assert info[1:] == [u"f", False, 512, 0, 420, 0, 0]
135+
136+
def test_get_file_info_map(self):
137+
files = (
138+
("test", "f", False, 512, 0, 420, 0, 0),
139+
)
140+
map = layers.get_file_info_map(files)
141+
assert "test" in map
142+
assert map['test'] == ("f", False, 512, 0, 420, 0, 0)
143+
144+
def test_image_diff_cache(self):
145+
layer_id = rndstr(16)
146+
layers.set_image_diff_cache(layer_id, layer_id)
147+
diff_json = layers.get_image_diff_cache(layer_id)
148+
assert layer_id == diff_json
149+
150+
def test_image_diff_json(self):
151+
layer_1 = (
152+
("deleted", "f", False, 512, 0, 420, 0, 0),
153+
("changed", "f", False, 512, 0, 420, 0, 0),
154+
)
155+
156+
layer_2 = (
157+
("deleted", "f", True, 512, 0, 420, 0, 0),
158+
("changed", "f", False, 512, 0, 420, 0, 0),
159+
("created", "f", False, 512, 0, 420, 0, 0),
160+
)
161+
layer_1_id = rndstr(16)
162+
layer_2_id = rndstr(16)
163+
164+
ancestry = json.dumps([layer_2_id, layer_1_id])
165+
ancestry_path = self.store.image_ancestry_path(layer_2_id)
166+
self.store.put_content(ancestry_path, ancestry)
167+
168+
layer_1_files_path = self.store.image_files_path(layer_1_id)
169+
self.store.put_content(layer_1_files_path, json.dumps(layer_1))
170+
171+
layer_2_files_path = self.store.image_files_path(layer_2_id)
172+
self.store.put_content(layer_2_files_path, json.dumps(layer_2))
173+
174+
diff_json = layers.get_image_diff_json(layer_2_id)
175+
diff = json.loads(diff_json)
176+
177+
for type in ("deleted", "changed", "created"):
178+
assert type in diff
179+
assert type in diff[type]

tests/workflow.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import os
33

44
import requests
5+
sess = requests.Session()
6+
adapter = requests.adapters.HTTPAdapter(pool_connections=100, pool_maxsize=100)
7+
sess.mount('https://', adapter)
8+
requests = sess
59

610
from docker_registry.lib import checksums
711
from docker_registry.lib import config
@@ -15,6 +19,8 @@
1519

1620
cfg = config.load()
1721

22+
ua = 'docker/1.0.0 registry test pretending to be docker'
23+
1824

1925
class TestWorkflow(base.TestCase):
2026

@@ -32,7 +38,7 @@ class TestWorkflow(base.TestCase):
3238

3339
def generate_chunk(self, data):
3440
bufsize = 1024
35-
io = StringIO.StringIO(data)
41+
io = StringIO(data)
3642
while True:
3743
buf = io.read(bufsize)
3844
if not buf:
@@ -60,14 +66,16 @@ def upload_image(self, image_id, parent_id, token):
6066
self.registry_endpoint, image_id),
6167
data=json_data,
6268
headers={'Authorization': 'Token ' + token,
69+
'User-Agent': ua,
6370
'X-Docker-Checksum': layer_checksum},
6471
cookies=self.cookies)
6572
self.assertEqual(resp.status_code, 200, resp.text)
6673
self.update_cookies(resp)
6774
resp = requests.put('{0}/v1/images/{1}/layer'.format(
6875
self.registry_endpoint, image_id),
6976
data=self.generate_chunk(layer),
70-
headers={'Authorization': 'Token ' + token},
77+
headers={'Authorization': 'Token ' + token,
78+
'User-Agent': ua},
7179
cookies=self.cookies)
7280
self.assertEqual(resp.status_code, 200, resp.text)
7381
self.update_cookies(resp)
@@ -94,7 +102,8 @@ def docker_push(self):
94102
resp = requests.put('{0}/v1/repositories/{1}/{2}/'.format(
95103
self.index_endpoint, namespace, repos),
96104
auth=tuple(self.user_credentials),
97-
headers={'X-Docker-Token': 'true'},
105+
headers={'X-Docker-Token': 'true',
106+
'User-Agent': ua},
98107
data=images_json)
99108
self.assertEqual(resp.status_code, 200, resp.text)
100109
token = resp.headers.get('x-docker-token')
@@ -108,7 +117,8 @@ def docker_push(self):
108117
resp = requests.put('{0}/v1/repositories/{1}/{2}/images'.format(
109118
self.index_endpoint, namespace, repos),
110119
auth=tuple(self.user_credentials),
111-
headers={'X-Endpoints': self.registry_endpoint},
120+
headers={'X-Endpoints': self.registry_endpoint,
121+
'User-Agent': ua},
112122
data=json.dumps(images_json))
113123
self.assertEqual(resp.status_code, 204)
114124
return (namespace, repos)
@@ -160,7 +170,7 @@ def docker_pull(self, namespace, repos):
160170
json_data, checksum, blob = self.fetch_image(image_id)
161171
# check queried checksum and local computed checksum from the image
162172
# are the same
163-
tmpfile = StringIO.StringIO()
173+
tmpfile = StringIO()
164174
tmpfile.write(blob)
165175
tmpfile.seek(0)
166176
computed_checksum = checksums.compute_simple(tmpfile, json_data)

0 commit comments

Comments
 (0)