Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions tests/util/test_util_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,51 @@ def test_populate_environment_with_terraform_remote_vars(
assert local_env["TF_REMOTE_LOCAL_KEY"] == "value"
assert local_env["TF_REMOTE_LOCAL_ANOTHER_KEY"] == "another_value"

@mock.patch("builtins.open", new_callable=mock.mock_open)
@mock.patch("tfworker.util.hooks.os.path.isfile", return_value=True)
@mock.patch(
"tfworker.util.hooks.get_state_item",
side_effect=[
'{"value":"staging","type":"string"}',
'{"value":true,"type":"bool"}',
'{"value":{"key":"val"},"type":"object"}',
'{"value":["a","b","c"],"type":"list"}',
],
)
def test_populate_environment_with_terraform_remote_vars_realistic_json(
self, mock_get_state_item, mock_isfile, mock_open
):
"""Test that remote vars correctly extract values from Terraform's JSON output format."""
mock_terraform_locals = """
local_key = data.terraform_remote_state.remote1.outputs.environment
local_flag = data.terraform_remote_state.remote2.outputs.enabled
local_config = data.terraform_remote_state.remote3.outputs.config
local_items = data.terraform_remote_state.remote4.outputs.items
"""
mock_open.return_value.read.return_value = mock_terraform_locals

local_env = {}
hooks._populate_environment_with_terraform_remote_vars(
local_env, "working_dir", "terraform_path", False, None
)

# Simple string should be unquoted
assert "TF_REMOTE_LOCAL_KEY" in local_env.keys()
assert local_env["TF_REMOTE_LOCAL_KEY"] == "staging"

# Boolean should be uppercase
assert "TF_REMOTE_LOCAL_FLAG" in local_env.keys()
assert local_env["TF_REMOTE_LOCAL_FLAG"] == "TRUE"

# Complex object should be JSON-encoded and shlex-escaped
assert "TF_REMOTE_LOCAL_CONFIG" in local_env.keys()
# shlex.quote wraps JSON in single quotes
assert local_env["TF_REMOTE_LOCAL_CONFIG"] == """'{"key":"val"}'"""

# List should be JSON-encoded and shlex-escaped
assert "TF_REMOTE_LOCAL_ITEMS" in local_env.keys()
assert local_env["TF_REMOTE_LOCAL_ITEMS"] == """'["a","b","c"]'"""

def test_populate_environment_with_extra_vars(self):
local_env = {}
extra_vars = {"extra_key": "extra_value"}
Expand Down
20 changes: 18 additions & 2 deletions tfworker/util/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,24 @@ def _populate_environment_with_terraform_remote_vars(
item = m.group("item")
state = m.group("state")
state_item = m.group("state_item")
state_value = get_state_item(
state_value_json = get_state_item(
working_dir, local_env, terraform_path, state, state_item, backend
)

# Parse the Terraform output JSON to extract just the value field
# Terraform output format is: {"value": <actual_value>, "type": <type>, "sensitive": <bool>}
try:
state_output = json.loads(state_value_json)
# Extract just the value field from the Terraform output structure
if isinstance(state_output, dict) and "value" in state_output:
state_value = state_output["value"]
else:
# Fallback to the raw value if it's not in the expected format
state_value = state_value_json
except (json.JSONDecodeError, TypeError):
# If parsing fails, use the raw value
state_value = state_value_json

_set_hook_env_var(
local_env, TFHookVarType.REMOTE, item, state_value, b64_encode
)
Expand Down Expand Up @@ -406,7 +421,8 @@ def _set_hook_env_var(
value_str = str(value).upper()
elif isinstance(value, (dict, list, tuple)):
try:
value_str = json.dumps(value)
# Use compact JSON formatting to match the rest of the codebase
value_str = json.dumps(value, separators=(",", ":"))
except Exception:
value_str = str(value)
else:
Expand Down