From 0e88019bc5947dd11a418e56b8a5bbb92d062f7c Mon Sep 17 00:00:00 2001 From: Timo Furrer <tuxtimo@gmail.com> Date: Wed, 26 Feb 2025 14:51:08 +0100 Subject: [PATCH] Support input for auto definting the HTTP backend Closes https://gitlab.com/components/opentofu/-/issues/100 Changelog: added --- .gitlab/README.md.template | 10 ++++--- README.md | 13 +++++---- src/gitlab-tofu.sh | 27 ++++++++++++++++++- templates/apply.yml | 5 ++++ templates/destroy.yml | 5 ++++ templates/full-pipeline.yml | 10 +++++++ templates/graph.yml | 5 ++++ templates/job-templates.yml | 10 +++++++ templates/plan.yml | 5 ++++ templates/test.yml | 5 ++++ templates/validate-plan-apply.yml | 8 ++++++ templates/validate-plan-destroy.yml | 8 ++++++ templates/validate-plan.yml | 7 +++++ templates/validate.yml | 5 ++++ tests/iac-auto-define-backend/main.tf | 19 +++++++++++++ .../tests/main.tftest.hcl | 6 +++++ .../AutoDefineBackend.gitlab-ci.yml | 20 ++++++++++++++ tests/integration.gitlab-ci.yml | 17 ++++++++++++ 18 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 tests/iac-auto-define-backend/main.tf create mode 100644 tests/iac-auto-define-backend/tests/main.tftest.hcl create mode 100644 tests/integration-tests/AutoDefineBackend.gitlab-ci.yml diff --git a/.gitlab/README.md.template b/.gitlab/README.md.template index 53feabf..523109b 100644 --- a/.gitlab/README.md.template +++ b/.gitlab/README.md.template @@ -125,9 +125,10 @@ The base image OS can be specified with the `base_os` input. ### GitLab-managed Terraform state backend This component - by leveraging the [`gitlab-tofu`](src/gitlab-tofu.sh) CLI internally - -automatically configures the +can automatically define and configure the [GitLab-managed Terraform state backend](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html). -The only thing required is that the Terraform configuration must specify an empty `http` backend block, like this: + +By default the HTTP backend must be defined manually using the following HCL: ```hcl terraform { @@ -135,8 +136,9 @@ terraform { } ``` -We recommend having a dedicated `backend.tf` file inside your `root_dir` -with the aforementioned block. +However, you may simply enable the `auto_define_backend` so that the component takes care of this step. + +**Note**: in future versions of this component we may enable `auto_define_backend` by default. ### State and Plan Encryption diff --git a/README.md b/README.md index 88d55c0..82e05fa 100644 --- a/README.md +++ b/README.md @@ -127,9 +127,10 @@ The base image OS can be specified with the `base_os` input. ### GitLab-managed Terraform state backend This component - by leveraging the [`gitlab-tofu`](src/gitlab-tofu.sh) CLI internally - -automatically configures the +can automatically define and configure the [GitLab-managed Terraform state backend](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html). -The only thing required is that the Terraform configuration must specify an empty `http` backend block, like this: + +By default the HTTP backend must be defined manually using the following HCL: ```hcl terraform { @@ -137,8 +138,9 @@ terraform { } ``` -We recommend having a dedicated `backend.tf` file inside your `root_dir` -with the aforementioned block. +However, you may simply enable the `auto_define_backend` so that the component takes care of this step. + +**Note**: in future versions of this component we may enable `auto_define_backend` by default. ### State and Plan Encryption @@ -315,7 +317,8 @@ The following environment variables are respected by the `gitlab-tofu` script: - `GITLAB_TOFU_AUTO_ENCRYPTION`: if set to true, enables auto state and plan encryption. Defaults to `false`. - `GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE`: the passphrase to use for state and plan encryption. Required if `GITLAB_TOFU_AUTO_ENCRYPTION` is true. - `GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED_ENABLED`: if set to true, enables a fallback for state and plan encryption to migrate unencrypted plans and states to encrypted ones. Defaults to `false`. -- `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +- `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE`: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +- `GITLAB_TOFU_AUTO_DEFINE_BACKEND`: if set to true, automatically creates a file with a HTTP backend configuration block. #### Respected OpenTofu Environment Variables diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh index e5ecfc1..8a339e0 100644 --- a/src/gitlab-tofu.sh +++ b/src/gitlab-tofu.sh @@ -30,7 +30,8 @@ # - `GITLAB_TOFU_AUTO_ENCRYPTION`: if set to true, enables auto state and plan encryption. Defaults to `false`. # - `GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE`: the passphrase to use for state and plan encryption. Required if `GITLAB_TOFU_AUTO_ENCRYPTION` is true. # - `GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED_ENABLED`: if set to true, enables a fallback for state and plan encryption to migrate unencrypted plans and states to encrypted ones. Defaults to `false`. -# - `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +# - `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE`: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +# - `GITLAB_TOFU_AUTO_DEFINE_BACKEND`: if set to true, automatically creates a file with a HTTP backend configuration block. # # #### Respected OpenTofu Environment Variables # @@ -163,6 +164,7 @@ fi # ============================ # Backend related variables +auto_define_backend=${GITLAB_TOFU_AUTO_DEFINE_BACKEND:-false} backend_username="gitlab-ci-token" backend_password="${CI_JOB_TOKEN}" backend_state_name="$(jq -rn --arg x "${GITLAB_TOFU_STATE_NAME:-default}" '$x|@uri')" @@ -177,6 +179,8 @@ if [ -n "${GITLAB_TOFU_ROOT_DIR}" ]; then default_tf_plan_cache="${abs_tf_root}/${base_plan_name}.cache" default_tf_plan_json="${abs_tf_root}/${base_plan_name}.json" +else + abs_tf_root=$(realpath "${CI_PROJECT_DIR}") fi # Init related variables @@ -227,6 +231,26 @@ fi # Helper functions # ================ +# define_http_backend defines the HTTP backend in a file called __gitlab-opentofu-backend.tf if no backend can be found. +# The backend configuration is attempted to be found with a simple grep. +define_http_backend() { + if ! $auto_define_backend; then + return + fi + + if ! grep -q '^[[:space:]]*backend[[:space:]]\+"http"[[:space:]]\+{.*$' "${abs_tf_root}" -r 2>/dev/null; then + echo "gitlab-tofu: automatically defining the HTTP backend in __gitlab-opentofu-backend.tf. If that is a mistake, please disable it with the auto_define_backend: false input." + + cat <<EOF > __gitlab-opentofu-backend.tf +terraform { + backend "http" {} +} +EOF + else + echo "gitlab-tofu: auto_define_backend is enabled, but found manually configured HTTP backend, doing nothing." + fi +} + # configure_variables_for_tofu sets and exports all relevant variables for subsequent `tofu` command invocations. configure_variables_for_tofu() { # Use terraform automation mode (will remove some verbose unneeded messages) @@ -352,6 +376,7 @@ EOF } # We always want to configure the tofu variables, even in source-mode. +define_http_backend configure_variables_for_tofu configure_encryption_for_tofu diff --git a/templates/apply.yml b/templates/apply.yml index 9ee6039..3b98e55 100644 --- a/templates/apply.yml +++ b/templates/apply.yml @@ -98,6 +98,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -124,6 +128,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/destroy.yml b/templates/destroy.yml index 90b1e55..fa350f8 100644 --- a/templates/destroy.yml +++ b/templates/destroy.yml @@ -102,6 +102,10 @@ spec: default: false type: boolean description: 'Whether to automatically delete the Terraform state. This only makes sense when using the GitLab-managed state backend. It is equivalent to running the delete-state job.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -128,6 +132,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/full-pipeline.yml b/templates/full-pipeline.yml index f4f22f1..290e81b 100644 --- a/templates/full-pipeline.yml +++ b/templates/full-pipeline.yml @@ -198,6 +198,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -236,6 +240,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/test.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "true"' @@ -259,6 +264,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -282,6 +288,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -302,6 +309,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -321,6 +329,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -408,6 +417,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/graph.yml b/templates/graph.yml index 02da517..c78cde0 100644 --- a/templates/graph.yml +++ b/templates/graph.yml @@ -96,6 +96,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -116,6 +120,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/job-templates.yml b/templates/job-templates.yml index 387f02f..eaa2d30 100644 --- a/templates/job-templates.yml +++ b/templates/job-templates.yml @@ -103,6 +103,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -134,6 +138,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/graph.yml' inputs: as: '$[[ inputs.job_name_prefix ]]graph' @@ -148,6 +153,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/test.yml' inputs: as: '$[[ inputs.job_name_prefix ]]test' @@ -164,6 +170,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' inputs: as: '$[[ inputs.job_name_prefix ]]plan' @@ -183,6 +190,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/apply.yml' inputs: as: '$[[ inputs.job_name_prefix ]]apply' @@ -200,6 +208,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/destroy.yml' inputs: as: '$[[ inputs.job_name_prefix ]]destroy' @@ -217,6 +226,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/delete-state.yml' inputs: as: '$[[ inputs.job_name_prefix ]]delete-state' diff --git a/templates/plan.yml b/templates/plan.yml index 752c37c..73b4a51 100644 --- a/templates/plan.yml +++ b/templates/plan.yml @@ -109,6 +109,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -175,6 +179,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: '$[[ inputs.allow_developer_role ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/test.yml b/templates/test.yml index 79b2b0b..a76e329 100644 --- a/templates/test.yml +++ b/templates/test.yml @@ -98,6 +98,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -119,6 +123,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/validate-plan-apply.yml b/templates/validate-plan-apply.yml index 6924516..f1c5005 100644 --- a/templates/validate-plan-apply.yml +++ b/templates/validate-plan-apply.yml @@ -162,6 +162,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -201,6 +205,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -225,6 +230,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -246,6 +252,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -303,6 +310,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml index c6a54b7..1d303fd 100644 --- a/templates/validate-plan-destroy.yml +++ b/templates/validate-plan-destroy.yml @@ -168,6 +168,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -207,6 +211,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -232,6 +237,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -254,6 +260,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -336,6 +343,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan.yml b/templates/validate-plan.yml index 96d199e..ed976b6 100644 --- a/templates/validate-plan.yml +++ b/templates/validate-plan.yml @@ -146,6 +146,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -185,6 +189,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -209,6 +214,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -263,6 +269,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate.yml b/templates/validate.yml index e4dcb8e..14154a1 100644 --- a/templates/validate.yml +++ b/templates/validate.yml @@ -95,6 +95,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -116,6 +120,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/tests/iac-auto-define-backend/main.tf b/tests/iac-auto-define-backend/main.tf new file mode 100644 index 0000000..6f610dc --- /dev/null +++ b/tests/iac-auto-define-backend/main.tf @@ -0,0 +1,19 @@ +resource "local_file" "foo" { + content = "foo!" + filename = "${path.module}/foo.bar" +} + +locals { + ts = plantimestamp() +} + +// NOTE: always force a change. +resource "null_resource" "this" { + triggers = { + timestamp = local.ts + } +} + +output "this_always_changes" { + value = local.ts +} diff --git a/tests/iac-auto-define-backend/tests/main.tftest.hcl b/tests/iac-auto-define-backend/tests/main.tftest.hcl new file mode 100644 index 0000000..fedf96e --- /dev/null +++ b/tests/iac-auto-define-backend/tests/main.tftest.hcl @@ -0,0 +1,6 @@ +run "test" { + assert { + condition = file(local_file.foo.filename) == "foo!" + error_message = "Incorrect content in ${local_file.foo.filename}" + } +} diff --git a/tests/integration-tests/AutoDefineBackend.gitlab-ci.yml b/tests/integration-tests/AutoDefineBackend.gitlab-ci.yml new file mode 100644 index 0000000..4a64e52 --- /dev/null +++ b/tests/integration-tests/AutoDefineBackend.gitlab-ci.yml @@ -0,0 +1,20 @@ +include: + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/full-pipeline@$CI_COMMIT_SHA + inputs: + image_registry_base: $GITLAB_OPENTOFU_IMAGE_BASE + version: $CI_COMMIT_SHA + base_os: $GITLAB_OPENTOFU_BASE_IMAGE_OS + opentofu_version: $OPENTOFU_VERSION + root_dir: $TEST_GITLAB_TOFU_ROOT_DIR + state_name: $TEST_GITLAB_TOFU_STATE_NAME + auto_define_backend: true + # Required to run everything immediately, instead of manually. + fmt_rules: [{when: always}] + validate_rules: [{when: always}] + test_rules: [{when: always}] + plan_rules: [{when: always}] + apply_rules: [{when: always}] + destroy_rules: [{when: always}] + delete_state_rules: [{when: always}] + +stages: [validate, test, build, deploy, cleanup] diff --git a/tests/integration.gitlab-ci.yml b/tests/integration.gitlab-ci.yml index c9298a5..f5ed0b8 100644 --- a/tests/integration.gitlab-ci.yml +++ b/tests/integration.gitlab-ci.yml @@ -17,6 +17,23 @@ full-pipeline: - alpine - debian +auto-define-backend: + stage: test-integration + variables: + OPENTOFU_VERSION: $LATEST_OPENTOFU_VERSION + TEST_GITLAB_TOFU_STATE_NAME: ci-integration-$CI_JOB_NAME_SLUG-$CI_PIPELINE_IID-$CI_NODE_INDEX + TEST_GITLAB_TOFU_ROOT_DIR: tests/iac-auto-define-backend + trigger: + include: tests/integration-tests/$PIPELINE_NAME.gitlab-ci.yml + strategy: depend + parallel: + matrix: + - PIPELINE_NAME: + - AutoDefineBackend + GITLAB_OPENTOFU_BASE_IMAGE_OS: + - alpine + - debian + validate-plan-apply: stage: test-integration variables: -- GitLab