Skip to content

Commit a82ce1c

Browse files
committed
Pass secrets to buildx builder
1 parent 0bcb0d7 commit a82ce1c

File tree

6 files changed

+109
-16
lines changed

6 files changed

+109
-16
lines changed

README.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,62 @@ shows the different configuration options available:
350350
# image is passed to subsequent steps.
351351
import: path/to/image/archive.tar
352352
353+
# Specify the secrets that should be used when building your image,
354+
# similar to the --secret option used by Docker
355+
# More info about secrets: https://docs.docker.com/build/building/secrets/
356+
secrets:
357+
# Example of a secret that is a file
358+
- id=secret1,src=examples/build/secrets/secret1.txt
359+
# Example of a secret that is an environment variable
360+
- id=secret2,env=SECRET2
361+
362+
363+
.. _Build Secrets:
364+
365+
Build Secrets
366+
=============
367+
368+
Buildrunner supports specifying secrets that should be used when building your image,
369+
similar to the --secret option used by Docker. This is done by adding the ``secrets``
370+
section to the ``build`` section. This is a list of secrets that should be used when
371+
building the image. The string should be in the format of ``id=secret1,src=<location of the file>``
372+
when the secret is a file or ``id=secret2,env=<environment variable name>`` when the secret is an environment variable.
373+
This syntax is the same as the syntax used by Docker to build with secrets.
374+
More info about building with secrets in docker and the syntax of the secret string
375+
see https://docs.docker.com/build/building/secrets/.
376+
377+
In order to use secrets in buildrunner, you need to do the following:
378+
379+
#. Update the buildrunner configuration file
380+
* Set ``use-legacy-builder`` to ``false``
381+
* Add the secrets to the ``secrets`` section in the ``build`` section
382+
#. Update the Dockerfile to use the secrets
383+
* Add the ``--mount`` at the beginning of each RUN command that needs the secret
384+
385+
.. code:: yaml
386+
387+
use-legacy-builder: false
388+
steps:
389+
build-my-container:
390+
build:
391+
dockerfile: |
392+
FROM alpine:latest
393+
RUN --mount=type=secret,id=secret1 \
394+
--mount=type=secret,id=secret2 \
395+
SECRET1_FILE=/run/secrets/secret1 \
396+
SECRET2_VARIABLE=$(cat /run/secrets/secret2) \
397+
echo "Using secrets in my build - secret1: $(cat $SECRET1_FILE) secret2: $SECRET2_VARIABLE"
398+
secrets:
399+
# Example of a secret that is a file
400+
- id=secret1,src=examples/build/secrets/secret1.txt
401+
# Example of a secret that is an environment variable
402+
- id=secret2,env=SECRET2
403+
404+
405+
406+
407+
408+
353409
354410
.. _Running Containers:
355411

buildrunner/config/models_step.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class StepBuild(StepTask):
8080
cache_to: Optional[Union[str, Dict[str, str]]] = None
8181
# import is a python reserved keyword so we need to alias it
8282
import_param: Optional[str] = Field(alias="import", default=None)
83+
secrets: Optional[List[str]] = None
8384

8485

8586
class RunAndServicesBase(StepTask):

