Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stream and timestamp args to client.build #3245

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion docker/models/images.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import itertools
import datetime
import re
import warnings

Expand Down Expand Up @@ -217,7 +218,7 @@ def reload(self):
class ImageCollection(Collection):
model = Image

def build(self, **kwargs):
def build(self, stream=False, timestamp=False, **kwargs):
"""
Build an image and return it. Similar to the ``docker build``
command. Either ``path`` or ``fileobj`` must be set.
Expand Down Expand Up @@ -245,6 +246,10 @@ def build(self, **kwargs):
custom_context (bool): Optional if using ``fileobj``
encoding (str): The encoding for a stream. Set to ``gzip`` for
compressing
stream (bool): Print the build output to stdout and stderr while
the build runs
timestamp (bool): Prefix build output stream with a timestamp
"%Y-%m-%d %H:%M:%S"
pull (bool): Downloads any updates to the FROM image in Dockerfiles
forcerm (bool): Always remove intermediate containers, even after
unsuccessful builds
Expand Down Expand Up @@ -300,9 +305,22 @@ def build(self, **kwargs):
image_id = None
result_stream, internal_stream = itertools.tee(json_stream(resp))
for chunk in internal_stream:
if timestamp:
timestamp_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if 'error' in chunk:
if stream:
if len(chunk['error'].strip()) > 0:
if timestamp:
print(timestamp_str, "- ", end='', file=sys.stderr)
print("Error:", chunk['error'].strip(), flush=True, file=sys.stderr)
raise BuildError(chunk['error'], result_stream)
if 'stream' in chunk:
if stream:
for line in chunk["stream"].splitlines():
if len(line.strip()) > 0 and not bool(re.match(r'^(\s*\x1b\[[0-9;]*m)*\s*(\x1b\[0m)?\s*$', line.strip())):
if timestamp:
print(timestamp_str, "- ", end='')
print(line.strip(), flush=True)
match = re.search(
r'(^Successfully built |sha256:)([0-9a-f]+)$',
chunk['stream']
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/api_build_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ def test_build_container(self):

self.client.build(fileobj=script)

def test_build_container_with_stream_with_timestamp(self):
script = io.BytesIO(
"\n".join(
[
"FROM busybox",
"RUN mkdir -p /tmp/test",
"EXPOSE 8080",
"ADD https://dl.dropboxusercontent.com/u/20637798/silence.tar.gz"
" /tmp/silence.tar.gz",
]
).encode("ascii")
)

self.client.build(fileobj=script, stream=True, timestamp=True)

def test_build_container_pull(self):
script = io.BytesIO(
"\n".join(
Expand Down