Skip to content

Commit 8ff6d98

Browse files
authored
Add new s3 backend endpoints option (#44)
1 parent d1f9d75 commit 8ff6d98

File tree

6 files changed

+74
-21
lines changed

6 files changed

+74
-21
lines changed

.github/workflows/build.yml

+17
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,30 @@ on:
1010
name: Build and Test
1111
jobs:
1212
build_test:
13+
strategy:
14+
matrix:
15+
version: ['1.5.7', 'latest']
16+
tool: ['terraform', 'tofu']
17+
exclude:
18+
- tool: tofu
19+
version: '1.5.7'
1320
timeout-minutes: 30
1421
runs-on: ubuntu-latest
1522
env:
1623
AWS_DEFAULT_REGION: us-east-1
1724
DNS_ADDRESS: 127.0.0.1
25+
TF_CMD: ${{matrix.tool}}
1826

1927
steps:
28+
- uses: hashicorp/setup-terraform@v3
29+
if: ${{ matrix.tool == 'terraform' }}
30+
with:
31+
terraform_version: ${{matrix.version}}
32+
- uses: opentofu/setup-opentofu@v1
33+
if: ${{ matrix.tool == 'tofu' }}
34+
with:
35+
tofu_version: ${{matrix.version}}
36+
tofu_wrapper: false
2037
- name: Check out code
2138
uses: actions/checkout@v3
2239
- name: Pull LocalStack Docker image

.gitignore

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Custom
22
.envrc
33
.env
4+
tmp/
45

56
.DS_Store
67
*.egg-info/
@@ -101,12 +102,7 @@ fabric.properties
101102
!.idea/runConfigurations
102103

103104
### VisualStudioCode ###
104-
.vscode/*
105-
!.vscode/settings.json
106-
!.vscode/tasks.json
107-
!.vscode/launch.json
108-
!.vscode/extensions.json
109-
!.vscode/*.code-snippets
105+
.vscode/
110106

111107
# Local History for Visual Studio Code
112108
.history/

Makefile

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ VENV_DIR ?= .venv
22
VENV_RUN = . $(VENV_DIR)/bin/activate
33
PIP_CMD ?= pip
44
TEST_PATH ?= tests
5+
TF_CMD ?= terraform
56

67
usage: ## Show this help
78
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
@@ -15,14 +16,15 @@ lint: ## Run code linter
1516
$(VENV_RUN); flake8 --ignore=E501,W503 bin/tflocal tests
1617

1718
test: ## Run unit/integration tests
18-
$(VENV_RUN); pytest $(PYTEST_ARGS) -sv $(TEST_PATH)
19+
$(VENV_RUN); TF_CMD=$(TF_CMD) pytest $(PYTEST_ARGS) -sv $(TEST_PATH)
1920

2021
publish: ## Publish the library to the central PyPi repository
2122
# build and upload archive
2223
($(VENV_RUN) && pip install setuptools twine && ./setup.py sdist && twine upload dist/*)
2324

2425
clean: ## Clean up
2526
rm -rf $(VENV_DIR)
26-
rm -rf dist/*
27+
rm -rf dist
28+
rm -rf *.egg-info
2729

2830
.PHONY: clean publish install usage lint test

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ please refer to the man pages of `terraform --help`.
4848

4949
## Change Log
5050

51+
* v0.17.0: Add option to use new endpoints S3 backend options
5152
* v0.16.1: Update Setuptools to exclude tests during packaging
5253
* v0.16.0: Introducing semantic versioning and AWS_ENDPOINT_URL variable
5354
* v0.15: Update endpoint overrides for Terraform AWS provider 5.22.0

bin/tflocal

+49-12
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ import os
1313
import sys
1414
import glob
1515
import subprocess
16+
import json
17+
import textwrap
1618

19+
from packaging import version
1720
from urllib.parse import urlparse
21+
from typing import Optional
1822

1923
PARENT_FOLDER = os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))
2024
if os.path.isdir(os.path.join(PARENT_FOLDER, ".venv")):
@@ -34,14 +38,15 @@ TF_CMD = os.environ.get("TF_CMD") or "terraform"
3438
LS_PROVIDERS_FILE = os.environ.get("LS_PROVIDERS_FILE") or "localstack_providers_override.tf"
3539
LOCALSTACK_HOSTNAME = urlparse(AWS_ENDPOINT_URL).hostname or os.environ.get("LOCALSTACK_HOSTNAME") or "localhost"
3640
EDGE_PORT = int(urlparse(AWS_ENDPOINT_URL).port or os.environ.get("EDGE_PORT") or 4566)
41+
TF_VERSION: Optional[version.Version] = None
3742
TF_PROVIDER_CONFIG = """
3843
provider "aws" {
3944
access_key = "<access_key>"
4045
secret_key = "test"
4146
skip_credentials_validation = true
4247
skip_metadata_api_check = true
4348
<configs>
44-
endpoints {
49+
endpoints {
4550
<endpoints>
4651
}
4752
}
@@ -56,10 +61,7 @@ terraform {
5661
5762
access_key = "test"
5863
secret_key = "test"
59-
endpoint = "<s3_endpoint>"
60-
iam_endpoint = "<iam_endpoint>"
61-
sts_endpoint = "<sts_endpoint>"
62-
dynamodb_endpoint = "<dynamodb_endpoint>"
64+
<endpoints>
6365
skip_credentials_validation = true
6466
skip_metadata_api_check = true
6567
}
@@ -126,7 +128,7 @@ def create_provider_config_file(provider_aliases=None):
126128
"<access_key>",
127129
get_access_key(provider) if CUSTOMIZE_ACCESS_KEY else DEFAULT_ACCESS_KEY
128130
)
129-
endpoints = "\n".join([f'{s} = "{get_service_endpoint(s)}"' for s in services])
131+
endpoints = "\n".join([f' {s} = "{get_service_endpoint(s)}"' for s in services])
130132
provider_config = provider_config.replace("<endpoints>", endpoints)
131133
additional_configs = []
132134
if use_s3_path_style():
@@ -139,7 +141,7 @@ def create_provider_config_file(provider_aliases=None):
139141
region = provider.get("region") or get_region()
140142
if isinstance(region, list):
141143
region = region[0]
142-
additional_configs += [f' region = "{region}"']
144+
additional_configs += [f'region = "{region}"']
143145
provider_config = provider_config.replace("<configs>", "\n".join(additional_configs))
144146
provider_configs.append(provider_config)
145147

@@ -203,17 +205,38 @@ def generate_s3_backend_config() -> str:
203205
"key": "terraform.tfstate",
204206
"dynamodb_table": "tf-test-state",
205207
"region": get_region(),
206-
"s3_endpoint": get_service_endpoint("s3"),
207-
"iam_endpoint": get_service_endpoint("iam"),
208-
"sts_endpoint": get_service_endpoint("sts"),
209-
"dynamodb_endpoint": get_service_endpoint("dynamodb"),
208+
"endpoints": {
209+
"s3": get_service_endpoint("s3"),
210+
"iam": get_service_endpoint("iam"),
211+
"sso": get_service_endpoint("sso"),
212+
"sts": get_service_endpoint("sts"),
213+
"dynamodb": get_service_endpoint("dynamodb"),
214+
},
210215
}
211216
configs.update(backend_config)
212217
get_or_create_bucket(configs["bucket"])
213218
get_or_create_ddb_table(configs["dynamodb_table"], region=configs["region"])
214219
result = TF_S3_BACKEND_CONFIG
215220
for key, value in configs.items():
216-
value = str(value).lower() if isinstance(value, bool) else str(value)
221+
if isinstance(value, bool):
222+
value = str(value).lower()
223+
elif isinstance(value, dict):
224+
is_tf_legacy = not (TF_VERSION.major > 1 or (TF_VERSION.major == 1 and TF_VERSION.minor > 5))
225+
if key == "endpoints" and is_tf_legacy:
226+
value = textwrap.indent(
227+
text=textwrap.dedent(f"""\
228+
endpoint = "{value["s3"]}"
229+
iam_endpoint = "{value["iam"]}"
230+
sts_endpoint = "{value["sts"]}"
231+
dynamodb_endpoint = "{value["dynamodb"]}"
232+
"""),
233+
prefix=" " * 4)
234+
else:
235+
value = textwrap.indent(
236+
text=f"{key} = {{\n" + "\n".join([f' {k} = "{v}"' for k, v in value.items()]) + "\n}",
237+
prefix=" " * 4)
238+
else:
239+
value = str(value)
217240
result = result.replace(f"<{key}>", value)
218241
return result
219242

@@ -347,6 +370,12 @@ def parse_tf_files() -> dict:
347370
return result
348371

349372

373+
def get_tf_version(env):
374+
global TF_VERSION
375+
output = subprocess.run([f"{TF_CMD}", "version", "-json"], env=env, check=True, capture_output=True).stdout.decode("utf-8")
376+
TF_VERSION = version.parse(json.loads(output)["terraform_version"])
377+
378+
350379
def run_tf_exec(cmd, env):
351380
"""Run terraform using os.exec - can be useful as it does not require any I/O
352381
handling for stdin/out/err. Does *not* allow us to perform any cleanup logic."""
@@ -395,6 +424,14 @@ def main():
395424
env = dict(os.environ)
396425
cmd = [TF_CMD] + sys.argv[1:]
397426

427+
try:
428+
get_tf_version(env)
429+
if not TF_VERSION:
430+
raise ValueError
431+
except (FileNotFoundError, ValueError) as e:
432+
print(f"Unable to determine version. See error message for details: {e}")
433+
exit(1)
434+
398435
# create TF provider config file
399436
providers = determine_provider_aliases()
400437
config_file = create_provider_config_file(providers)

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = terraform-local
3-
version = 0.16.1
3+
version = 0.17.0
44
url = https://github.com/localstack/terraform-local
55
author = LocalStack Team
66
author_email = [email protected]

0 commit comments

Comments
 (0)