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

Commit f245656

Browse files
committed
Drop the old tarsum computation for Docker <0.10
This landed in 82021b4 (Merging bypass-tarsum, 2014-04-07, #300), but those Dockers are now ancient history. I'm against this sort of user-agent-dependent behavior in general. If we want to version the API, it makes sense to have version checks in one place (e.g. in the URL, or in an explicit Docker-Version header, or in the PUT payload, ...). I'm happy with either the URL approach (always explicit) or the Docker-Version approach (defaulting to the current version). If we make some small change to the API (such as not storing layer tarsums), and want to add per-feature headers or registry-side options to enable/disable this behaviour, that's fine. If we can auto-detect new features (e.g. if Docker-Checksum-Payload exists, it must be using the new SHA-256 semantics), that's fine too. But regardless of how we version the API, conflating the API version with the user-agent version just seems confusing. Olivier wants us to warn older clients instead of just failing when their tarsum doesn't match our checksum [1], so we still need to use their user-agent string to decide which clients get warned. I've softened the old DockerVersion into a more relaxed docker_client_version, which no longer rejects non-Docker clients [2]. If we can't version the API, we can at least use a blacklist to deny old implementations instead of a whitelist that requires other clients to fake the Docker-daemon User-Agent string. For the 'PUT /v1/images/<image_id>/checksum' endpoint, we don't need to rely on the user-agent, because the old X-Docker-Checksum was replaced by the new X-Docker-Checksum-Payload. If the client isn't setting X-Docker-Checksum-Payload, just say that in the error message and hint that it might be due to an outdated client. I'd like to remove all the user-agent-dependent behavior, but we're currently also using it to setup some tag metadata in docker_registry.tags.put_tag. I don't touch that here, since I haven't tracked down the reasoning behind that behavior (although I suspect it's also a bad idea ;). I also haven't touched the user-agent bits under tests/, since I haven't checked to see if they're designed to test this check, or to test the put_tags check, or just to bypass either of those checks. [1]: #570 (comment) [2]: #375
1 parent 99f7d0c commit f245656

File tree

2 files changed

+28
-43
lines changed

2 files changed

+28
-43
lines changed

docker_registry/images.py

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ def get_image_layer(image_id, headers):
194194
@app.route('/v1/images/<image_id>/layer', methods=['PUT'])
195195
@toolkit.requires_auth
196196
def put_image_layer(image_id):
197+
client_version = toolkit.docker_client_version()
198+
if client_version and client_version < (0, 10):
199+
return toolkit.api_error(
200+
'This endpoint does not support Docker daemons older than 0.10',
201+
412)
197202
try:
198203
json_data = store.get_content(store.image_json_path(image_id))
199204
except exceptions.FileNotFoundError:
@@ -210,39 +215,11 @@ def put_image_layer(image_id):
210215
# compute checksums
211216
csums = []
212217
sr = toolkit.SocketReader(input_stream)
213-
if toolkit.DockerVersion() < '0.10':
214-
tmp, store_hndlr = storage.temp_store_handler()
215-
sr.add_handler(store_hndlr)
216218
h, sum_hndlr = checksums.simple_checksum_handler(json_data)
217219
sr.add_handler(sum_hndlr)
218220
store.stream_write(layer_path, sr)
219221
csums.append('sha256:{0}'.format(h.hexdigest()))
220222

221-
if toolkit.DockerVersion() < '0.10':
222-
# NOTE(samalba): After docker 0.10, the tarsum is not used to ensure
223-
# the image has been transfered correctly.
224-
logger.debug('put_image_layer: Tarsum is enabled')
225-
tar = None
226-
tarsum = checksums.TarSum(json_data)
227-
try:
228-
tmp.seek(0)
229-
tar = tarfile.open(mode='r|*', fileobj=tmp)
230-
tarfilesinfo = layers.TarFilesInfo()
231-
for member in tar:
232-
tarsum.append(member, tar)
233-
tarfilesinfo.append(member)
234-
layers.set_image_files_cache(image_id, tarfilesinfo.json())
235-
except (IOError, tarfile.TarError) as e:
236-
logger.debug('put_image_layer: Error when reading Tar stream '
237-
'tarsum. Disabling TarSum, TarFilesInfo. '
238-
'Error: {0}'.format(e))
239-
finally:
240-
if tar:
241-
tar.close()
242-
# All data have been consumed from the tempfile
243-
csums.append(tarsum.compute())
244-
tmp.close()
245-
246223
# We store the computed checksums for a later check
247224
save_checksums(image_id, csums)
248225
return toolkit.response()
@@ -251,10 +228,12 @@ def put_image_layer(image_id):
251228
@app.route('/v1/images/<image_id>/checksum', methods=['PUT'])
252229
@toolkit.requires_auth
253230
def put_image_checksum(image_id):
254-
if toolkit.DockerVersion() < '0.10':
255-
checksum = flask.request.headers.get('X-Docker-Checksum')
256-
else:
257-
checksum = flask.request.headers.get('X-Docker-Checksum-Payload')
231+
checksum = flask.request.headers.get('X-Docker-Checksum-Payload')
232+
if checksum is None:
233+
return toolkit.api_error(
234+
('X-Docker-Checksum-Payload not set. If you are using the Docker '
235+
'daemon, you should upgrade to version 0.10 or later'),
236+
412)
258237
if not checksum:
259238
return toolkit.api_error('Missing Image\'s checksum')
260239
if not store.exists(store.image_json_path(image_id)):

docker_registry/toolkit.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# -*- coding: utf-8 -*-
22

33
import base64
4-
import distutils.version
54
import functools
65
import logging
76
import os
@@ -26,17 +25,24 @@
2625
_re_authorization = re.compile(r'(\w+)[:=][\s"]?([^",]+)"?')
2726

2827

29-
class DockerVersion(distutils.version.StrictVersion):
28+
def docker_client_version():
29+
"""Try and extract the client version from the User-Agent string
3030
31-
def __init__(self):
32-
ua = flask.request.headers.get('user-agent', '')
33-
m = _re_docker_version.search(ua)
34-
if not m:
35-
raise RuntimeError('toolkit.DockerVersion: cannot parse version')
36-
version = m.group(1)
37-
if '-' in version:
38-
version = version.split('-')[0]
39-
distutils.version.StrictVersion.__init__(self, version)
31+
So we can warn older versions of the Docker engine/daemon about
32+
incompatible APIs. If we can't figure out the version (e.g. the
33+
client is not a Docker engine), just return None.
34+
"""
35+
ua = flask.request.headers.get('user-agent', '')
36+
m = _re_docker_version.search(ua)
37+
if not m:
38+
return
39+
version = m.group(1)
40+
if '-' in version:
41+
version = version.split('-')[0]
42+
try:
43+
return tuple(int(x) for x in version)
44+
except ValueError:
45+
return
4046

4147

4248
class SocketReader(object):

0 commit comments

Comments
 (0)