Browse Source

Merge pull request #2361 from dnephin/pr-2262

Rebase of PR 2262
Daniel Nephin 10 years ago
parent
commit
7d6c63d1b7

+ 6 - 3
compose/cli/main.py

@@ -181,12 +181,15 @@ class TopLevelCommand(DocoptCommand):
         Usage: build [options] [SERVICE...]
 
         Options:
+            --force-rm  Always remove intermediate containers.
             --no-cache  Do not use cache when building the image.
             --pull      Always attempt to pull a newer version of the image.
         """
-        no_cache = bool(options.get('--no-cache', False))
-        pull = bool(options.get('--pull', False))
-        project.build(service_names=options['SERVICE'], no_cache=no_cache, pull=pull)
+        project.build(
+            service_names=options['SERVICE'],
+            no_cache=bool(options.get('--no-cache', False)),
+            pull=bool(options.get('--pull', False)),
+            force_rm=bool(options.get('--force-rm', False)))
 
     def help(self, project, options):
         """

+ 2 - 2
compose/project.py

@@ -278,10 +278,10 @@ class Project(object):
         for service in self.get_services(service_names):
             service.restart(**options)
 
-    def build(self, service_names=None, no_cache=False, pull=False):
+    def build(self, service_names=None, no_cache=False, pull=False, force_rm=False):
         for service in self.get_services(service_names):
             if service.can_be_built():
-                service.build(no_cache, pull)
+                service.build(no_cache, pull, force_rm)
             else:
                 log.info('%s uses an image, skipping' % service.name)
 

+ 2 - 1
compose/service.py

@@ -701,7 +701,7 @@ class Service(object):
             cgroup_parent=cgroup_parent
         )
 
-    def build(self, no_cache=False, pull=False):
+    def build(self, no_cache=False, pull=False, force_rm=False):
         log.info('Building %s' % self.name)
 
         path = self.options['build']
@@ -715,6 +715,7 @@ class Service(object):
             tag=self.image_name,
             stream=True,
             rm=True,
+            forcerm=force_rm,
             pull=pull,
             nocache=no_cache,
             dockerfile=self.options.get('dockerfile', None),

+ 1 - 1
contrib/completion/bash/docker-compose

@@ -93,7 +93,7 @@ __docker_compose_services_stopped() {
 _docker_compose_build() {
 	case "$cur" in
 		-*)
-			COMPREPLY=( $( compgen -W "--help --no-cache --pull" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--force-rm --help --no-cache --pull" -- "$cur" ) )
 			;;
 		*)
 			__docker_compose_services_from_build

+ 1 - 0
contrib/completion/zsh/_docker-compose

@@ -192,6 +192,7 @@ __docker-compose_subcommand() {
         (build)
             _arguments \
                 $opts_help \
+                '--force-rm[Always remove intermediate containers.]' \
                 '--no-cache[Do not use cache when building the image]' \
                 '--pull[Always attempt to pull a newer version of the image.]' \
                 '*:services:__docker-compose_services_from_build' && ret=0

+ 1 - 0
docs/reference/build.md

@@ -15,6 +15,7 @@ parent = "smn_compose_cli"
 Usage: build [options] [SERVICE...]
 
 Options:
+--force-rm  Always remove intermediate containers.
 --no-cache  Do not use cache when building the image.
 --pull      Always attempt to pull a newer version of the image.
 ```

+ 28 - 0
tests/acceptance/cli_test.py

@@ -9,6 +9,7 @@ from operator import attrgetter
 from .. import mock
 from compose.cli.command import get_project
 from compose.cli.docker_client import docker_client
+from compose.container import Container
 from tests.integration.testcases import DockerClientTestCase
 
 
@@ -145,6 +146,33 @@ class CLITestCase(DockerClientTestCase):
         assert BUILD_CACHE_TEXT not in result.stdout
         assert BUILD_PULL_TEXT in result.stdout
 
+    def test_build_failed(self):
+        self.base_dir = 'tests/fixtures/simple-failing-dockerfile'
+        self.dispatch(['build', 'simple'], returncode=1)
+
+        labels = ["com.docker.compose.test_failing_image=true"]
+        containers = [
+            Container.from_ps(self.project.client, c)
+            for c in self.project.client.containers(
+                all=True,
+                filters={"label": labels})
+        ]
+        assert len(containers) == 1
+
+    def test_build_failed_forcerm(self):
+        self.base_dir = 'tests/fixtures/simple-failing-dockerfile'
+        self.dispatch(['build', '--force-rm', 'simple'], returncode=1)
+
+        labels = ["com.docker.compose.test_failing_image=true"]
+
+        containers = [
+            Container.from_ps(self.project.client, c)
+            for c in self.project.client.containers(
+                all=True,
+                filters={"label": labels})
+        ]
+        assert not containers
+
     def test_up_detached(self):
         self.dispatch(['up', '-d'])
         service = self.project.get_service('simple')

+ 7 - 0
tests/fixtures/simple-failing-dockerfile/Dockerfile

@@ -0,0 +1,7 @@
+FROM busybox:latest
+LABEL com.docker.compose.test_image=true
+LABEL com.docker.compose.test_failing_image=true
+# With the following label the container wil be cleaned up automatically
+# Must be kept in sync with LABEL_PROJECT from compose/const.py
+LABEL com.docker.compose.project=composetest
+RUN exit 1

+ 2 - 0
tests/fixtures/simple-failing-dockerfile/docker-compose.yml

@@ -0,0 +1,2 @@
+simple:
+  build: .

+ 1 - 0
tests/unit/service_test.py

@@ -356,6 +356,7 @@ class ServiceTest(unittest.TestCase):
             stream=True,
             path='.',
             pull=False,
+            forcerm=False,
             nocache=False,
             rm=True,
         )