Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,62 @@ shows the different configuration options available:
# image is passed to subsequent steps.
import: path/to/image/archive.tar

# Specify the secrets that should be used when building your image,
# similar to the --secret option used by Docker
# More info about secrets: https://docs.docker.com/build/building/secrets/
secrets:
# Example of a secret that is a file
- id=secret1,src=<path to the secret file>
# Example of a secret that is an environment variable
- id=secret2,env=<environment variable name>


.. _Build Secrets:

Build Secrets
=============

Buildrunner supports specifying secrets that should be used when building your image,
similar to the --secret option used by Docker. This is done by adding the ``secrets``
section to the ``build`` section. This is a list of secrets that should be used when
building the image. The string should be in the format of ``id=secret1,src=<location of the file>``
when the secret is a file or ``id=secret2,env=<environment variable name>`` when the secret is an environment variable.
This syntax is the same as the syntax used by Docker to build with secrets.
More info about building with secrets in docker and the syntax of the secret string
see https://docs.docker.com/build/building/secrets/.

In order to use secrets in buildrunner, you need to do the following:

#. Update the buildrunner configuration file
* Set ``use-legacy-builder`` to ``false``
* Add the secrets to the ``secrets`` section in the ``build`` section
#. Update the Dockerfile to use the secrets
* Add the ``--mount`` at the beginning of each RUN command that needs the secret

.. code:: yaml

use-legacy-builder: false
steps:
build-my-container:
build:
dockerfile: |
FROM alpine:latest
RUN --mount=type=secret,id=secret1 \
--mount=type=secret,id=secret2 \
SECRET1_FILE=/run/secrets/secret1 \
SECRET2_VARIABLE=$(cat /run/secrets/secret2) \
echo "Using secrets in my build - secret1: $(cat $SECRET1_FILE) secret2: $SECRET2_VARIABLE"
secrets:
# Example of a secret that is a file
- id=secret1,src=examples/build/secrets/secret1.txt
# Example of a secret that is an environment variable
- id=secret2,env=SECRET2







.. _Running Containers:

Expand Down
12 changes: 10 additions & 2 deletions buildrunner/config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class Config(BaseModel, extra="forbid"):

@field_validator("steps")
@classmethod
def validate_steps(cls, vals) -> None:
def validate_steps(cls, vals, info) -> None:
"""
Validate the config file

Expand All @@ -161,12 +161,20 @@ def validate_steps(cls, vals) -> None:
if not vals:
raise ValueError('The "steps" configuration was not provided')

# Checks to see if there is a mutli-platform build step in the config
# Checks steps for mutli-platform or secrets
has_multi_platform_build = False
has_secrets = False
for step in vals.values():
has_multi_platform_build = (
has_multi_platform_build or step.is_multi_platform()
)
has_secrets = has_secrets or step.has_secrets()

if has_secrets:
if info.data.get("use_legacy_builder"):
raise ValueError(
"Build secrets are not supported with the legacy builder. Please set use-legacy-builder to false in order to use secrets in your build."
)

if has_multi_platform_build:
mp_push_tags = set()
Expand Down
7 changes: 7 additions & 0 deletions buildrunner/config/models_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class StepBuild(StepTask):
cache_to: Optional[Union[str, Dict[str, str]]] = None
# import is a python reserved keyword so we need to alias it
import_param: Optional[str] = Field(alias="import", default=None)
secrets: Optional[List[str]] = None


class RunAndServicesBase(StepTask):
Expand Down Expand Up @@ -242,3 +243,9 @@ def is_multi_platform(self):
Check if the step is a multi-platform build step
"""
return self.build and self.build.platforms is not None

