Skip to content

Commit

Permalink
adds build time secret support
Browse files Browse the repository at this point in the history
This is done using the build section in the yaml which may look like a strange decision given build secrets are almost by definition not in a well known location. It's intended that this feature be used with env substitution.

Signed-off-by: Ollie Edwards <oliver.s.edwards@gmail.com>
  • Loading branch information
Ollie Edwards committed Nov 22, 2019
1 parent 0c8470d commit 26fe5d7
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 3 deletions.
3 changes: 2 additions & 1 deletion compose/config/config_schema_v3.7.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
"cache_from": {"$ref": "#/definitions/list_of_strings"},
"network": {"type": "string"},
"target": {"type": "string"},
"shm_size": {"type": ["integer", "string"]}
"shm_size": {"type": ["integer", "string"]},
"secrets": {"$ref": "#/definitions/list_of_strings"}
},
"additionalProperties": false
}
Expand Down
9 changes: 7 additions & 2 deletions compose/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,9 @@ def build(self, no_cache=False, pull=False, force_rm=False, memory=None, build_a
'Impossible to perform platform-targeted builds for API version < 1.35'
)

builder = self.client if not cli else _CLIBuilder(progress, self.options.get('environment'))
builder = self.client if not cli else _CLIBuilder(progress,
self.options.get('environment'),
build_opts.get('secrets'))
build_output = builder.build(
path=path,
tag=self.image_name,
Expand Down Expand Up @@ -1717,11 +1719,12 @@ def rewrite_build_path(path):


class _CLIBuilder(object):
def __init__(self, progress, environment=None):
def __init__(self, progress, environment=None, secrets=None):
self._progress = progress
if environment is None:
environment = {}
self._environment = environment
self._secrets = secrets

def build(self, path, tag=None, quiet=False, fileobj=None,
nocache=False, rm=False, timeout=None,
Expand Down Expand Up @@ -1800,6 +1803,8 @@ def build(self, path, tag=None, quiet=False, fileobj=None,
command_builder.add_arg("--tag", tag)
command_builder.add_arg("--target", target)
command_builder.add_arg("--iidfile", iidfile)
if self._secrets is not None:
command_builder.add_list('--secret', self._secrets)
args = command_builder.build([path])

magic_word = "Successfully built "
Expand Down
21 changes: 21 additions & 0 deletions tests/acceptance/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,27 @@ def test_build_override_dir(self):

assert 'Successfully built' in result.stdout

@mock.patch.dict(os.environ)
def test_build_with_secrets(self):
os.environ["COMPOSE_DOCKER_CLI_BUILD"] = "1"
os.environ["DOCKER_BUILDKIT"] = "1"
self.base_dir = 'tests/fixtures/build-secrets'
build_result = self.dispatch(['build', '--build-arg', 'CACHEBUST=1'])
assert 'Successfully built' in build_result.stdout
run_result = self.dispatch(['run', 'foo'])
assert 'secret 1' in run_result.stdout

@mock.patch.dict(os.environ)
def test_build_with_secrets_substitution(self):
os.environ["COMPOSE_DOCKER_CLI_BUILD"] = "1"
os.environ["DOCKER_BUILDKIT"] = "1"
os.environ["FOO_SECRET"] = "secret_2"
self.base_dir = 'tests/fixtures/build-secrets'
build_result = self.dispatch(['build', '--build-arg', 'CACHEBUST=2'])
assert 'Successfully built' in build_result.stdout
run_result = self.dispatch(['run', 'foo'])
assert 'secret 2' in run_result.stdout

def test_build_override_dir_invalid_path(self):
config_path = os.path.abspath('tests/fixtures/build-path-override-dir/docker-compose.yml')
result = self.dispatch([
Expand Down
8 changes: 8 additions & 0 deletions tests/fixtures/build-secrets/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# syntax=docker/dockerfile:1.0.0-experimental
FROM busybox

# https://github.com/moby/moby/issues/1996#issuecomment-550020843
ARG CACHEBUST

RUN --mount=type=secret,target=/secret,required cp secret out
CMD cat out
7 changes: 7 additions & 0 deletions tests/fixtures/build-secrets/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: '3.7'
services:
foo:
build:
context: .
secrets:
- "id=secret,src=${FOO_SECRET:-secret_1}"
1 change: 1 addition & 0 deletions tests/fixtures/build-secrets/secret_1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
secret 1
1 change: 1 addition & 0 deletions tests/fixtures/build-secrets/secret_2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
secret 2
33 changes: 33 additions & 0 deletions tests/integration/service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,39 @@ def test_build_cli(self):
self.addCleanup(self.client.remove_image, service.image_name)
assert self.client.inspect_image('composetest_web')

def test_build_cli_with_secrets(self):
base_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base_dir)
secret_path_1 = os.path.join(base_dir, 'secret_1')
secret_path_2 = os.path.join(base_dir, 'secret_2')

with open(os.path.join(base_dir, 'Dockerfile'), 'w') as f:
# f.write("FROM busybox\n")
f.write(dedent("""\
# syntax=docker/dockerfile:1.0.0-experimental
FROM busybox
RUN --mount=type=secret,target=/secret_1,required cat /secret_1
RUN --mount=type=secret,target=/secret_2,required cat /secret_2
"""))
with open(secret_path_1, 'w') as f:
f.write("secret 1\n")
with open(secret_path_2, 'w') as f:
f.write("secret 2\n")

service = self.create_service('web',
build={
'context': base_dir,
'secrets': [
'id=secret_1,src={}'.format(secret_path_1),
'id=secret_2,src={}'.format(secret_path_2)]
},
environment={
'DOCKER_BUILDKIT': '1',
})
service.build(cli=True)
self.addCleanup(self.client.remove_image, service.image_name)
assert self.client.inspect_image('composetest_web')

def test_up_build_cli(self):
base_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base_dir)
Expand Down

0 comments on commit 26fe5d7

Please sign in to comment.