Add support for build arguments

Allows 'build' configuration option to be specified as an
object and adds support for build args.

Signed-off-by: Garrett Heel <garrettheel@gmail.com>
This commit is contained in:
Garrett Heel 2015-12-11 15:19:51 -08:00 committed by Joffrey F
commit 9cfa71ceee
9 changed files with 297 additions and 49 deletions

View file

@ -294,7 +294,7 @@ class ServiceTest(DockerClientTestCase):
project='composetest',
name='db',
client=self.client,
build='tests/fixtures/dockerfile-with-volume',
build={'context': 'tests/fixtures/dockerfile-with-volume'},
)
old_container = create_and_start_container(service)
@ -315,7 +315,7 @@ class ServiceTest(DockerClientTestCase):
def test_execute_convergence_plan_when_image_volume_masks_config(self):
service = self.create_service(
'db',
build='tests/fixtures/dockerfile-with-volume',
build={'context': 'tests/fixtures/dockerfile-with-volume'},
)
old_container = create_and_start_container(service)
@ -346,7 +346,7 @@ class ServiceTest(DockerClientTestCase):
def test_execute_convergence_plan_without_start(self):
service = self.create_service(
'db',
build='tests/fixtures/dockerfile-with-volume'
build={'context': 'tests/fixtures/dockerfile-with-volume'}
)
containers = service.execute_convergence_plan(ConvergencePlan('create', []), start=False)
@ -450,7 +450,7 @@ class ServiceTest(DockerClientTestCase):
service = Service(
name='test',
client=self.client,
build='tests/fixtures/simple-dockerfile',
build={'context': 'tests/fixtures/simple-dockerfile'},
project='composetest',
)
container = create_and_start_container(service)
@ -463,7 +463,7 @@ class ServiceTest(DockerClientTestCase):
service = Service(
name='test',
client=self.client,
build='this/does/not/exist/and/will/throw/error',
build={'context': 'this/does/not/exist/and/will/throw/error'},
project='composetest',
)
container = create_and_start_container(service)
@ -483,7 +483,7 @@ class ServiceTest(DockerClientTestCase):
with open(os.path.join(base_dir, 'Dockerfile'), 'w') as f:
f.write("FROM busybox\n")
self.create_service('web', build=base_dir).build()
self.create_service('web', build={'context': base_dir}).build()
assert self.client.inspect_image('composetest_web')
def test_build_non_ascii_filename(self):
@ -496,7 +496,7 @@ class ServiceTest(DockerClientTestCase):
with open(os.path.join(base_dir.encode('utf8'), b'foo\xE2bar'), 'w') as f:
f.write("hello world\n")
self.create_service('web', build=text_type(base_dir)).build()
self.create_service('web', build={'context': text_type(base_dir)}).build()
assert self.client.inspect_image('composetest_web')
def test_build_with_image_name(self):
@ -508,16 +508,30 @@ class ServiceTest(DockerClientTestCase):
image_name = 'examples/composetest:latest'
self.addCleanup(self.client.remove_image, image_name)
self.create_service('web', build=base_dir, image=image_name).build()
self.create_service('web', build={'context': base_dir}, image=image_name).build()
assert self.client.inspect_image(image_name)
def test_build_with_git_url(self):
build_url = "https://github.com/dnephin/docker-build-from-url.git"
service = self.create_service('buildwithurl', build=build_url)
service = self.create_service('buildwithurl', build={'context': build_url})
self.addCleanup(self.client.remove_image, service.image_name)
service.build()
assert service.image()
def test_build_with_build_args(self):
base_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base_dir)
with open(os.path.join(base_dir, 'Dockerfile'), 'w') as f:
f.write("FROM busybox\n")
f.write("ARG build_version\n")
service = self.create_service('buildwithargs',
build={'context': text_type(base_dir),
'args': {"build_version": "1"}})
service.build()
assert service.image()
def test_start_container_stays_unpriviliged(self):
service = self.create_service('web')
container = create_and_start_container(service).inspect()

View file

@ -266,13 +266,13 @@ class ServiceStateTest(DockerClientTestCase):
dockerfile = context.join('Dockerfile')
dockerfile.write(base_image)
web = self.create_service('web', build=str(context))
web = self.create_service('web', build={'context': str(context)})
container = web.create_container()
dockerfile.write(base_image + 'CMD echo hello world\n')
web.build()
web = self.create_service('web', build=str(context))
web = self.create_service('web', build={'context': str(context)})
self.assertEqual(('recreate', [container]), web.convergence_plan())
def test_image_changed_to_build(self):
@ -286,7 +286,7 @@ class ServiceStateTest(DockerClientTestCase):
web = self.create_service('web', image='busybox')
container = web.create_container()
web = self.create_service('web', build=str(context))
web = self.create_service('web', build={'context': str(context)})
plan = web.convergence_plan()
self.assertEqual(('recreate', [container]), plan)
containers = web.execute_convergence_plan(plan)

View file

