Skip to content
Snippets Groups Projects
Commit 0dfe066f authored by Timo Furrer's avatar Timo Furrer
Browse files

Merge branch 'destroy' into 'main'

Introduce destroy input for plan job

See merge request components/opentofu!79
parents 3b5f6e73 58553856
No related branches found
No related tags found
No related merge requests found
......@@ -105,6 +105,7 @@ but no destructive actions.
- [`validate-plan`](templates/validate-plan.yml)
- [`validate-plan-apply`](templates/validate-plan-apply.yml)
- [`validate-plan-destroy`](templates/validate-plan-destroy.yml)
### Job Templates
......
......@@ -107,6 +107,7 @@ but no destructive actions.
- [`validate-plan`](templates/validate-plan.yml)
- [`validate-plan-apply`](templates/validate-plan-apply.yml)
- [`validate-plan-destroy`](templates/validate-plan-destroy.yml)
### Job Templates
......
......@@ -16,6 +16,10 @@ if [ -z "$TF_FF_AUTO_URLENCODE_STATE_NAME" ]; then
TF_FF_AUTO_URLENCODE_STATE_NAME=true
fi
if [ -z "$TF_FF_AUTO_APPROVE_APPLY" ]; then
TF_FF_AUTO_APPROVE_APPLY=true
fi
# Helpers
# Evaluate if this script is being sourced or executed directly.
......@@ -62,25 +66,33 @@ if [ -n "${TF_STATE_NAME}" ] && [ -z "${TF_ADDRESS}" ]; then
TF_ADDRESS="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}"
fi
if [ -z "${TF_PLAN_NAME}" ]; then
TF_PLAN_NAME=plan
fi
if [ -z "${TF_APPLY_NO_PLAN}" ]; then
TF_APPLY_NO_PLAN=false
fi
# If TF_ROOT is set then use the -chdir option
if [ -n "${TF_ROOT}" ]; then
abs_tf_root=$(cd "${CI_PROJECT_DIR}"; realpath "${TF_ROOT}")
TF_CHDIR_OPT="-chdir=${abs_tf_root}"
default_tf_plan_cache="${abs_tf_root}/plan.cache"
default_tf_plan_json="${abs_tf_root}/plan.json"
default_tf_plan_cache="${abs_tf_root}/${TF_PLAN_NAME}.cache"
default_tf_plan_json="${abs_tf_root}/${TF_PLAN_NAME}.json"
fi
# If TF_PLAN_CACHE is not set then use either the plan.cache file within TF_ROOT if set, or plan.cache in CWD
if [ -z "${TF_PLAN_CACHE}" ]; then
TF_PLAN_CACHE="${default_tf_plan_cache:-plan.cache}"
TF_PLAN_CACHE="${default_tf_plan_cache:-${TF_PLAN_NAME}.cache}"
fi
# If TF_PLAN_JSON is not set then use either the plan.json file within TF_ROOT if set, or plan.json in CWD
if [ -z "${TF_PLAN_JSON}" ]; then
TF_PLAN_JSON="${default_tf_plan_json:-plan.json}"
TF_PLAN_JSON="${default_tf_plan_json:-${TF_PLAN_NAME}.json}"
fi
# Set variables for the HTTP backend to default to TF_* values
......@@ -157,8 +169,17 @@ if [ $sourced -eq 0 ]; then
case "${1}" in
"apply")
auto_approve_args=""
if [ "${TF_FF_AUTO_APPROVE_APPLY}" = true ]; then
auto_approve_args="-auto-approve"
fi
$TF_IMPLICIT_INIT && terraform_init
tofu "${TF_CHDIR_OPT}" "${@}" -input=false "${TF_PLAN_CACHE}"
if [ "$TF_APPLY_NO_PLAN" = false ]; then
tofu "${TF_CHDIR_OPT}" "${@}" -input=false "${auto_approve_args}" "${TF_PLAN_CACHE}"
else
tofu "${TF_CHDIR_OPT}" "${@}" -input=false "${auto_approve_args}"
fi
;;
"destroy")
$TF_IMPLICIT_INIT && terraform_init
......
......@@ -50,6 +50,13 @@ spec:
state_name:
default: default
description: 'Remote OpenTofu state name.'
no_plan:
default: false
type: boolean
description: 'Whether a plan file should be used.'
plan_name:
default: 'plan'
description: 'The name of the plan file to use. Will be used for TF_PLAN_CACHE and TF_PLAN_JSON.'
auto_apply:
default: false
type: boolean
......@@ -76,6 +83,8 @@ spec:
__CACHE_KEY_HACK: "$[[ inputs.root_dir ]]"
TF_ROOT: $[[ inputs.root_dir ]]
TF_STATE_NAME: $[[ inputs.state_name ]]
TF_APPLY_NO_PLAN: $[[ inputs.no_plan ]]
TF_PLAN_NAME: $[[ inputs.plan_name ]]
image:
name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]'
script:
......
......@@ -50,6 +50,13 @@ spec:
state_name:
default: default
description: 'Remote OpenTofu state name.'
no_plan:
default: true
type: boolean
description: 'Whether a plan file should be used.'
plan_name:
default: 'destroy-plan'
description: 'The name of the plan file to use. Will be used for TF_PLAN_CACHE and TF_PLAN_JSON.'
auto_destroy:
default: false
type: boolean
......@@ -75,7 +82,9 @@ spec:
__CACHE_KEY_HACK: "$[[ inputs.root_dir ]]"
TF_ROOT: $[[ inputs.root_dir ]]
TF_STATE_NAME: $[[ inputs.state_name ]]
TF_APPLY_NO_PLAN: $[[ inputs.no_plan ]]
TF_PLAN_NAME: $[[ inputs.plan_name ]]
image:
name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]'
script:
- gitlab-tofu destroy
- gitlab-tofu apply -destroy
......@@ -50,6 +50,13 @@ spec:
state_name:
default: default
description: 'Remote OpenTofu state name.'
plan_name:
default: 'plan'
description: 'The name of the plan cache and plan json file.'
destroy:
default: false
type: boolean
description: 'Indicate if the plan should be a destroy plan. You may want to change the `plan_name` input to `destroy-plan` which is the default for the destroy job.'
---
......@@ -68,9 +75,9 @@ spec:
# See: https://docs.gitlab.com/ee/ci/yaml/#artifactspublic
public: false
paths:
- $TF_ROOT/plan.cache
- $TF_ROOT/$[[ inputs.plan_name ]].cache
reports:
terraform: $TF_ROOT/plan.json
terraform: $TF_ROOT/$[[ inputs.plan_name]].json
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.
......@@ -85,8 +92,15 @@ spec:
__CACHE_KEY_HACK: "$[[ inputs.root_dir ]]"
TF_ROOT: $[[ inputs.root_dir ]]
TF_STATE_NAME: $[[ inputs.state_name ]]
TF_PLAN_NAME: $[[ inputs.plan_name ]]
image:
name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]'
script:
- gitlab-tofu plan
- |
args=""
if [ "$[[ inputs.destroy ]]" == "true" ]; then
echo "Planning for a destroy"
args="-destroy"
fi
- gitlab-tofu plan $args
- gitlab-tofu plan-json
spec:
inputs:
# Stages
stage_validate:
default: 'validate'
description: 'Defines the validate stage. This stage includes the `fmt` and `validate` jobs.'
stage_build:
default: 'build'
description: 'Defines the build stage. This stage includes the `plan` job.'
stage_cleanup:
default: 'cleanup'
description: 'Defines the cleanup stage. This stage includes the `destroy` and `delete-state` jobs.'
# 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.7.1'
options:
- '$OPENTOFU_VERSION'
- '1.7.1'
- '1.7.0'
- '1.7.0-alpha1'
- '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.'
plan_name:
default: 'destroy-plan'
description: 'Destroy plan file name.'
auto_destroy:
default: false
type: boolean
description: 'Whether the destroy job is manual or automatically run.'
---
include:
- local: '/templates/fmt.yml'
inputs:
as: 'fmt'
stage: $[[ inputs.stage_validate ]]
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 ]]
- local: '/templates/validate.yml'
inputs:
as: 'validate'
stage: $[[ inputs.stage_validate ]]
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: 'plan'
stage: $[[ inputs.stage_build ]]
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 ]]
plan_name: $[[ inputs.plan_name ]]
destroy: true
- local: '/templates/destroy.yml'
inputs:
as: 'destroy'
stage: $[[ inputs.stage_cleanup ]]
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 ]]
no_plan: false
plan_name: $[[ inputs.plan_name ]]
auto_destroy: $[[ inputs.auto_destroy ]]
- local: '/templates/delete-state.yml'
inputs:
as: 'delete-state'
stage: $[[ inputs.stage_cleanup ]]
state_name: $[[ inputs.state_name ]]
# NOTE: we have to define this `needs` here, because inputs don't support arrays, yet.
delete-state:
needs: [destroy]
include:
- component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/apply@$CI_COMMIT_SHA
inputs:
image_registry_base: $GITLAB_OPENTOFU_IMAGE_BASE
version: $CI_COMMIT_SHA
opentofu_version: $OPENTOFU_VERSION
as: 'setup:apply'
stage: setup
root_dir: $TEST_TF_ROOT
state_name: $TEST_TF_STATE_NAME
no_plan: true
auto_apply: true
- component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/validate-plan-destroy@$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: [setup, validate, build, cleanup]
# Required to run everything immediately, instead of manually.
'setup:apply':
rules: [{when: always}]
fmt:
rules: [{when: always}]
validate:
rules: [{when: always}]
plan:
rules: [{when: always}]
destroy:
rules: [{when: always}]
delete-state:
rules: [{when: always}]
......@@ -12,3 +12,4 @@ component:
- PIPELINE_NAME: [Defaults]
- PIPELINE_NAME: [JobTemplates]
- PIPELINE_NAME: [TestJob]
- PIPELINE_NAME: [Destroy]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment