diff --git a/.gitlab/README.md.template b/.gitlab/README.md.template index 1f2172898790ba0fb387eaa83bc28efea76d2257..f231b2bb4fd7f73cfee1eb4a11d1f432751ee0ad 100644 --- a/.gitlab/README.md.template +++ b/.gitlab/README.md.template @@ -37,7 +37,7 @@ include: version: <VERSION> opentofu_version: <OPENTOFU_VERSION> -stages: [validate, build, deploy, cleanup] +stages: [validate, test, build, deploy, cleanup] --- @@ -67,7 +67,7 @@ include: version: 0.10.0 opentofu_version: 1.6.1 -stages: [validate, build, deploy, cleanup] +stages: [validate, test, build, deploy, cleanup] --- @@ -82,7 +82,7 @@ include: version: latest opentofu_version: 1.6.1 -stages: [validate, build, deploy, cleanup] +stages: [validate, test, build, deploy, cleanup] ``` Or import all jobs as hidden templates ready to be extended: @@ -161,6 +161,7 @@ The following job components exist: - [`fmt`](templates/fmt.yml) - [`validate`](templates/validate.yml) +- [`test`](templates/test.yml) - [`plan`](templates/plan.yml) - [`apply`](templates/apply.yml) - [`destroy`](templates/destroy.yml) diff --git a/README.md b/README.md index 9f538bd033d3d6e5dba7409ca6a356c40ea4c840..650fbd1048f685f2a08abe3377335804fe889997 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ include: version: <VERSION> opentofu_version: <OPENTOFU_VERSION> -stages: [validate, build, deploy, cleanup] +stages: [validate, test, build, deploy, cleanup] --- @@ -69,7 +69,7 @@ include: version: 0.10.0 opentofu_version: 1.6.1 -stages: [validate, build, deploy, cleanup] +stages: [validate, test, build, deploy, cleanup] --- @@ -84,7 +84,7 @@ include: version: latest opentofu_version: 1.6.1 -stages: [validate, build, deploy, cleanup] +stages: [validate, test, build, deploy, cleanup] ``` Or import all jobs as hidden templates ready to be extended: @@ -163,6 +163,7 @@ The following job components exist: - [`fmt`](templates/fmt.yml) - [`validate`](templates/validate.yml) +- [`test`](templates/test.yml) - [`plan`](templates/plan.yml) - [`apply`](templates/apply.yml) - [`destroy`](templates/destroy.yml) @@ -176,6 +177,7 @@ Have a look at the individual template spec to learn about the available inputs. | Name | Default | Description | | ---- | ------- | ----------- | | `stage_validate` | `validate` | Defines the validate stage. This stage includes the `fmt` and `validate` jobs. | +| `stage_test` | `test` | Defines the test stage. This stage includes the `test` job. | | `stage_build` | `build` | Defines the build stage. This stage includes the `plan` job. | | `stage_deploy` | `deploy` | Defines the deploy stage. This stage includes the `apply` job. | | `stage_cleanup` | `cleanup` | Defines the cleanup stage. This stage includes the `destroy` and `delete-state` jobs. | diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh index 7f3307396a5cfa82455d33b83863c611a31aa418..aa62e139e4c3d6a34ccedc075806ff2a8bad302d 100644 --- a/src/gitlab-tofu.sh +++ b/src/gitlab-tofu.sh @@ -172,6 +172,10 @@ if [ $sourced -eq 0 ]; then $TF_IMPLICIT_INIT && terraform_init -backend=false tofu "${TF_CHDIR_OPT}" "${@}" ;; + "test") + $TF_IMPLICIT_INIT && terraform_init -backend=false + tofu "${TF_CHDIR_OPT}" "${@}" + ;; --) shift tofu "${TF_CHDIR_OPT}" "${@}" diff --git a/templates/full-pipeline.yml b/templates/full-pipeline.yml index a2ae99f9e12043c5c2cce5504fdd1b3b694db1cb..38a1be142cbddb5376657597f291c08b5602f87e 100644 --- a/templates/full-pipeline.yml +++ b/templates/full-pipeline.yml @@ -4,6 +4,9 @@ spec: stage_validate: default: 'validate' description: 'Defines the validate stage. This stage includes the `fmt` and `validate` jobs.' + stage_test: + default: 'test' + description: 'Defines the test stage. This stage includes the `test` job.' stage_build: default: 'build' description: 'Defines the build stage. This stage includes the `plan` job.' @@ -82,6 +85,19 @@ include: image_name: $[[ inputs.image_name ]] root_dir: $[[ inputs.root_dir ]] state_name: $[[ inputs.state_name ]] + - local: '/templates/test.yml' + inputs: + as: 'test' + stage: $[[ inputs.stage_test ]] + version: $[[ inputs.version ]] + opentofu_version: $[[ inputs.opentofu_version ]] + image_registry_base: $[[ inputs.image_registry_base ]] + image_name: $[[ inputs.image_name ]] + root_dir: $[[ inputs.root_dir ]] + state_name: $[[ inputs.state_name ]] + rules: + - exists: + - $[[ inputs.root_dir ]]/**/*.tftest.hcl - local: '/templates/plan.yml' inputs: as: 'plan' diff --git a/templates/job-templates.yml b/templates/job-templates.yml index 7a77ef50ae7c0e9828f7e756dfc4da68f61a44ce..f8f8aa1199c2373131d41bbb2afd04e4c732abbe 100644 --- a/templates/job-templates.yml +++ b/templates/job-templates.yml @@ -4,6 +4,9 @@ spec: stage_validate: default: 'validate' description: 'Defines the validate stage. This stage includes the `fmt` and `validate` jobs.' + stage_test: + default: 'test' + description: 'Defines the test stage. This stage includes the `test` job.' stage_build: default: 'build' description: 'Defines the build stage. This stage includes the `plan` job.' @@ -85,6 +88,16 @@ include: image_name: $[[ inputs.image_name ]] root_dir: $[[ inputs.root_dir ]] state_name: $[[ inputs.state_name ]] + - local: '/templates/test.yml' + inputs: + as: '$[[ inputs.job_name_prefix ]]test' + stage: $[[ inputs.stage_test ]] + version: $[[ inputs.version ]] + opentofu_version: $[[ inputs.opentofu_version ]] + image_registry_base: $[[ inputs.image_registry_base ]] + image_name: $[[ inputs.image_name ]] + root_dir: $[[ inputs.root_dir ]] + state_name: $[[ inputs.state_name ]] - local: '/templates/plan.yml' inputs: as: '$[[ inputs.job_name_prefix ]]plan' diff --git a/templates/test.yml b/templates/test.yml new file mode 100644 index 0000000000000000000000000000000000000000..be0cbe8cf0ae994246bcd4da7b211b9a7c352db6 --- /dev/null +++ b/templates/test.yml @@ -0,0 +1,73 @@ +spec: + inputs: + # Job and Stage name + as: + default: 'test' + description: 'Defines the name of this job.' + stage: + default: 'test' + description: 'Defines the stage that this job will belong to.' + + # Versions + # This version is only required, because we cannot access the context of the component, + # see https://gitlab.com/gitlab-org/gitlab/-/issues/438275 + version: + default: 'latest' + description: 'Version of this component. Has to be the same as the one in the component include entry.' + + opentofu_version: + default: '1.6.2' + options: + - '$OPENTOFU_VERSION' + - '1.6.2' + - '1.6.1' + - '1.6.0' + description: 'OpenTofu version that should be used.' + + # Images + image_registry_base: + default: '$CI_REGISTRY/components/opentofu' + description: 'Host URI to the job images. Will be combined with `image_name` to construct the actual image URI.' + # FIXME: not yet possible because of https://gitlab.com/gitlab-org/gitlab/-/issues/438722 + # gitlab_opentofu_image: + # # FIXME: This should reference the component tag that is used. + # # Currently, blocked by https://gitlab.com/gitlab-org/gitlab/-/issues/438275 + # # default: '$CI_REGISTRY/components/opentofu/gitlab-opentofu:$[[ inputs.opentofu_version ]]' + # default: '$CI_REGISTRY/components/opentofu/gitlab-opentofu:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]' + # description: 'Tag of the gitlab-opentofu image.' + + image_name: + default: 'gitlab-opentofu' + description: 'Image name for the job images. Hosted under `image_registry_base`.' + + # Configuration + root_dir: + default: ${CI_PROJECT_DIR} + description: 'Root directory for the OpenTofu project.' + state_name: + default: default + description: 'Remote OpenTofu state name.' + +--- + +'$[[ inputs.as ]]': + stage: $[[ inputs.stage ]] + needs: [] + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_OPEN_MERGE_REQUESTS # Don't add it to a *branch* pipeline if it's already in a merge request pipeline. + when: never + - if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead. + cache: + key: "$__CACHE_KEY_HACK" + paths: + - $TF_ROOT/.terraform/ + variables: + # FIXME: work around to make slashes work in `cache:key`. see https://gitlab.com/gitlab-org/gitlab/-/issues/439898 + __CACHE_KEY_HACK: "$[[ inputs.root_dir ]]" + TF_ROOT: $[[ inputs.root_dir ]] + TF_STATE_NAME: $[[ inputs.state_name ]] + image: + name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]' + script: + - gitlab-tofu test diff --git a/tests/iac/modules/random-pet/main.tf b/tests/iac/modules/random-pet/main.tf index ad50e07e95d3bf9f0c9ccb0e1c942583c8d6f358..382c4e673853109279a8d53323b2aad80a8848e2 100644 --- a/tests/iac/modules/random-pet/main.tf +++ b/tests/iac/modules/random-pet/main.tf @@ -7,8 +7,6 @@ terraform { } } -provider "random" {} - resource "random_pet" "random_pet" { length = var.length } diff --git a/tests/iac/tests/main.tftest.hcl b/tests/iac/tests/main.tftest.hcl new file mode 100644 index 0000000000000000000000000000000000000000..fedf96e4c458eee084740b05980a52ffe64362a1 --- /dev/null +++ b/tests/iac/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/Defaults.gitlab-ci.yml b/tests/integration-tests/Defaults.gitlab-ci.yml index 4e856f3a4093ed6ce4f91a7aa24b8f32b882cd0c..0b85e6eb539de02fc8875b3cda43f257c20db60b 100644 --- a/tests/integration-tests/Defaults.gitlab-ci.yml +++ b/tests/integration-tests/Defaults.gitlab-ci.yml @@ -17,6 +17,9 @@ fmt: validate: rules: [{when: always}] +test: + rules: [{when: always}] + plan: rules: [{when: always}] diff --git a/tests/integration-tests/JobTemplates.gitlab-ci.yml b/tests/integration-tests/JobTemplates.gitlab-ci.yml index e552295efc4d66997fb478d65b816838452dd4b3..7d2b47b6961409e050c2b0d3c764187bcd700ad9 100644 --- a/tests/integration-tests/JobTemplates.gitlab-ci.yml +++ b/tests/integration-tests/JobTemplates.gitlab-ci.yml @@ -7,7 +7,7 @@ include: root_dir: $TEST_TF_ROOT state_name: $TEST_TF_STATE_NAME -stages: [validate, test, build, deploy, cleanup] +stages: [validate, build, deploy, cleanup] # Required to run everything immediately, instead of manually. diff --git a/tests/integration-tests/TestJob.gitlab-ci.yml b/tests/integration-tests/TestJob.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..05b8144592a52fb379795bfbd86ae57aa0d93080 --- /dev/null +++ b/tests/integration-tests/TestJob.gitlab-ci.yml @@ -0,0 +1,15 @@ +include: + - component: gitlab.com/$CI_PROJECT_PATH/test@$CI_COMMIT_SHA + inputs: + image_registry_base: $GITLAB_OPENTOFU_IMAGE_BASE + version: $CI_COMMIT_SHA + opentofu_version: $OPENTOFU_VERSION + root_dir: $TEST_TF_ROOT + state_name: $TEST_TF_STATE_NAME + +stages: [test] + +# Required to run everything immediately, instead of manually. + +test: + rules: [{when: always}] diff --git a/tests/integration.gitlab-ci.yml b/tests/integration.gitlab-ci.yml index e72224df461f3338c4f725fe4820bff039b37277..2c71401d87f48a9a6ea7f953bc7194b75c5662e9 100644 --- a/tests/integration.gitlab-ci.yml +++ b/tests/integration.gitlab-ci.yml @@ -11,6 +11,7 @@ component: matrix: - PIPELINE_NAME: [Defaults] - PIPELINE_NAME: [JobTemplates] + - PIPELINE_NAME: [TestJob] backport-templates: stage: test-integration