Skip to content

Commit 485df1c

Browse files
author
Andy Goldstein
committed
Add before_put_image_json signal
Add before_put_image_json signal that allows receivers to short-circuit the put if they return any non-None value. This can be useful for things such as enforcing quotas, e.g. by having a custom extension connect to this signal, check the layer's Size, and returning something to the effect that the put was denied due to quota. Signed-off-by: Andy Goldstein <[email protected]>
1 parent 5a92101 commit 485df1c

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

docker_registry/images.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from .lib import checksums
2020
from .lib import layers
2121
from .lib import mirroring
22+
from .lib import signals
2223
# this is our monkey patched snippet from python v2.7.6 'tarfile'
2324
# with xattr support
2425
from .lib.xtarfile import tarfile
@@ -370,6 +371,13 @@ def put_image_json(image_id):
370371
mark_path = store.image_mark_path(image_id)
371372
if store.exists(json_path) and not store.exists(mark_path):
372373
return toolkit.api_error('Image already exists', 409)
374+
375+
sender = flask.current_app._get_current_object()
376+
signal_result = signals.before_put_image_json.send(sender, image_json=data)
377+
for result_pair in signal_result:
378+
if result_pair[1] is not None:
379+
return toolkit.api_error(result_pair[1])
380+
373381
# If we reach that point, it means that this is a new image or a retry
374382
# on a failed push
375383
store.put_content(mark_path, 'true')

docker_registry/lib/signals.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,8 @@
1313
# Triggered when a tag is modified (registry/tags.py)
1414
tag_created = _signals.signal('tag-created')
1515
tag_deleted = _signals.signal('tag-deleted')
16+
17+
# Triggered after all put_image_json validations have passed but before
18+
# actually storing anything. Any non-None return value from the signalled
19+
# subscribers will result in the put_image_json operation being cancelled.
20+
before_put_image_json = _signals.signal('before-put-image-json')

tests/test_images.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from docker_registry.core import compat
88
import docker_registry.images as images
9+
import docker_registry.lib.signals as signals
910

1011
json = compat.json
1112

@@ -79,3 +80,37 @@ def test_bytes_range(self):
7980
msg = 'expected size: {0}; got: {1}'.format(len(expected_data),
8081
len(received_data))
8182
self.assertEqual(expected_data, received_data, msg)
83+
84+
def before_put_image_json_handler_ok(self, sender, image_json):
85+
return None
86+
87+
def before_put_image_json_handler_not_ok(self, sender, image_json):
88+
return "Not ok"
89+
90+
def test_before_put_image_json_ok(self):
91+
image_id = self.gen_random_string()
92+
json_obj = {
93+
'id': image_id
94+
}
95+
json_data = compat.json.dumps(json_obj)
96+
with signals.before_put_image_json.connected_to(
97+
self.before_put_image_json_handler_ok):
98+
resp = self.http_client.put('/v1/images/{0}/json'.format(image_id),
99+
data=json_data)
100+
self.assertEqual(resp.status_code, 200, resp.data)
101+
102+
def test_before_put_image_json_not_ok(self):
103+
image_id = self.gen_random_string()
104+
json_obj = {
105+
'id': image_id
106+
}
107+
json_data = compat.json.dumps(json_obj)
108+
with signals.before_put_image_json.connected_to(
109+
self.before_put_image_json_handler_not_ok):
110+
resp = self.http_client.put('/v1/images/{0}/json'.format(image_id),
111+
data=json_data)
112+
resp_data = json.loads(resp.data)
113+
self.assertEqual(resp.status_code, 400, resp.data)
114+
self.assertTrue('error' in resp_data,
115+
'Expected error key in response')
116+
self.assertEqual(resp_data['error'], 'Not ok', resp.data)

0 commit comments

Comments
 (0)