Repository: docker/docker-py

Issue:
images.build fails when using tag argument.
When using the `tag` kwarg on `client.images.build`, it will always throw a Build Error.

The api.build output looks like this

```
['{"stream":"Step 1/4 : FROM scratch\\n"}\r\n',
 '{"stream":" ---\\u003e \\n"}\r\n',
 '{"stream":"Step 2/4 : LABEL com.nvidia.volumes.needed \\"nvidia_driver\\"\\n"}\r\n',
 '{"stream":" ---\\u003e Using cache\\n"}\r\n',
 '{"stream":" ---\\u003e 36ec3942c5f5\\n"}\r\n',
 '{"stream":"Step 3/4 : SHELL /usr/local/nvidia/bin/nvidia-smi\\n"}\r\n',
 '{"stream":" ---\\u003e Running in f875d54529eb\\n"}\r\n',
 '{"stream":" ---\\u003e b750cf87aed6\\n"}\r\n',
 '{"stream":"Step 4/4 : CMD -L\\n"}\r\n',
 '{"stream":" ---\\u003e Running in f3f9e21b8171\\n"}\r\n',
 '{"stream":" ---\\u003e 61c46a80da73\\n"}\r\n',
 '{"stream":"Successfully built 61c46a80da73\\n"}\r\n',
 '{"stream":"Successfully tagged 5b3bc129-d296-4b7d-872c-bc7117d4f327:latest\\n"}\r\n']
```

The problem can be tracked down to [here](https://github.com/docker/docker-py/blob/2.2.1-release/docker/models/images.py#L164-L167). The code is assuming the "Successfully built" comes last, however in the tag case, this is not true.

Two ideas are to either 

1. Search the last two lines
2. Or in case that is not enough, search all the lines for "Successfully built"


I'm using docker server 17.05.0-ce-rc1



Discussion:
Thank you for the report!

Resolving patch:
diff --git a/docker/models/images.py b/docker/models/images.py
--- a/docker/models/images.py
+++ b/docker/models/images.py
@@ -166,18 +166,18 @@ def build(self, **kwargs):
         resp = self.client.api.build(**kwargs)
         if isinstance(resp, six.string_types):
             return self.get(resp)
-        events = list(json_stream(resp))
-        if not events:
-            return BuildError('Unknown')
-        event = events[-1]
-        if 'stream' in event:
-            match = re.search(r'(Successfully built |sha256:)([0-9a-f]+)',
-                              event.get('stream', ''))
-            if match:
-                image_id = match.group(2)
-                return self.get(image_id)
-
-        raise BuildError(event.get('error') or event)
+        for chunk in json_stream(resp):
+            if 'error' in chunk:
+                raise BuildError(chunk['error'])
+                break
+            if 'stream' in chunk:
+                match = re.search(r'(Successfully built |sha256:)([0-9a-f]+)',
+                                  chunk['stream'])
+                if match:
+                    image_id = match.group(2)
+                    return self.get(image_id)
+
+        return BuildError('Unknown')
 
     def get(self, name):
         """


Test patch:
diff --git a/tests/integration/models_images_test.py b/tests/integration/models_images_test.py
--- a/tests/integration/models_images_test.py
+++ b/tests/integration/models_images_test.py
@@ -28,6 +28,15 @@ def test_build_with_error(self):
         assert str(cm.exception) == ("Unknown instruction: "
                                      "NOTADOCKERFILECOMMAND")
 
+    def test_build_with_multiple_success(self):
+        client = docker.from_env(version=TEST_API_VERSION)
+        image = client.images.build(tag='some-tag', 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"
+
     def test_list(self):
         client = docker.from_env(version=TEST_API_VERSION)
         image = client.images.pull('alpine:latest')