From 6d1b76c1c3f430f12f52760a6d4bb1a6f4d2e592 Mon Sep 17 00:00:00 2001
From: Timo Furrer <tfurrer@gitlab.com>
Date: Fri, 31 May 2024 08:23:53 +0200
Subject: [PATCH] Introduce plan name

---
 src/gitlab-tofu.sh                  | 22 +++++++++++++++++-----
 templates/apply.yml                 |  4 ++++
 templates/destroy.yml               |  9 +++++++++
 templates/plan.yml                  | 10 +++++++---
 templates/validate-plan-destroy.yml |  6 ++++++
 5 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh
index d168c58..9282763 100644
--- a/src/gitlab-tofu.sh
+++ b/src/gitlab-tofu.sh
@@ -62,25 +62,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
@@ -158,7 +166,11 @@ if [ $sourced -eq 0 ]; then
   case "${1}" in
     "apply")
       $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 "${TF_PLAN_CACHE}"
+      else
+        tofu "${TF_CHDIR_OPT}" "${@}" -input=false
+      fi
     ;;
     "destroy")
       $TF_IMPLICIT_INIT && terraform_init
diff --git a/templates/apply.yml b/templates/apply.yml
index b4c321f..235c6e7 100644
--- a/templates/apply.yml
+++ b/templates/apply.yml
@@ -50,6 +50,9 @@ spec:
     state_name:
       default: default
       description: 'Remote OpenTofu state name.'
+    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 +79,7 @@ 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:
diff --git a/templates/destroy.yml b/templates/destroy.yml
index 8c26613..cd9a743 100644
--- a/templates/destroy.yml
+++ b/templates/destroy.yml
@@ -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,6 +82,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:
diff --git a/templates/plan.yml b/templates/plan.yml
index bcaed74..a28b9c6 100644
--- a/templates/plan.yml
+++ b/templates/plan.yml
@@ -50,10 +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'
+      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.'
 
 ---
 
@@ -72,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.
@@ -89,6 +92,7 @@ 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:
diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml
index 7593c8f..b94fc54 100644
--- a/templates/validate-plan-destroy.yml
+++ b/templates/validate-plan-destroy.yml
@@ -53,6 +53,9 @@ spec:
     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
@@ -90,6 +93,7 @@ include:
       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:
@@ -101,6 +105,8 @@ include:
       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:
-- 
GitLab