From 1769e0de1d8ce01b0460e3fd03beb39a678dedba Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:59:05 +0100 Subject: [PATCH] build: add variable validation in bake Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com> --- content/manuals/build/bake/overrides.md | 54 ++++++++++----- content/manuals/build/bake/variables.md | 88 +++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 17 deletions(-) diff --git a/content/manuals/build/bake/overrides.md b/content/manuals/build/bake/overrides.md index 2028650f16af..c4fc5bb2a77c 100644 --- a/content/manuals/build/bake/overrides.md +++ b/content/manuals/build/bake/overrides.md @@ -288,43 +288,63 @@ which is the short commit hash generated by `git rev-parse --short HEAD`. Overriding non-string variables with environment variables is supported. Values passed as environment variables are coerced into suitable types first. -The following example defines a `PORT` variable with a default value of `8080`. -The `default` target uses a [ternary operator](expressions.md#ternary-operators) -to set the `PORT` variable to the value of the environment variable `PORT` -if it is greater than `1024`, otherwise it uses the default value. - -In this case, the `PORT` variable is coerced to an integer before the ternary -operator is evaluated. +The following example defines a `PORT` variable. The `backend` target uses the +`PORT` variable as-is, and the `frontend` target uses the value of `PORT` +incremented by one. ```hcl -default_port = 8080 - variable "PORT" { - default = default_port + default = 3000 } -target "default" { +group "default" { + targets = ["backend", "frontend"] +} + +target "backend" { args = { - PORT = PORT > 1024 ? PORT : default_port + PORT = PORT + } +} + +target "frontend" { + args = { + PORT = add(PORT, 1) } } ``` -Attempting to set the `PORT` variable with a value less than `1024` will result -in the default value being used. +Overriding `PORT` using an environment variable will first coerce the value +into the expected type, an integer, before the expression in the `frontend` +target runs. ```console -$ PORT=80 docker buildx bake --print +$ PORT=7070 docker buildx bake --print ``` ```json { - "target": { + "group": { "default": { + "targets": [ + "backend", + "frontend" + ] + } + }, + "target": { + "backend": { + "context": ".", + "dockerfile": "Dockerfile", + "args": { + "PORT": "7070" + } + }, + "frontend": { "context": ".", "dockerfile": "Dockerfile", "args": { - "PORT": "8080" + "PORT": "7071" } } } diff --git a/content/manuals/build/bake/variables.md b/content/manuals/build/bake/variables.md index b584cc1483a2..0db49d53718d 100644 --- a/content/manuals/build/bake/variables.md +++ b/content/manuals/build/bake/variables.md @@ -78,6 +78,94 @@ $ docker buildx bake --print } ``` +## Validating variables + +To verify that the value of a variable conforms to an expected type, value +range, or other condition, you can define custom validation rules using the +`validation` block. + +In the following example, validation is used to enforce a numeric constraint on +a variable value; the `PORT` variable must be 1024 or higher. + +```hcl +# Define a variable `PORT` with a default value and a validation rule +variable "PORT" { + default = 3000 # Default value assigned to `PORT` + + # Validation block to ensure `PORT` is a valid number within the acceptable range + validation { + condition = PORT >= 1024 # Ensure `PORT` is at least 1024 + error_message = "The variable 'PORT' must be 1024 or higher." # Error message for invalid values + } +} +``` + +If the `condition` expression evaluates to `false`, the variable value is +considered invalid, whereby the build invocation fails and `error_message` is +emitted. For example, if `PORT=443`, the condition evaluates to `false`, and +the error is raised. + +Values are coerced into the expected type before the validation is set. This +ensures that any overrides set with environment variables work as expected. + +### Validate multiple conditions + +To evaluate more than one condition, define multiple `validation` blocks for +the variable. All conditions must be `true`. + +Here’s an example: + +```hcl +# Define a variable `VAR` with multiple validation rules +variable "VAR" { + # First validation block: Ensure the variable is not empty + validation { + condition = VAR != "" + error_message = "The variable 'VAR' must not be empty." + } + + # Second validation block: Ensure the value contains only alphanumeric characters + validation { + # VAR and the regex match must be identical: + condition = VAR == regex("[a-zA-Z0-9]+", VAR) + error_message = "The variable 'VAR' can only contain letters and numbers." + } +} +``` + +This example enforces: + +- The variable must not be empty. +- The variable must match a specific character set. + +For invalid inputs like `VAR="hello@world"`, the validation would fail. + +### Validating variable dependencies + +You can reference other Bake variables in your condition expression, enabling +validations that enforce dependencies between variables. This ensures that +dependent variables are set correctly before proceeding. + +Here’s an example: + +```hcl +# Define a variable `FOO` +variable "FOO" {} + +# Define a variable `BAR` with a validation rule that references `FOO` +variable "BAR" { + # Validation block to ensure `FOO` is set if `BAR` is used + validation { + condition = FOO != "" # Check if `FOO` is not an empty string + error_message = "The variable 'BAR' requires 'FOO' to be set." + } +} +``` + +This configuration ensures that the `BAR` variable can only be used if `FOO` +has been assigned a non-empty value. Attempting to build without setting `FOO` +will trigger the validation error. + ## Escape variable interpolation If you want to bypass variable interpolation when parsing the Bake definition,