buildrunner/docker/multiplatform_image_builder.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def __init__(
8080
cache_builders: Optional[List[str]] = None,
8181
cache_from: Optional[Union[dict, str]] = None,
8282
cache_to: Optional[Union[dict, str]] = None,
83+
secrets: Optional[List[str]] = None,
8384
):
8485
self._docker_registry = docker_registry
8586
self._build_registry = build_registry
@@ -89,6 +90,7 @@ def __init__(
8990
self._cache_builders = set(cache_builders if cache_builders else [])
9091
self._cache_from = cache_from
9192
self._cache_to = cache_to
93+
self._secrets = secrets
9294
if self._cache_from or self._cache_to:
9395
LOGGER.info(
9496
f'Configuring multiplatform builds to cache from {cache_from} and to {cache_to} '
@@ -197,17 +199,18 @@ def _build_with_inject(
197199
inject: dict,
198200
image_ref: str,
199201
platform: str,
200-
path: str,
202+
context_path: str,
201203
dockerfile: str,
202204
target: str,
203205
build_args: dict,
204206
builder: Optional[str],
205207
cache: bool = False,
206208
pull: bool = False,
209+
secrets: Optional[List[str]] = None,
207210
) -> None:
208-
if not path or not os.path.isdir(path):
211+
if not context_path or not os.path.isdir(context_path):
209212
LOGGER.warning(
210-
f"Failed to inject {inject} for {image_ref} since path {path} isn't a directory."
213+
f"Failed to inject {inject} for {image_ref} since path {context_path} isn't a directory."
211214
)
212215
return
213216

@@ -217,14 +220,14 @@ def _build_with_inject(
217220
) as tmp_dir:
218221
context_dir = os.path.join(tmp_dir, f"{dir_prefix}/")
219222
shutil.copytree(
220-
path,
223+
context_path,
221224
context_dir,
222225
ignore=shutil.ignore_patterns(dir_prefix, ".git"),
223226
symlinks=True,
224227
)
225228

226229
for src, dest in inject.items():
227-
src_path = os.path.join(path, src)
230+
src_path = os.path.join(context_path, src)
228231

229232
# Remove '/' prefix
230233
if dest.startswith("/"):
@@ -265,6 +268,7 @@ def _build_with_inject(
265268
cache=cache,
266269
pull=pull,
267270
stream_logs=True,
271+
secrets=secrets,
268272
**self._get_build_cache_options(builder),
269273
)
270274
self._log_buildx(logs_itr, platform)
@@ -294,6 +298,7 @@ def _build_single_image(
294298
inject: dict,
295299
cache: bool = False,
296300
pull: bool = False,
301+
secrets: Optional[List[str]] = None,
297302
) -> None:
298303
"""
299304
Builds a single image for the given platform.
@@ -307,6 +312,7 @@ def _build_single_image(
307312
target (str): The name of the stage to build in a multi-stage Dockerfile
308313
build_args (dict): The build args to pass to docker.
309314
inject (dict): The files to inject into the build context.
315+
secrets (List[str]): The secrets to pass to docker.
310316
"""
311317
assert os.path.isdir(path) and os.path.exists(dockerfile), (
312318
f"Either path {path} ({os.path.isdir(path)}) or file "
@@ -321,32 +327,37 @@ def _build_single_image(
321327
f"Building image for platform {platform} with {builder or 'default'} builder"
322328
)
323329

330+
# Build kwargs for the buildx build command
331+
build_kwargs = {
332+
"builder": builder,
333+
"cache": cache,
334+
"context_path": path,
335+
"pull": pull,
336+
"target": target,
337+
}
338+
339+
# Only pass secrets if they are provided
340+
if secrets:
341+
build_kwargs["secrets"] = secrets
342+
324343
if inject and isinstance(inject, dict):
325344
self._build_with_inject(
326345
inject=inject,
327346
image_ref=image_ref,
328347
platform=platform,
329-
path=path,
330348
dockerfile=dockerfile,
331-
target=target,
332349
build_args=build_args,
333-
builder=builder,
334-
cache=cache,
335-
pull=pull,
350+
**build_kwargs,
336351
)
337352
else:
338353
logs_itr = docker.buildx.build(
339-
path,
340354
tags=[image_ref],
341355
platforms=[platform],
342356
load=True,
343357
file=dockerfile,
344-
target=target,
345358
build_args=build_args,
346-
builder=builder,
347-
cache=cache,
348-
pull=pull,
349359
stream_logs=True,
360+
**build_kwargs,
350361
**self._get_build_cache_options(builder),
351362
)
352363
self._log_buildx(logs_itr, platform)
@@ -398,11 +409,12 @@ def build_multiple_images(
398409
path: str = ".",
399410
file: str = "Dockerfile",
400411
target: Optional[str] = None,
401-
use_threading: bool = True,
412+
use_threading: bool = False,
402413
build_args: dict = None,
403414
inject: dict = None,
404415
cache: bool = False,
405416
pull: bool = False,
417+
secrets: Optional[List[str]] = None,
406418
) -> BuiltImageInfo:
407419
"""
408420
Builds multiple images for the given platforms. One image will be built for each platform.
@@ -498,6 +510,7 @@ def build_multiple_images(
498510
inject,
499511
cache,
500512
pull,
513+
secrets,
501514
)
502515
LOGGER.debug(f"Building {repo} for {platform}")
503516
if use_threading:

buildrunner/steprunner/tasks/build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ def run(self, context):
238238
inject=self.to_inject,
239239
cache=not self.nocache,
240240
pull=self.pull,
241+
secrets=self.step.secrets,
241242
)
242243

243244
# Set expected number of platforms
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# In order to use secrets, you need to set use-legacy-builder to false in the config file
2+
# To run this example, you need to set the SECRET_PASSWORD environment variable
3+
# and run the example with the following command:
4+
# SECRET2=my_secret ./run-buildrunner.sh -f examples/build/secrets/buildrunner.yaml
5+
# More info about secrets: https://docs.docker.com/build/building/secrets/
6+
use-legacy-builder: false
7+
steps:
8+
simple-build-step:
9+
build:
10+
dockerfile: |
11+
FROM alpine:latest
12+
RUN --mount=type=secret,id=secret1 \
13+
--mount=type=secret,id=secret2 \
14+
SECRET1_FILE=/run/secrets/secret1 \
15+
SECRET2_VARIABLE=$(cat /run/secrets/secret2) \
16+
echo "Using secrets in my build - secret1: $(cat $SECRET1_FILE) secret2: $SECRET2_VARIABLE"
17+
secrets:
18+
# Example of a secret that is a file
19+
- id=secret1,src=examples/build/secrets/secret1.txt
20+
# Example of a secret that is an environment variable
21+
- id=secret2,env=SECRET2

examples/build/secrets/secret1.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
testuser123

0 commit comments

Comments
 (0)