From 43148c5485b5b83cc858abb39f2033f579d2030c Mon Sep 17 00:00:00 2001
From: Timo Furrer <tfurrer@gitlab.com>
Date: Mon, 27 May 2024 14:13:52 +0200
Subject: [PATCH] Auto URL-encode state name

This change set implements auto url-encoding for the state name when
it's used as a URL.
This allows to seamlessly define a state name like `production/aws`.

The feature is behind a feature flag that can be disabled.
You can define the `TF_FF_AUTO_URLENCODE_STATE_NAME` variable as `false`
in your pipeline configuration to disable this behavior.

Changelog: added
---
 src/gitlab-tofu.sh       | 17 +++++++++++--
 tests/unit.gitlab-ci.yml | 55 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh
index aa62e13..d168c58 100644
--- a/src/gitlab-tofu.sh
+++ b/src/gitlab-tofu.sh
@@ -8,6 +8,14 @@ if [ "${DEBUG_OUTPUT}" = "true" ]; then
     set -x
 fi
 
+# Feature Flags
+# =============
+# Below are a bunch of variables that we use as "feature flags".
+
+if [ -z "$TF_FF_AUTO_URLENCODE_STATE_NAME" ]; then
+  TF_FF_AUTO_URLENCODE_STATE_NAME=true
+fi
+
 # Helpers
 
 # Evaluate if this script is being sourced or executed directly.
@@ -45,8 +53,13 @@ if [ -z "${TF_PASSWORD}" ]; then
 fi
 
 # If TF_ADDRESS is unset but TF_STATE_NAME is provided, then default to GitLab backend in current project
-if [ -n "${TF_STATE_NAME}" ]; then
-  TF_ADDRESS="${TF_ADDRESS:-${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}}"
+if [ -n "${TF_STATE_NAME}" ] && [ -z "${TF_ADDRESS}" ]; then
+  # auto url-encode TF_STATE_NAME when FF is enabled
+  if $TF_FF_AUTO_URLENCODE_STATE_NAME; then
+    TF_STATE_NAME="$(jq -rn --arg x "${TF_STATE_NAME}" '$x|@uri')"
+  fi
+
+  TF_ADDRESS="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}"
 fi
 
 # If TF_ROOT is set then use the -chdir option
diff --git a/tests/unit.gitlab-ci.yml b/tests/unit.gitlab-ci.yml
index 7490a01..c51ee83 100644
--- a/tests/unit.gitlab-ci.yml
+++ b/tests/unit.gitlab-ci.yml
@@ -160,7 +160,7 @@ gitlab-tofu-init-with-prepared-registry-token:
     - |
       cat <<'EOF' > test.sh
       set -x
-      # NOTE: as part of the tst fixture, we need to overwrite the CI_SERVER_HOST,
+      # NOTE: as part of the test fixture, we need to overwrite the CI_SERVER_HOST,
       # so that this test also properly works on GitLab self-managed.
       export CI_SERVER_HOST=gitlab.example.com
       export TF_TOKEN_gitlab_example_com=mysecrettoken
@@ -189,7 +189,7 @@ gitlab-tofu-init-without-prepared-registry-token:
     - |
       cat <<'EOF' > test.sh
       set -x
-      # NOTE: as part of the tst fixture, we need to overwrite the CI_SERVER_HOST,
+      # NOTE: as part of the test fixture, we need to overwrite the CI_SERVER_HOST,
       # so that this test also properly works on GitLab self-managed.
       export CI_SERVER_HOST=gitlab.example.com
       . $(which gitlab-tofu)
@@ -340,3 +340,54 @@ gitlab-tofu-no-wrapper:
     - cat /tmp/output.txt
     - test $FAILED = true
     - 'grep "Error: Backend initialization required, please run \"tofu init\"" /tmp/output.txt'
+
+gitlab-tofu-state-name-auto-urlencode:
+  extends:
+    - .gitlab-tofu-test
+  stage: test
+  variables:
+    OPENTOFU_VERSION: $LATEST_OPENTOFU_VERSION
+  script:
+    - apk add --update $PKG
+    - |
+      cat <<'EOF' > test.sh
+      set -x
+      export TF_STATE_NAME=production/europe
+      . $(which gitlab-tofu)
+      test "$TF_STATE_NAME" = "production%2Feurope"
+      EOF
+    - $SHELL test.sh
+  parallel:
+    matrix:
+      - SHELL: "bash"
+        PKG: "bash"
+      - SHELL: "zsh"
+        PKG: "zsh"
+      - SHELL: "ksh"
+        PKG: "loksh"
+
+gitlab-tofu-state-name-auto-urlencode-ff-disabled:
+  extends:
+    - .gitlab-tofu-test
+  stage: test
+  variables:
+    OPENTOFU_VERSION: $LATEST_OPENTOFU_VERSION
+  script:
+    - apk add --update $PKG
+    - |
+      cat <<'EOF' > test.sh
+      set -x
+      export TF_FF_AUTO_URLENCODE_STATE_NAME=false
+      export TF_STATE_NAME=production/europe
+      . $(which gitlab-tofu)
+      test "$TF_STATE_NAME" = "production/europe"
+      EOF
+    - $SHELL test.sh
+  parallel:
+    matrix:
+      - SHELL: "bash"
+        PKG: "bash"
+      - SHELL: "zsh"
+        PKG: "zsh"
+      - SHELL: "ksh"
+        PKG: "loksh"
-- 
GitLab