@ -12,6 +12,7 @@ import py
import pytest
from compose.config import config
from compose.config.config import resolve_build_args
from compose.config.config import resolve_environment
from compose.config.errors import ConfigurationError
from compose.config.types import VolumeSpec
@ -284,7 +285,7 @@ class ConfigTest(unittest.TestCase):
expected = [
{
'name': 'web',
'build': os.path.abspath('/'),
'build': {'context': os.path.abspath('/')},
'volumes': [VolumeSpec.parse('/home/user/project:/code')],
'links': ['db'],
},
@ -414,6 +415,59 @@ class ConfigTest(unittest.TestCase):
assert services[1]['name'] == 'db'
assert services[2]['name'] == 'web'
def test_config_build_configuration(self):
service = config.load(
build_config_details(
{'web': {
'build': '.',
'dockerfile': 'Dockerfile-alt'
}},
'tests/fixtures/extends',
'filename.yml'
)
).services
self.assertTrue('context' in service[0]['build'])
self.assertEqual(service[0]['build']['dockerfile'], 'Dockerfile-alt')
def test_config_build_configuration_v2(self):
service = config.load(
build_config_details(
{
'version': 2,
'services': {
'web': {
'build': '.',
'dockerfile': 'Dockerfile-alt'
}
}
},
'tests/fixtures/extends',
'filename.yml'
)
).services
self.assertTrue('context' in service[0]['build'])
self.assertEqual(service[0]['build']['dockerfile'], 'Dockerfile-alt')
service = config.load(
build_config_details(
{
'version': 2,
'services': {
'web': {
'build': {
'context': '.',
'dockerfile': 'Dockerfile-alt'
}
}
}
},
'tests/fixtures/extends',
'filename.yml'
)
).services
self.assertTrue('context' in service[0]['build'])
self.assertEqual(service[0]['build']['dockerfile'], 'Dockerfile-alt')
def test_load_with_multiple_files_v2(self):
base_file = config.ConfigFile(
'base.yaml',
@ -445,7 +499,7 @@ class ConfigTest(unittest.TestCase):
expected = [
{
'name': 'web',
'build': os.path.abspath('/'),
'build': {'context': os.path.abspath('/')},
'image': 'example/web',
'volumes': [VolumeSpec.parse('/home/user/project:/code')],
},
@ -1157,7 +1211,7 @@ class BuildOrImageMergeTest(unittest.TestCase):
self.assertEqual(
config.merge_service_dicts({'image': 'redis'}, {'build': '.'}, V1),
{'build': '.'},
{'build': '.'}
)
@ -1388,6 +1442,24 @@ class EnvTest(unittest.TestCase):
},
)
@mock.patch.dict(os.environ)
def test_resolve_build_args(self):
os.environ['env_arg'] = 'value2'
build = {
'context': '.',
'args': {
'arg1': 'value1',
'empty_arg': '',
'env_arg': None,
'no_env': None
}
}
self.assertEqual(
resolve_build_args(build),
{'arg1': 'value1', 'empty_arg': '', 'env_arg': 'value2', 'no_env': ''},
)
@pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
@mock.patch.dict(os.environ)
def test_resolve_path(self):
@ -1873,7 +1945,7 @@ class BuildPathTest(unittest.TestCase):
def test_from_file(self):
service_dict = load_from_filename('tests/fixtures/build-path/docker-compose.yml')
self.assertEquals(service_dict, [{'name': 'foo', 'build': self.abs_context_path}])
self.assertEquals(service_dict, [{'name': 'foo', 'build': {'context': self.abs_context_path}}])
def test_valid_url_in_build_path(self):
valid_urls = [
@ -1888,7 +1960,7 @@ class BuildPathTest(unittest.TestCase):
service_dict = config.load(build_config_details({
'validurl': {'build': valid_url},
}, '.', None)).services
assert service_dict[0]['build'] == valid_url
assert service_dict[0]['build'] == {'context': valid_url}
def test_invalid_url_in_build_path(self):
invalid_urls = [

View file

@ -355,7 +355,7 @@ class ServiceTest(unittest.TestCase):
self.assertEqual(parse_repository_tag("url:5000/repo@sha256:digest"), ("url:5000/repo", "sha256:digest", "@"))
def test_create_container_with_build(self):
service = Service('foo', client=self.mock_client, build='.')
service = Service('foo', client=self.mock_client, build={'context': '.'})
self.mock_client.inspect_image.side_effect = [
NoSuchImageError,
{'Id': 'abc123'},
@ -374,17 +374,18 @@ class ServiceTest(unittest.TestCase):
forcerm=False,
nocache=False,
rm=True,
buildargs=None,
)
def test_create_container_no_build(self):
service = Service('foo', client=self.mock_client, build='.')
service = Service('foo', client=self.mock_client, build={'context': '.'})
self.mock_client.inspect_image.return_value = {'Id': 'abc123'}
service.create_container(do_build=False)
self.assertFalse(self.mock_client.build.called)
def test_create_container_no_build_but_needs_build(self):
service = Service('foo', client=self.mock_client, build='.')
service = Service('foo', client=self.mock_client, build={'context': '.'})
self.mock_client.inspect_image.side_effect = NoSuchImageError
with self.assertRaises(NeedsBuildError):
service.create_container(do_build=False)
@ -394,7 +395,7 @@ class ServiceTest(unittest.TestCase):
b'{"stream": "Successfully built 12345"}',
]
service = Service('foo', client=self.mock_client, build='.')
service = Service('foo', client=self.mock_client, build={'context': '.'})
service.build()
self.assertEqual(self.mock_client.build.call_count, 1)