diff --git a/compose/cli/main.py b/compose/cli/main.py index c3e30919d..bc63d9718 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -232,6 +232,7 @@ class TopLevelCommand(object): --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. + -m, --memory MEM Sets memory limit for the bulid container. --build-arg key=val Set build-time variables for one service. """ service_names = options['SERVICE'] @@ -248,6 +249,7 @@ class TopLevelCommand(object): no_cache=bool(options.get('--no-cache', False)), pull=bool(options.get('--pull', False)), force_rm=bool(options.get('--force-rm', False)), + memory=options.get('--memory'), build_args=build_args) def bundle(self, config_options, options): diff --git a/compose/project.py b/compose/project.py index f6bd30a88..a618e0f2f 100644 --- a/compose/project.py +++ b/compose/project.py @@ -357,10 +357,11 @@ class Project(object): ) return containers - def build(self, service_names=None, no_cache=False, pull=False, force_rm=False, build_args=None): + def build(self, service_names=None, no_cache=False, pull=False, force_rm=False, memory=None, + build_args=None): for service in self.get_services(service_names): if service.can_be_built(): - service.build(no_cache, pull, force_rm, build_args) + service.build(no_cache, pull, force_rm, memory, build_args) else: log.info('%s uses an image, skipping' % service.name) diff --git a/compose/service.py b/compose/service.py index 0b6561d99..e19b8fbb1 100644 --- a/compose/service.py +++ b/compose/service.py @@ -900,7 +900,7 @@ class Service(object): return [build_spec(secret) for secret in self.secrets] - def build(self, no_cache=False, pull=False, force_rm=False, build_args_override=None): + def build(self, no_cache=False, pull=False, force_rm=False, memory=None, build_args_override=None): log.info('Building %s' % self.name) build_opts = self.options.get('build', {}) @@ -931,6 +931,9 @@ class Service(object): target=build_opts.get('target', None), shmsize=parse_bytes(build_opts.get('shm_size')) if build_opts.get('shm_size') else None, extra_hosts=build_opts.get('extra_hosts', None), + container_limits={ + 'memory': parse_bytes(memory) if memory else None + }, ) try: diff --git a/contrib/completion/bash/docker-compose b/contrib/completion/bash/docker-compose index 1fdb27705..d9cbbbbf3 100644 --- a/contrib/completion/bash/docker-compose +++ b/contrib/completion/bash/docker-compose @@ -120,7 +120,7 @@ _docker_compose_build() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--build-arg --force-rm --help --no-cache --pull" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--build-arg --force-rm --help --memory --no-cache --pull" -- "$cur" ) ) ;; *) __docker_compose_services_from_build diff --git a/contrib/completion/zsh/_docker-compose b/contrib/completion/zsh/_docker-compose index f53f96334..c0a54cced 100644 --- a/contrib/completion/zsh/_docker-compose +++ b/contrib/completion/zsh/_docker-compose @@ -196,6 +196,7 @@ __docker-compose_subcommand() { $opts_help \ "*--build-arg=[Set build-time variables for one service.]:=: " \ '--force-rm[Always remove intermediate containers.]' \ + '--memory[Memory limit for the build container.]' \ '--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 diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 8468dfbde..019380644 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -584,6 +584,12 @@ class CLITestCase(DockerClientTestCase): result = self.dispatch(['build', '--no-cache'], None) assert 'shm_size: 96' in result.stdout + def test_build_memory_build_option(self): + pull_busybox(self.client) + self.base_dir = 'tests/fixtures/build-memory' + result = self.dispatch(['build', '--no-cache', '--memory', '96m', 'service'], None) + assert 'memory: 100663296' in result.stdout # 96 * 1024 * 1024 + def test_bundle_with_digests(self): self.base_dir = 'tests/fixtures/bundle-with-digests/' tmpdir = pytest.ensuretemp('cli_test_bundle') diff --git a/tests/fixtures/build-memory/Dockerfile b/tests/fixtures/build-memory/Dockerfile new file mode 100644 index 000000000..b27349b96 --- /dev/null +++ b/tests/fixtures/build-memory/Dockerfile @@ -0,0 +1,4 @@ +FROM busybox + +# Report the memory (through the size of the group memory) +RUN echo "memory:" $(cat /sys/fs/cgroup/memory/memory.limit_in_bytes) diff --git a/tests/fixtures/build-memory/docker-compose.yml b/tests/fixtures/build-memory/docker-compose.yml new file mode 100644 index 000000000..f98355851 --- /dev/null +++ b/tests/fixtures/build-memory/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3.5' + +services: + service: + build: + context: . diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index 8e8f60203..3a0eab0ec 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -499,6 +499,7 @@ class ServiceTest(unittest.TestCase): target=None, shmsize=None, extra_hosts=None, + container_limits={'memory': None}, ) def test_ensure_image_exists_no_build(self): @@ -541,6 +542,7 @@ class ServiceTest(unittest.TestCase): target=None, shmsize=None, extra_hosts=None, + container_limits={'memory': None}, ) def test_build_does_not_pull(self):