def has_secrets(self):
"""
Check if the step has secrets
"""
return self.build and self.build.secrets is not None
53 changes: 30 additions & 23 deletions buildrunner/docker/multiplatform_image_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def __init__(
cache_builders: Optional[List[str]] = None,
cache_from: Optional[Union[dict, str]] = None,
cache_to: Optional[Union[dict, str]] = None,
secrets: Optional[List[str]] = None,
):
self._docker_registry = docker_registry
self._build_registry = build_registry
Expand All @@ -89,6 +90,7 @@ def __init__(
self._cache_builders = set(cache_builders if cache_builders else [])
self._cache_from = cache_from
self._cache_to = cache_to
self._secrets = secrets
if self._cache_from or self._cache_to:
LOGGER.info(
f'Configuring multiplatform builds to cache from {cache_from} and to {cache_to} '
Expand Down Expand Up @@ -196,14 +198,11 @@ def _build_with_inject(
self,
inject: dict,
image_ref: str,
platform: str,
path: str,
platform: str,
dockerfile: str,
target: str,
build_args: dict,
builder: Optional[str],
cache: bool = False,
pull: bool = False,
build_kwargs: dict,
) -> None:
if not path or not os.path.isdir(path):
LOGGER.warning(
Expand Down Expand Up @@ -256,16 +255,13 @@ def _build_with_inject(

logs_itr = docker.buildx.build(
context_dir,
tags=[image_ref],
platforms=[platform],
load=True,
target=target,
builder=builder,
build_args=build_args,
cache=cache,
pull=pull,
load=True,
platforms=[platform],
stream_logs=True,
**self._get_build_cache_options(builder),
tags=[image_ref],
**build_kwargs,
**self._get_build_cache_options(build_kwargs.get("builder")),
)
self._log_buildx(logs_itr, platform)

Expand Down Expand Up @@ -294,6 +290,7 @@ def _build_single_image(
inject: dict,
cache: bool = False,
pull: bool = False,
secrets: Optional[List[str]] = None,
) -> None:
"""
Builds a single image for the given platform.
Expand All @@ -307,6 +304,7 @@ def _build_single_image(
target (str): The name of the stage to build in a multi-stage Dockerfile
build_args (dict): The build args to pass to docker.
inject (dict): The files to inject into the build context.
secrets (List[str]): The secrets to pass to docker.
"""
assert os.path.isdir(path) and os.path.exists(dockerfile), (
f"Either path {path} ({os.path.isdir(path)}) or file "
Expand All @@ -321,18 +319,28 @@ def _build_single_image(
f"Building image for platform {platform} with {builder or 'default'} builder"
)

# Build kwargs for the buildx build command
build_kwargs = {}
if builder:
build_kwargs["builder"] = builder
if cache:
build_kwargs["cache"] = cache
if pull:
build_kwargs["pull"] = pull
if secrets:
build_kwargs["secrets"] = secrets
if target:
build_kwargs["target"] = target

if inject and isinstance(inject, dict):
self._build_with_inject(
path=path,
inject=inject,
image_ref=image_ref,
platform=platform,
path=path,
dockerfile=dockerfile,
target=target,
build_args=build_args,
builder=builder,
cache=cache,
pull=pull,
build_kwargs=build_kwargs,
)
else:
logs_itr = docker.buildx.build(
Expand All @@ -341,12 +349,9 @@ def _build_single_image(
platforms=[platform],
load=True,
file=dockerfile,
target=target,
build_args=build_args,
builder=builder,
cache=cache,
pull=pull,
stream_logs=True,
**build_kwargs,
**self._get_build_cache_options(builder),
)
self._log_buildx(logs_itr, platform)
Expand Down Expand Up @@ -398,11 +403,12 @@ def build_multiple_images(
path: str = ".",
file: str = "Dockerfile",
target: Optional[str] = None,
use_threading: bool = True,
use_threading: bool = False,
build_args: dict = None,
inject: dict = None,
cache: bool = False,
pull: bool = False,
secrets: Optional[List[str]] = None,
) -> BuiltImageInfo:
"""
Builds multiple images for the given platforms. One image will be built for each platform.
Expand Down Expand Up @@ -498,6 +504,7 @@ def build_multiple_images(
inject,
cache,
pull,
secrets,
)
LOGGER.debug(f"Building {repo} for {platform}")
if use_threading:
Expand Down
1 change: 1 addition & 0 deletions buildrunner/steprunner/tasks/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ def run(self, context):
inject=self.to_inject,
cache=not self.nocache,
pull=self.pull,
secrets=self.step.secrets,
)

# Set expected number of platforms
Expand Down
21 changes: 21 additions & 0 deletions examples/build/secrets/buildrunner.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# In order to use secrets, you need to set use-legacy-builder to false in the config file
# To run this example, you need to set the SECRET_PASSWORD environment variable
# and run the example with the following command:
# SECRET2=my_secret ./run-buildrunner.sh -f examples/build/secrets/buildrunner.yaml
# More info about secrets: https://docs.docker.com/build/building/secrets/
use-legacy-builder: false
steps:
simple-build-step:
build:
dockerfile: |
FROM alpine:latest
RUN --mount=type=secret,id=secret1 \
--mount=type=secret,id=secret2 \
SECRET1_FILE=/run/secrets/secret1 \
SECRET2_VARIABLE=$(cat /run/secrets/secret2) \
echo "Using secrets in my build - secret1: $(cat $SECRET1_FILE) secret2: $SECRET2_VARIABLE"
secrets:
# Example of a secret that is a file
- id=secret1,src=examples/build/secrets/secret1.txt
# Example of a secret that is an environment variable
- id=secret2,env=SECRET2
1 change: 1 addition & 0 deletions examples/build/secrets/secret1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testuser123
16 changes: 0 additions & 16 deletions tests/test_multiplatform.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,8 @@ def test_build_multiple_builds(
load=True,
file=f"{test_path}/Dockerfile",
build_args={"DOCKER_REGISTRY": None},
builder=None,
cache=False,
cache_from=None,
cache_to=None,
pull=False,
target=None,
stream_logs=True,
),
call(
Expand All @@ -493,12 +489,8 @@ def test_build_multiple_builds(
load=True,
file=f"{test_path}/Dockerfile",
build_args={"DOCKER_REGISTRY": None},
builder=None,
cache=False,
cache_from=None,
cache_to=None,
pull=False,
target=None,
stream_logs=True,
),
call(
Expand All @@ -508,12 +500,8 @@ def test_build_multiple_builds(
load=True,
file=f"{test_path}/Dockerfile",
build_args={"DOCKER_REGISTRY": None},
builder=None,
cache=False,
cache_from=None,
cache_to=None,
pull=False,
target=None,
stream_logs=True,
),
call(
Expand All @@ -523,12 +511,8 @@ def test_build_multiple_builds(
load=True,
file=f"{test_path}/Dockerfile",
build_args={"DOCKER_REGISTRY": None},
builder=None,
cache=False,
cache_from=None,
cache_to=None,
pull=False,
target=None,
stream_logs=True,
),
]
Expand Down
Loading