Skip to content

Commit

Permalink
Service endpoint alias fix (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
lakkeger authored Jun 25, 2024
1 parent 07643c4 commit 27a834d
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 36 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ please refer to the man pages of `terraform --help`.

## Change Log

* v0.18.2: Fix warning on aliased custom endpoint names
* v0.18.1: Fix issue with not proxied commands
* v0.18.0: Add `DRY_RUN` and patch S3 backend entrypoints
* v0.17.1: Add `packaging` module to install requirements
Expand Down
100 changes: 72 additions & 28 deletions bin/tflocal
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,73 @@ terraform {
}
"""
PROCESS = None
# some services have aliases which are mutually exclusive to each other
# see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/custom-service-endpoints#available-endpoint-customizations
SERVICE_ALIASES = [
("amp", "prometheus", "prometheusservice"),
("appautoscaling", "applicationautoscaling"),
("appintegrations", "appintegrationsservice"),
("ce", "costexplorer"),
("cloudcontrol", "cloudcontrolapi"),
("cloudhsmv2", "cloudhsm"),
("cognitoidp", "cognitoidentityprovider"),
("configservice", "config"),
("cur", "costandusagereportservice"),
("deploy", "codedeploy"),
("dms", "databasemigration", "databasemigrationservice"),
("ds", "directoryservice"),
("elasticbeanstalk", "beanstalk"),
("elasticsearch", "es", "elasticsearchservice"),
("elb", "elasticloadbalancing"),
("elbv2", "elasticloadbalancingv2"),
("events", "eventbridge", "cloudwatchevents"),
("evidently", "cloudwatchevidently"),
("grafana", "managedgrafana", "amg"),
("inspector2", "inspectorv2"),
("kafka", "msk"),
("lexmodels", "lexmodelbuilding", "lexmodelbuildingservice", "lex"),
("lexv2models", "lexmodelsv2"),
("location", "locationservice"),
("logs", "cloudwatchlog", "cloudwatchlogs"),
("oam", "cloudwatchobservabilityaccessmanager"),
("opensearch", "opensearchservice"),
("osis", "opensearchingestion"),
("rbin", "recyclebin"),
("redshiftdata", "redshiftdataapiservice"),
("resourcegroupstaggingapi", "resourcegroupstagging"),
("rum", "cloudwatchrum"),
("s3", "s3api"),
("serverlessrepo", "serverlessapprepo", "serverlessapplicationrepository"),
("servicecatalogappregistry", "appregistry"),
("sfn", "stepfunctions"),
("simpledb", "sdb"),
("transcribe", "transcribeservice"),
]
# service names to be excluded (not yet available in TF)
SERVICE_EXCLUSIONS = ["meteringmarketplace"]
# maps services to be replaced with alternative names
# skip services which do not have equivalent endpoint overrides
# see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/custom-service-endpoints
SERVICE_REPLACEMENTS = {
"apigatewaymanagementapi": "",
"appconfigdata": "",
"ce": "costexplorer",
"dynamodbstreams": "",
"edge": "",
"emrserverless": "",
"iotdata": "",
"ioteventsdata": "",
"iotjobsdata": "",
"iotwireless": "",
"logs": "cloudwatchlogs",
"mediastoredata": "",
"qldbsession": "",
"rdsdata": "",
"sagemakerruntime": "",
"support": "",
"timestream": "",
"timestreamquery": "",
}


# ---
Expand All @@ -79,40 +146,17 @@ PROCESS = None
def create_provider_config_file(provider_aliases=None):
provider_aliases = provider_aliases or []

# maps services to be replaced with alternative names
# skip services which do not have equivalent endpoint overrides
# see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/custom-service-endpoints
service_replaces = {
"apigatewaymanagementapi": "",
"appconfigdata": "",
"ce": "costexplorer",
"dynamodbstreams": "",
"edge": "",
"emrserverless": "",
"iotdata": "",
"ioteventsdata": "",
"iotjobsdata": "",
"iotwireless": "",
"logs": "cloudwatchlogs",
"mediastoredata": "",
"qldbsession": "",
"rdsdata": "",
"sagemakerruntime": "",
"support": "",
"timestream": "",
"timestreamquery": "",
}
# service names to be excluded (not yet available in TF)
service_excludes = ["meteringmarketplace"]
# Force service alias replacements
SERVICE_REPLACEMENTS.update({alias: alias_pairs[0] for alias_pairs in SERVICE_ALIASES for alias in alias_pairs if alias != alias_pairs[0]})

# create list of service names
services = list(config.get_service_ports())
services = [srvc for srvc in services if srvc not in service_excludes]
services = [srvc for srvc in services if srvc not in SERVICE_EXCLUSIONS]
services = [s.replace("-", "") for s in services]
for old, new in service_replaces.items():
for old, new in SERVICE_REPLACEMENTS.items():
try:
services.remove(old)
if new:
if new and new not in services:
services.append(new)
except ValueError:
pass
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = terraform-local
version = 0.18.1
version = 0.18.2
url = https://github.com/localstack/terraform-local
author = LocalStack Team
author_email = [email protected]
Expand Down
49 changes: 42 additions & 7 deletions tests/test_apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ def test_access_key_override_by_provider(monkeypatch):


def test_s3_path_addressing():
bucket_name = f"bucket.{short_uid()}"
# Temporarily change "." -> "-" as aws provider >5.55.0 fails with LocalStack
# by calling aws-global pseudo region at S3 bucket creation instead of us-east-1
bucket_name = f"bucket-{short_uid()}"
config = """
resource "aws_s3_bucket" "test-bucket" {
bucket = "%s"
Expand Down Expand Up @@ -164,7 +166,9 @@ def test_provider_aliases():
def test_s3_backend():
state_bucket = f"tf-state-{short_uid()}"
state_table = f"tf-state-{short_uid()}"
bucket_name = f"bucket.{short_uid()}"
# Temporarily change "." -> "-" as aws provider >5.55.0 fails with LocalStack
# by calling aws-global pseudo region at S3 bucket creation instead of us-east-1
bucket_name = f"bucket-{short_uid()}"
config = """
terraform {
backend "s3" {
Expand Down Expand Up @@ -203,7 +207,9 @@ def test_dry_run(monkeypatch):
monkeypatch.setenv("DRY_RUN", "1")
state_bucket = "tf-state-dry-run"
state_table = "tf-state-dry-run"
bucket_name = "bucket.dry-run"
# Temporarily change "." -> "-" as aws provider >5.55.0 fails with LocalStack
# by calling aws-global pseudo region at S3 bucket creation instead of us-east-1
bucket_name = "bucket-dry-run"
config = """
terraform {
backend "s3" {
Expand All @@ -224,7 +230,7 @@ def test_dry_run(monkeypatch):
override_file = os.path.join(temp_dir, "localstack_providers_override.tf")
assert check_override_file_exists(override_file)

assert check_override_file_content(override_file, is_legacy=is_legacy_tf)
assert check_override_file_backend_content(override_file, is_legacy=is_legacy_tf)

# assert that bucket with state file exists
s3 = client("s3", region_name="us-east-2")
Expand All @@ -243,6 +249,33 @@ def test_dry_run(monkeypatch):
s3.head_bucket(Bucket=bucket_name)


def test_service_endpoint_alias_replacements(monkeypatch):
monkeypatch.setenv("DRY_RUN", "1")
config = """
provider "aws" {
region = "eu-west-1"
}"""

temp_dir = deploy_tf_script(config, cleanup=False, user_input="yes")
override_file = os.path.join(temp_dir, "localstack_providers_override.tf")
assert check_override_file_content(override_file)
rmtree(temp_dir)


def check_override_file_content(override_file):
try:
with open(override_file, "r") as fp:
result = hcl2.load(fp)
result = result["provider"][0]["aws"]
except Exception as e:
raise Exception(f'Unable to parse "{override_file}" as HCL file: {e}')

endpoints = result["endpoints"][0]
if "config" in endpoints and "configservice" in endpoints:
return False
return True


@pytest.mark.parametrize("endpoints", [
'',
'endpoint = "http://s3-localhost.localstack.cloud:4566"',
Expand All @@ -255,7 +288,9 @@ def test_s3_backend_endpoints_merge(monkeypatch, endpoints: str):
monkeypatch.setenv("DRY_RUN", "1")
state_bucket = "tf-state-merge"
state_table = "tf-state-merge"
bucket_name = "bucket.merge"
# Temporarily change "." -> "-" as aws provider >5.55.0 fails with LocalStack
# by calling aws-global pseudo region at S3 bucket creation instead of us-east-1
bucket_name = "bucket-merge"
config = """
terraform {
backend "s3" {
Expand All @@ -279,15 +314,15 @@ def test_s3_backend_endpoints_merge(monkeypatch, endpoints: str):
temp_dir = deploy_tf_script(config, cleanup=False, user_input="yes")
override_file = os.path.join(temp_dir, "localstack_providers_override.tf")
assert check_override_file_exists(override_file)
assert check_override_file_content(override_file, is_legacy=is_legacy_tf)
assert check_override_file_backend_content(override_file, is_legacy=is_legacy_tf)
rmtree(temp_dir)


def check_override_file_exists(override_file):
return os.path.isfile(override_file)


def check_override_file_content(override_file, is_legacy: bool = False):
def check_override_file_backend_content(override_file, is_legacy: bool = False):
legacy_options = (
"endpoint",
"iam_endpoint",
Expand Down

0 comments on commit 27a834d

Please sign in to comment.