summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2018-01-26 15:59:46 -0800
committerJoffrey F <joffrey@docker.com>2018-01-26 15:59:46 -0800
commit631cc3c1215441edb075a999a77061c1275c5e5a (patch)
tree7d605cd7451cf94f0698b8a318721c8e7c627e91
parentdeb8222d6994dca12be65146189859c9b76ed9a5 (diff)
downloaddocker-py-631cc3c1215441edb075a999a77061c1275c5e5a.tar.gz
ImageCollection.build now also returns build logs along with the built image reference1702-build-logs-dockerclient
BuildError.build_logs has a copy of the logs generator Signed-off-by: Joffrey F <joffrey@docker.com>
-rw-r--r--docker/errors.py7
-rw-r--r--docker/models/images.py14
-rw-r--r--tests/integration/models_images_test.py17
3 files changed, 24 insertions, 14 deletions
diff --git a/docker/errors.py b/docker/errors.py
index eeeac57..0253695 100644
--- a/docker/errors.py
+++ b/docker/errors.py
@@ -140,8 +140,11 @@ class StreamParseError(RuntimeError):
self.msg = reason
-class BuildError(Exception):
- pass
+class BuildError(DockerException):
+ def __init__(self, reason, build_log):
+ super(BuildError, self).__init__(reason)
+ self.msg = reason
+ self.build_log = build_log
class ImageLoadError(DockerException):
diff --git a/docker/models/images.py b/docker/models/images.py
index dcdeac9..c4e727b 100644
--- a/docker/models/images.py
+++ b/docker/models/images.py
@@ -1,3 +1,4 @@
+import itertools
import re
import six
@@ -160,7 +161,9 @@ class ImageCollection(Collection):
platform (str): Platform in the format ``os[/arch[/variant]]``.
Returns:
- (:py:class:`Image`): The built image.
+ (tuple): The first item is the :py:class:`Image` object for the
+ image that was build. The second item is a generator of the
+ build logs as JSON-decoded objects.
Raises:
:py:class:`docker.errors.BuildError`
@@ -175,9 +178,10 @@ class ImageCollection(Collection):
return self.get(resp)
last_event = None
image_id = None
- for chunk in json_stream(resp):
+ result_stream, internal_stream = itertools.tee(json_stream(resp))
+ for chunk in internal_stream:
if 'error' in chunk:
- raise BuildError(chunk['error'])
+ raise BuildError(chunk['error'], result_stream)
if 'stream' in chunk:
match = re.search(
r'(^Successfully built |sha256:)([0-9a-f]+)$',
@@ -187,8 +191,8 @@ class ImageCollection(Collection):
image_id = match.group(2)
last_event = chunk
if image_id:
- return self.get(image_id)
- raise BuildError(last_event or 'Unknown')
+ return (self.get(image_id), result_stream)
+ raise BuildError(last_event or 'Unknown', result_stream)
def get(self, name):
"""
diff --git a/tests/integration/models_images_test.py b/tests/integration/models_images_test.py
index 8840e15..900555d 100644
--- a/tests/integration/models_images_test.py
+++ b/tests/integration/models_images_test.py
@@ -10,27 +10,30 @@ class ImageCollectionTest(BaseIntegrationTest):
def test_build(self):
client = docker.from_env(version=TEST_API_VERSION)
- image = client.images.build(fileobj=io.BytesIO(
+ image, _ = client.images.build(fileobj=io.BytesIO(
"FROM alpine\n"
"CMD echo hello world".encode('ascii')
))
self.tmp_imgs.append(image.id)
assert client.containers.run(image) == b"hello world\n"
- @pytest.mark.xfail(reason='Engine 1.13 responds with status 500')
+ # @pytest.mark.xfail(reason='Engine 1.13 responds with status 500')
def test_build_with_error(self):
client = docker.from_env(version=TEST_API_VERSION)
with self.assertRaises(docker.errors.BuildError) as cm:
client.images.build(fileobj=io.BytesIO(
"FROM alpine\n"
- "NOTADOCKERFILECOMMAND".encode('ascii')
+ "RUN exit 1".encode('ascii')
))
- assert str(cm.exception) == ("Unknown instruction: "
- "NOTADOCKERFILECOMMAND")
+ print(cm.exception)
+ assert str(cm.exception) == (
+ "The command '/bin/sh -c exit 1' returned a non-zero code: 1"
+ )
+ assert cm.exception.build_log
def test_build_with_multiple_success(self):
client = docker.from_env(version=TEST_API_VERSION)
- image = client.images.build(
+ image, _ = client.images.build(
tag='some-tag', fileobj=io.BytesIO(
"FROM alpine\n"
"CMD echo hello world".encode('ascii')
@@ -41,7 +44,7 @@ class ImageCollectionTest(BaseIntegrationTest):
def test_build_with_success_build_output(self):
client = docker.from_env(version=TEST_API_VERSION)
- image = client.images.build(
+ image, _ = client.images.build(
tag='dup-txt-tag', fileobj=io.BytesIO(
"FROM alpine\n"
"CMD echo Successfully built abcd1234".encode('ascii')