Skip to content

fix(aws): honour AWS_ENDPOINT_URL for aws4fetch-based resources - #1405

Merged
sam-goodwin merged 2 commits into
mainfrom
claude/fix-issue-1404-NOHqE
Jul 8, 2026
Merged

fix(aws): honour AWS_ENDPOINT_URL for aws4fetch-based resources#1405
sam-goodwin merged 2 commits into
mainfrom
claude/fix-issue-1404-NOHqE

Conversation

@sam-goodwin

Copy link
Copy Markdown
Collaborator

Summary

Fixes #1404 — aws4fetch-based AWS resources didn't pick up AWS_ENDPOINT_URL / AWS_ENDPOINT_URL_<SERVICE> after the SDK→aws4fetch migration, which blocked LocalStack / moto / motoserver workflows.

Adds an awsEndpoint(service, region) helper in alchemy/src/aws/utils.ts:

  1. AWS_ENDPOINT_URL_<SERVICE> (e.g. AWS_ENDPOINT_URL_EC2) wins first
  2. AWS_ENDPOINT_URL is the global fallback
  3. Defaults to https://<service>.<region>.amazonaws.com

…and wires it into the three aws4fetch call sites that previously hardcoded the AWS host:

  • callEC2Api (EC2: Vpc, Subnet, SecurityGroup, SecurityGroupRule, RouteTable, Route, RouteTableAssociation, NatGateway, InternetGateway, InternetGatewayAttachment)
  • CloudControlClient.fetch (every aws/control/* resource)
  • getAwsApiCaller (generic XML-API caller)

Also flips forcePathStyle: true on S3Client whenever an S3 endpoint override is in effect, since most emulators reject virtual-hosted-style — same trick the AWS CLI uses.

Usage

export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test
export AWS_REGION=us-east-1
export AWS_ENDPOINT_URL=http://localhost:4566   # all services
# or, per-service:
export AWS_ENDPOINT_URL_EC2=http://localhost:5000
import alchemy from "alchemy";
import { Vpc } from "alchemy/aws/ec2";

const app = await alchemy("emulator-test");
await Vpc("v", { cidrBlock: "10.0.0.0/16" });
// → POST goes to http://localhost:4566/ (LocalStack), not real AWS
await app.finalize();

Test plan

  • bun vitest run ./alchemy/test/aws/endpoint.test.ts — 8 new unit tests covering precedence (service-specific > global > default), trailing-slash stripping, hyphen→underscore env-var normalisation, and hasAwsEndpointOverride detection
  • bun tsc --noEmit clean across alchemy/
  • bun format clean
  • Manual: point AWS_ENDPOINT_URL=http://localhost:4566 at a running LocalStack and exercise Vpc, an aws/control/* resource, and Bucket end-to-end

Generated by Claude Code

claude added 2 commits May 11, 2026 04:50
Adds an `awsEndpoint(service, region)` helper that resolves
`AWS_ENDPOINT_URL_<SERVICE>` then `AWS_ENDPOINT_URL`, falling back to the
default `https://<service>.<region>.amazonaws.com`. Wires it into the
aws4fetch call sites that previously hardcoded the AWS host:

- `callEC2Api` (EC2 resources: Vpc, Subnet, SecurityGroup, …)
- `getAwsApiCaller` (generic XML-API caller)
- `CloudControlClient.fetch` (every `aws/control/*` resource)

This unblocks LocalStack / moto / motoserver workflows and matches the
AWS SDK v3 / AWS CLI env-var convention so users don't have to learn a
new variable. Also enables `forcePathStyle` on `S3Client` whenever an S3
endpoint override is in effect, since most emulators reject
virtual-hosted-style addressing.

Closes #1404
Drop the S3 forcePathStyle/S3StateStore changes and the helper's test
suite — the S3 quirk is out of scope for this fix. All that remains is
the one-liner `awsEndpoint()` helper and its three aws4fetch consumers
(EC2 callEC2Api, CloudControlClient, getAwsApiCaller).
@sam-goodwin
sam-goodwin merged commit fb411f0 into main Jul 8, 2026
3 of 5 checks passed
@sam-goodwin
sam-goodwin deleted the claude/fix-issue-1404-NOHqE branch July 8, 2026 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

aws4fetch-based AWS resources don't honour AWS_ENDPOINT_URL — blocks LocalStack / emulator usage

2 participants