Skip to content

fix(aws-vpclattice): close the real-cloud parity gaps in #1451 - #1551

Merged
thzgajendra merged 2 commits into
stackshy:developmentfrom
thzgajendra:fix/vpclattice-parity-1451
Oct 13, 2026
Merged

thzgajendra merged 2 commits into
stackshy:developmentfrom
thzgajendra:fix/vpclattice-parity-1451

Conversation

@thzgajendra

@thzgajendra thzgajendra commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Close all 19 real-cloud parity gaps listed in AWS VPCLattice: open real-cloud parity gaps #1451 for the VPC Lattice emulator.
  • The rules live in the provider, so the Go library and cloudemu serve behave the same.
  • One PR for all 19 items at the issue owner's request. It is one commit because the same files carry several of the changes; the table below maps each item to its behaviour.

The problem (in plain words)

The emulator said yes to requests that real AWS refuses. It accepted a target group of type BOGUS, a second service with the same name, a rule priority already in use, and a VPC that does not exist. It let you delete a target group that a rule still forwards to. It also left out things real AWS reports: the default rule, the services using a target group, in-progress statuses and CloudTrail events. A test that passed against cloudemu could then fail against real AWS.

How this fixes it

  1. Validate input in the provider: enums, listener port, auth policy JSON, rule priority range.
  2. Refuse duplicates (service, service network, rule priority, service-network association) with a ConflictException.
  3. Check references: delete is refused while something still uses a target group or resource gateway, and creates are refused for a missing VPC or log source.
  4. Fill in what AWS reports: serviceArns, the default rule, nextToken pagination, clean batch-failure messages.
  5. Model creation lifecycle and CloudTrail: statuses and target health settle on the mock clock when async settle is on, and Lattice calls are recorded as management events.

Changes

How it works:

flowchart LR
    C[AWS CLI / SDK] --> W[server/aws/vpclattice<br/>decode, paginate, map errors]
    W --> P[providers/aws/vpclattice<br/>validate, uniqueness, references, status]
    P --> S[(memstore)]
    P -. VPC lookup .-> V[providers/aws/vpc]
    W -. operation name .-> T[CloudTrail recorder]
Loading
Item in #1451 Behaviour now (exception from the vendored SDK v1.25.5 operation model)
CreateTargetGroup type=BOGUS type must be IP, LAMBDA, INSTANCE or ALB, else ValidationException
DeleteTargetGroup while a rule forwards to it ConflictException while any rule action or listener default action forwards to it (id or ARN form)
CreateListener FTP / port 999999 protocol HTTP, HTTPS or TLS_PASSTHROUGH; port 1-65535; an omitted port defaults to 80 (HTTP) or 443 (HTTPS) per the API reference, and TLS_PASSTHROUGH needs an explicit port
CreateServiceNetwork authType=BOGUS NONE or AWS_IAM on create and update of service networks and services (same root cause)
Duplicate service / service-network name ConflictException
Duplicate rule priority on a listener ConflictException on create, update and per item in BatchUpdateRule; priority 1-2000 (API reference)
Duplicate SN-service association ConflictException
SN VPC association with a nonexistent VPC ResourceNotFoundException when the VPC service is wired (always under the AWS provider); a standalone mock accepts any id
AccessLogSubscription with a nonexistent resource ResourceNotFoundException (service network, service or resource configuration, id or ARN)
DeleteResourceGateway while a configuration references it ConflictException
TargetGroup.serviceArns never populated derived at read time from listeners and rules; sorted and de-duplicated
ListRules omits the default rule every listener has a synthesized default rule (isDefault true, name default); UpdateRule, BatchUpdateRule and DeleteRule on it are ValidationExceptions, as the API reference says; the default action changes only through UpdateListener
RegisterTargets immediately HEALTHY INITIAL then HEALTHY on the mock clock with async settle on; unchanged (immediate) otherwise
PutAuthPolicy accepts malformed JSON must be a JSON object, else ValidationException
BatchUpdateRule leaks NotFound: failureMessage is the plain message; failureCode is mapped per error kind
List* ignore maxResults / nextToken maxResults 1-100 and nextToken on every list; invalid values are a ValidationException
Statuses immediately ACTIVE CREATE_IN_PROGRESS then ACTIVE with async settle on, for services, target groups, the three association kinds, resource gateways and resource configurations
No CloudTrail events calls are recorded as management events (source vpc-lattice.amazonaws.com, name derived from method and path)

Where unconfirmed against the live service (kept in docs/coverage/nongoals/vpclattice.md): the exact error message wording, whether the default rule reports a priority, the TLS_PASSTHROUGH port rule, and the exception for a missing VPC (chosen ResourceNotFoundException). Tests assert exception type and HTTP status, not message text.

Not changed (sibling gaps, left for #1451): target-group name uniqueness, target-group config protocol enum, duplicate VPC and resource associations, duplicate listener ports, PutResourcePolicy validation, the PutAuthPolicy newline rule, rule and listener actions that name a missing target group, access-log destination checks.

One small fix beyond the 19 items, found by the not-found test sweep: ListListeners for a service that does not exist returned an empty list; it now returns ResourceNotFoundException like the other list operations.

Behaviour change to be aware of: stricter validation can fail tests that relied on the old lenient behaviour. Two existing tests pinned it and were updated (ListRules now includes the default rule; the SDK association test uses a real VPC).

Checklist of the maintainers' recurring review points: create paths are atomic under the mock's existing mutex (race-tested with 50 parallel creates), reads return deep copies (race-probed), typed exceptions come only from each operation's modeled set, validation lives in the provider, time and settling go through the injected clock, rejected creates store nothing, no new persisted state, and TestSnapshotCompleteness stays green.

Provider Coverage

  • AWS

Azure, GCP and OCI are not applicable: VPC Lattice exists only on AWS.

Checklist

  • All tests pass (go test ./...)
  • Linter passes (golangci-lint run --timeout=9m ./...)
  • Every provider the change applies to implements the same behavior
  • Unit tests added to provider test files

Integration tests in cloudemu_test.go were not added: recent single-service PRs keep SDK round-trip tests beside the service, and this PR does the same (happy to add one if you want it).

  • Regenerated docs (go generate ./... for coverage; go run ./internal/compatgen for the compat matrix) and committed the result

Test Plan

  • Provider tests (providers/aws/vpclattice/vpclattice_test.go) cover every item, with edge cases (own-priority update, batch update with a repeated priority, id versus ARN references, a standalone mock without the VPC service), -race tests with 50 concurrent creates, and FakeClock settle tests.
  • Real aws-sdk-go-v2 round trips (server/aws/vpclattice/sdk_roundtrip_test.go) assert the typed exception (errors.As) and HTTP status for each rejection, pagination over every list operation with page size 1, and OperationName for the REST routes. server/aws/cloudtrail_recording_test.go checks LookupEvents after Lattice calls.
  • Real-environment run against the built server (branch binary built with -race, baseline from development):
    • AWS CLI v2 script, 29 of 29 checks pass on the branch; 18 of the same checks fail on development.
    • Docker image built from the repo Dockerfile, driven by boto3 in a fresh virtualenv with default flags: all typed errors and statuses as above.
    • go install with cloudemu start, status, stop.
    • OpenTofu 1.13.1 (Terraform-compatible) with the AWS provider: init, apply of a service, target group, listener and rule, plan showing no changes, and a clean destroy.
    • --async-settle (CREATE_IN_PROGRESS to ACTIVE, INITIAL to HEALTHY), --persist restart, snapshot and rewind, --enforce-auth together with S3, and 50-way parallel creates with no race reports in the server log.
  • go test ./..., go test -race on the touched packages, go build -mod=readonly ./..., golangci-lint on the touched packages (0 issues), go generate ./... and go run ./internal/compatgen (no drift beyond the regenerated Lattice coverage page).
  • Coverage before to after: providers/aws/vpclattice 81.7% to 89.9%; server/aws/vpclattice 70.9% to 82.1%.

Related Issues

Closes #1451. It covers all 19 items, so every box in the issue can be ticked.

Validate enums and ports, refuse duplicates and in-use deletes, check
references, return serviceArns, the default rule and paginated lists, model
create-time statuses and target health, and record Lattice calls as CloudTrail
management events. Closes stackshy#1451.

@NitinKumar004 NitinKumar004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. It covers the whole of #1451 in one go, and the rules sit in the provider where they belong. I built the head commit (16e3be7) myself and tested it three ways: the AWS CLI against cloudemu serve, the real Terraform AWS provider, and the same CLI script against a development build to confirm each item fails there first. All 19 items behave as described. I found one regression and one leftover leniency, both in the rule code, plus a few small gaps.

Issue coverage

Item in #1451 Status Evidence (CLI against serve; development behaviour in brackets)
CreateTargetGroup type=BOGUS Fixed ValidationException 400 [accepted]
DeleteTargetGroup while a rule forwards to it Fixed ConflictException 409, for both rule and listener default action [deleted]
CreateListener FTP / port 999999 Fixed ValidationException for each [accepted]
CreateServiceNetwork authType=BOGUS Fixed ValidationException [accepted]
Duplicate service name Fixed ConflictException [accepted]
Duplicate service-network name Fixed ConflictException [accepted]
Duplicate rule priority on a listener Fixed ConflictException [accepted, two rules at priority 1]
Duplicate SN-service association Fixed ConflictException [accepted]
SN VPC association with a nonexistent VPC Fixed ResourceNotFoundException [accepted]
AccessLogSubscription on a nonexistent resource Fixed ResourceNotFoundException [accepted]
DeleteResourceGateway while referenced Fixed covered by provider and SDK tests
TargetGroup.serviceArns Fixed returns the forwarding service ARN [null]
ListRules default rule Fixed, but see Medium 2 default rule with isDefault: true is listed first; DeleteRule on it gives ValidationException
RegisterTargets immediately HEALTHY Fixed INITIAL then HEALTHY with --async-settle (FakeClock tests); unchanged when settle is off
PutAuthPolicy malformed JSON Fixed ValidationException [accepted]
BatchUpdateRule NotFound: prefix Fixed failureMessage is rule "..." not found [NotFound: rule ...]
List* maxResults / nextToken Fixed maxResults=1 returns a page plus a token; all list ops go through one helper
Statuses immediately ACTIVE Fixed with --async-settle, Terraform waiters for service, target groups and associations waited about 5s, then completed
No CloudTrail events Fixed LookupEvents shows the Lattice calls (CreateRule, UpdateRule, BatchUpdateRule, ListTargets and so on) with source vpc-lattice.amazonaws.com

I found no items claimed but not delivered and nothing skipped silently. The PR body and the non-goals page list the sibling gaps it leaves out.

Findings

Medium

1. Rule priority is capped at 100, but AWS allows 1 to 2000. Priorities that work today now fail.
providers/aws/vpclattice/validate.go:28 (maxRulePriority = 100)
The CreateRule and UpdateRule references both give priority a valid range of 1 to 2000. On this branch aws vpc-lattice create-rule --priority 500 returns ValidationException: rule priority 500 is out of range 1-100. On development the same call succeeds, so any CLI or SDK user with a priority from 101 to 2000 hits a new failure that real AWS would not give. The non-goals page (line 4) calls 1 to 100 "the documented one", which is also wrong. The Terraform provider checks 1 to 100 on its own side, so Terraform users never get this far; the CLI and SDKs don't check it.
Fix: set the max to 2000, update the non-goals sentence, and add a test case at 2000 and 2001.

2. UpdateRule and BatchUpdateRule can change the default rule. AWS refuses this.
providers/aws/vpclattice/rules.go:55, called from lines 204 and 296.
The UpdateRule reference says: "You can't modify a default listener rule. To modify a default listener rule, use UpdateListener." On this branch, update-rule --rule-identifier <default rule id> --action '{"fixedResponse":{"statusCode":503}}' succeeds and rewrites the listener's default action. A test that changes the default action through UpdateRule would pass here and fail on AWS. That is the kind of gap #1451 is meant to close, and this PR adds it.
Fix: return a ValidationException for the default rule in UpdateRule, and the same failure entry in BatchUpdateRule, as DeleteRule already does. updateDefaultRule can then go. Update the non-goals bullet to match.

Low

3. A TLS_PASSTHROUGH listener created without a port stores port 0.
providers/aws/vpclattice/validate.go:80
validateListener fills in 80 for HTTP and 443 for HTTPS when the port is omitted, but leaves 0 for TLS_PASSTHROUGH. create-listener --protocol TLS_PASSTHROUGH without --port succeeds, and the listener then comes back with no port. The CreateListener response documents port as 1 to 65535. Either require a port for TLS_PASSTHROUGH (ValidationException) or reject 0 after the defaults are applied.

4. Target health never reports UNUSED.
providers/aws/vpclattice/targets.go:58
The SDK's TargetStatus includes UNUSED ("The target group is not used in a listener rule"). AWS reports that status for targets in a group that no listener or rule forwards to. A target registered in an unattached group still goes INITIAL then HEALTHY here. targetGroupInUse from this PR could answer the question directly. This is fine as a follow-up, but it is worth a line in the non-goals page.

5. Coverage of server/aws/vpclattice is 70.4%.
That's what I measured locally with go test -race -coverprofile; the provider package is at 92.5%. This was about the same before the PR, but it is still well below 90%. The new event_name.go branches (tags, policies, domain verifications, unknown verbs) are the cheapest place to add cases.

AWS docs divergences

  • Rule priority range: AWS says 1 to 2000, the PR uses 1 to 100. CreateRule, UpdateRule
  • UpdateRule on the default rule: AWS forbids it, the PR allows it. UpdateRule
  • TLS_PASSTHROUGH listener port: AWS says 1 to 65535, the PR stores 0 when omitted. CreateListener
  • Target status UNUSED is not modelled. TargetSummary
  • Checked and consistent: ListTargets takes maxResults (1 to 100) and nextToken as query parameters; DeleteRule refuses the default rule; ConflictException is 409 and ResourceNotFoundException is 404 on the create and delete paths; ID and ARN shapes (sn-, svc-, tg-, listener-, rule-, snsa-, snva-, each with 17 characters) match the documented patterns, including the derived default-rule id.

Terraform and CLI

Terraform 1.14.3 with hashicorp/aws 6.68.0, pointed at serve through endpoints { vpclattice, ec2 }. The config had aws_vpc, aws_vpclattice_service_network, aws_vpclattice_service, two aws_vpclattice_target_group (one with a health check), aws_vpclattice_listener (forward default action), aws_vpclattice_listener_rule (path match, weighted forward 60/40 across both groups), aws_vpclattice_service_network_service_association and aws_vpclattice_service_network_vpc_association.

  • apply: 9 created, no waiter hang. plan -detailed-exitcode: 0.
  • I changed tags on six resources: apply completed with 6 changed, then plan returned 0.
  • destroy: 9 destroyed, in the right order (rule, listener, then target groups, so the new in-use check did not get in the way). Every follow-up get-* returned ResourceNotFoundException.
  • The same run with --async-settle: waiters polled CREATE_IN_PROGRESS for about 5s and then completed. Plan returned 0 and destroy was clean.
  • A side note, not caused by this PR: changing priority on aws_vpclattice_listener_rule leaves drift. Provider 6.68.0 sends UpdateRule with an empty {} body, so the priority is never sent. The CLI update-rule --priority path works correctly.

CLI: every error case in the table above, against this branch and against development. I also checked a --persist restart: after the restart, serviceArns, the default rule (same id), the in-use refusal on DeleteTargetGroup and the name-uniqueness conflict all still held.

What I verified

  • I reviewed the head commit itself, not the PR's self-reported results. go build ./... and go vet on the touched packages are clean.
  • go test -race on providers/aws/vpclattice and server/aws/vpclattice passes (it includes the 50-way concurrent create tests). The CloudTrail, authorization, wiring and snapshot tests in server/aws, providers/aws and persist also pass.
  • go generate . leaves no diff, and gofmt is clean on the touched files.
  • golangci-lint: CI Lint is green. Locally, linter 2.14 reports extra goconst hits, mostly route-name strings repeated between event_name.go and handler.go. They don't fail CI, but reusing the new constants in handler.go for the remaining roots would remove them.
  • All CI checks are green.
  • Architecture: validation and state rules are in the provider, and the handler only paginates and maps errors. The VPC lookup is wired once through SetNetworking in providers/aws/aws.go. Settling uses the injected clock and settle.Set. No new persisted state is added (settle windows are documented as in-memory). The driver interface is unchanged, and there is no mirror concern because the service exists only on AWS.

Verdict

Request changes for the two Medium items. Both are small, contained in validate.go and rules.go, and come with doc-backed fixes. Everything else is solid and I would approve after those two.

Comment thread providers/aws/vpclattice/validate.go Outdated
maxPort = 65535

minRulePriority = 1
maxRulePriority = 100

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CreateRule and UpdateRule API references both give priority a valid range of 1 to 2000 (https://docs.aws.amazon.com/vpc-lattice/latest/APIReference/API_CreateRule.html). With 100 here, aws vpc-lattice create-rule --priority 500 now fails with ValidationException, while it succeeds on development and on AWS. Can this be 2000, with a test at 2000 and 2001?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed against the CreateRule / UpdateRule reference (Valid Range 1 to 2000). My 1-100 was a guess that I wrongly called documented. The max is now 2000, with tests at 500, 2000 and 2001 (provider, SDK and CLI), fixed in 6449cc1.

Comment thread providers/aws/vpclattice/rules.go Outdated

// updateDefaultRule applies a rule update to the listener's default action; the
// default rule has no priority or match to change.
func (m *Mock) updateDefaultRule(l *driver.Listener, priority int32, match, action []byte) (*driver.Rule, error) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UpdateRule reference says "You can't modify a default listener rule. To modify a default listener rule, use UpdateListener." Here UpdateRule on the default rule succeeds and rewrites the listener's default action, so a test can pass locally and fail on AWS. I'd return a ValidationException for the default rule here and in BatchUpdateRule (line 296), the same way DeleteRule does, and drop this helper.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ("You can't modify a default listener rule. To modify a default listener rule, use UpdateListener."). UpdateRule and BatchUpdateRule on the default rule now return ValidationException (a per-item failure in the batch), and updateDefaultRule is gone. The default action changes only through UpdateListener, and tests check it is untouched after the refused calls. Fixed in 6449cc1.

Comment thread docs/coverage/nongoals/vpclattice.md Outdated
- Resource statuses (`CREATE_IN_PROGRESS` to `ACTIVE` for services, target groups, the three association kinds, resource gateways and resource configurations) and target health (`INITIAL` to `HEALTHY`) settle on the mock's clock only when async settling is on (`--async-settle` / `config.WithAsyncSettle`); otherwise they are final immediately. Settle windows are in memory and are not part of snapshots, so a restored resource reports its final state. Only the create-time transition is modeled: there is no `UPDATE_IN_PROGRESS` or `DELETE_IN_PROGRESS`, and no health checking against real targets.
- A target group's `serviceArns` is derived at read time from the listener default actions and rules that forward to it. Deleting a target group while any of them still forwards to it is rejected with `ConflictException`. A rule or listener action that names a target group that does not exist is still accepted.
- Every listener has a synthesized default rule (`isDefault` true, name `default`, no priority) built from the listener's default action. `ListRules` and `GetRule` return it, `UpdateRule` on it changes the listener's default action (its priority and match cannot be changed), and `DeleteRule` on it is rejected with `ValidationException`. Its id is derived from the listener id. Whether real VPC Lattice reports a priority for the default rule, and the exact exception for deleting it, are unconfirmed.
- Rule priority must be 1 to 100 and unique per listener (`ConflictException` on a duplicate, also per item in `BatchUpdateRule`); the range is the documented one but is unconfirmed against the live service. Service names and service network names are unique per account and region (`ConflictException`). Only the `(service network, service)` association pair is checked for duplicates; duplicate VPC and resource associations, duplicate target group names and duplicate listener ports are still accepted.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documented range is 1 to 2000, not 1 to 100 (CreateRule / UpdateRule API reference), so this sentence needs updating along with the constant.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to 1 to 2000 with the API reference as the source, and the default-rule bullet now matches the new behaviour. Fixed in 6449cc1.

Comment thread providers/aws/vpclattice/validate.go Outdated
}
}

return port, nil

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For TLS_PASSTHROUGH with no port this returns 0, so the listener is stored and returned without a valid port (the API documents 1 to 65535). Either require the port for TLS_PASSTHROUGH or reject 0 after the HTTP and HTTPS defaults are applied.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took the first option: TLS_PASSTHROUGH without a port is now a ValidationException. The docs give defaults for HTTP and HTTPS only. What real AWS does for TLS_PASSTHROUGH without a port is unconfirmed, and the non-goals page says so. Fixed in 6449cc1.

index[targetKey(t)] = len(cur)
cur = append(cur, t)

m.statusSettle.Begin(targetSettleKey(id, t), targetStatusInitial, m.opts.Clock.Now(),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small follow-up: AWS reports UNUSED for targets in a group that no listener or rule forwards to. targetGroupInUse from this PR could drive that. If it's out of scope here, a line in the non-goals page would help.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left out of this PR on purpose: reporting UNUSED would change the default (sync) health for every existing user, whose targets in an unattached group show HEALTHY today. Added to the non-goals page (UNUSED, UNAVAILABLE and UNHEALTHY are not modeled). Happy to do it as a follow-up using targetGroupInUse.

Allow rule priorities 1-2000 as the API reference documents, refuse UpdateRule
and BatchUpdateRule on a listener's default rule (UpdateListener changes it),
require a port for TLS_PASSTHROUGH listeners, return ResourceNotFoundException
from ListListeners for an unknown service, document the unmodeled UNUSED
target status, and share the route-root constants between the handler and the
CloudTrail operation names.
@thzgajendra

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review and for running the CLI and Terraform checks yourself. Round 1 is pushed in 6449cc1:

  • Rule priority is 1 to 2000, per the API reference.
  • UpdateRule and BatchUpdateRule refuse the default rule (ValidationException); UpdateListener is the way to change it.
  • TLS_PASSTHROUGH needs an explicit port.
  • Coverage: event_name.go was already fully covered, so I added a not-found sweep over every get, update, delete and list through the SDK. server/aws/vpclattice is now 82.1% (70.9% before) and the provider is 89.9%. The sweep also found that ListListeners on a missing service returned an empty list; it now returns ResourceNotFoundException (noted in the PR body).
  • The remaining route roots share constants between event_name.go and handler.go, which should clear the goconst hits.
  • UNUSED target status is documented as a non-goal rather than implemented, because it would change the default health for existing users.

Re-verified with the AWS CLI against a -race build (19 of 19 checks, no race reports) and an OpenTofu apply, plan and destroy. The PR description is updated to match.

@NitinKumar004 NitinKumar004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for turning this round so quickly. I rebuilt the new head (6449cc1) and went back over every point from my first review with the AWS CLI against cloudemu serve, then re-ran the Terraform lifecycle. Both Medium items are fixed, and so is the TLS_PASSTHROUGH port. The UNUSED status is now listed as a non-goal. I found no regressions in the delta.

Earlier items

Item from the first review Status Evidence (CLI against serve on 6449cc1)
Medium 1: rule priority capped at 100 Fixed create-rule --priority 101, 500 and 2000 succeed. 2001 gives ValidationException: rule priority 2001 is out of range 1-2000. update-rule to 2001 is refused and 1999 is accepted. The non-goals page now says 1 to 2000 and cites the API reference.
Medium 2: UpdateRule / BatchUpdateRule could change the default rule Fixed update-rule on the default rule gives ValidationException: a default listener rule cannot be modified; use UpdateListener. In a batch-update-rule that mixes the default rule with a normal rule, the default rule comes back in unsuccessful with failureCode: ValidationException and the normal rule still updates (priority 1500). The listener's default action stays at 404 afterwards. update-listener to 503 then shows up through get-rule on the default rule. updateDefaultRule has been removed.
Low 3: TLS_PASSTHROUGH without a port stored as 0 Fixed Without --port, the call fails with ValidationException: listener port is required for protocol TLS_PASSTHROUGH. With --port 8443 it succeeds. HTTPS without a port still defaults to 443. An invalid protocol with no port still gets the protocol error.
Low 4: target status never UNUSED Addressed as a non-goal The non-goals page now lists UNUSED, UNAVAILABLE and UNHEALTHY as not modelled. Keeping the default health unchanged for existing users is a fair reason.
Low 5: server/aws/vpclattice coverage 70.4% Partly It is 79.5% in my local go test -race -cover run (82.1% in your measurement, which probably used a wider -coverpkg). The provider is at 92.5%. That is a good improvement, but the package is still under 90%. The new not-found sweep is useful, so I'm fine with this as is.
Side note: goconst hits for route names Fixed The route roots are now shared constants. The 10 goconst hits that remain locally (linter 2.14) are all JSON keys (items, id, successful, failureCode and so on), and CI Lint is green.

Delta since 16e3be7

  • validate.go: the max is now 2000. validateListener returns early when a port is given, then defaults HTTP and HTTPS and rejects anything else that has no port. The protocol is still validated before this point, so the order of errors is unchanged.
  • rules.go: UpdateRule and batchUpdateOne return errDefaultRuleImmutable() for the default rule. The batch path still reports it as a per-item failure and does not fail the whole call.
  • listeners.go: ListListeners now returns ResourceNotFoundException for an unknown service. That matches the ListListeners reference, which lists ResourceNotFoundException (404). serviceARNFor is lock-free, so calling it under m.mu cannot deadlock. The handler is the only caller of ListListeners, so nothing internal depends on the old empty-list behaviour.
  • event_name.go / handler.go: a constants-only refactor. The route map and the CloudTrail noun map use the same keys as before.
  • Tests: provider tests cover 500, 1000, 2000 and 2001, UpdateRule to 2001, refused default-rule updates through both paths with the default action left unchanged, TLS_PASSTHROUGH without a port, and ListListeners on an unknown service. The SDK tests mirror these and add a not-found sweep over every get, update, delete and list.

Findings

No High or Medium issues.

Low (pre-existing, not from this PR)

ARN-form identifiers in the URL path don't resolve over the wire.
server/aws/vpclattice/handler.go:62 (splitPath(r.URL.Path), unchanged since August)
The API reference accepts either the ID or the ARN for serviceIdentifier (pattern svc-... or arn:...:service/svc-...). The CLI percent-encodes the ARN's / as %2F, but the handler splits the decoded r.URL.Path. As a result, get-service --service-identifier <service ARN> returns ResourceNotFoundException: service "arn:aws:vpc-lattice:us-east-1:000000000000:service" not found, and create-listener with the ARN returns method not allowed. This PR only touches it in one place: list-listeners with an ARN used to return an empty list silently and now returns the same not-found error, which at least makes the problem visible. It is outside #1451, so a follow-up is fine: split r.URL.EscapedPath() and unescape each segment.

AWS docs check on the changed ops

  • CreateRule / UpdateRule: priority 1 to 2000, and the default rule can't be modified. The emulator now matches both.
  • CreateListener: "For HTTP, the default is 80. For HTTPS, the default is 443." No default is documented for TLS_PASSTHROUGH, so requiring a port is a reasonable reading. The non-goals page says this is unconfirmed against the live service.
  • ListListeners: ResourceNotFoundException (404) is listed. That matches.

Terraform and CLI

Terraform with hashicorp/aws 6.68.0 against serve --async-settle. The config had aws_vpc, aws_vpclattice_service_network, aws_vpclattice_service, aws_vpclattice_target_group, two aws_vpclattice_listener (HTTP with a fixed-response default, and TLS_PASSTHROUGH on 8443 forwarding to the group), aws_vpclattice_listener_rule (priority 20, path match, forward), aws_vpclattice_service_network_service_association and aws_vpclattice_service_network_vpc_association.

  • apply: 9 added, and the settle waiters finished in about 18s total. plan -detailed-exitcode: 0.
  • update: I changed tags on all seven tagged resources and changed the HTTP listener's default action from 404 to 503. This goes through UpdateListener and is unaffected by the new default-rule guard. The apply reported 7 changed, the next plan returned 0, and list-rules then showed the default rule plus rr-rule at priority 20, with the listener default action at 503.
  • destroy: 9 destroyed. All seven Lattice resources then returned ResourceNotFoundException from their get-* call.

CLI: every check in the table above, plus list-rules on a missing listener (ResourceNotFoundException) and an invalid protocol with no port (protocol ValidationException).

What I verified

  • I built the head commit myself. go build ./... passes, and go vet and gofmt are clean on the touched packages.
  • go test -race passes on providers/aws/vpclattice (92.5%) and server/aws/vpclattice (79.5%).
  • go generate . leaves no diff.
  • golangci-lint on the touched packages reports only the 10 JSON-key goconst hits described above. CI Lint is green.
  • All CI checks on 6449cc1 are green, including Test (4 shards), Race, Lint, Tidy, Structure and Compat matrix.
  • No new persisted state, driver change or cross-provider surface was added, so the earlier --persist and architecture checks still hold.
  • The commit message is clean, with author Gajendra Malviya and no co-author or attribution trailer.

Verdict

Approve. Both Medium items and the TLS_PASSTHROUGH gap are fixed and covered by tests at the provider and SDK level, and Terraform runs the full lifecycle cleanly. The ARN-in-path issue is older than this PR and can be a separate ticket.

m.mu.Lock()
defer m.mu.Unlock()

if _, err := m.serviceARNFor(serviceID); err != nil {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches the ListListeners reference (ResourceNotFoundException, 404). A related issue that is older than this PR: the handler splits the decoded r.URL.Path, so a service ARN in the path (the CLI sends arn%3A...%3Aservice%2Fsvc-...) is cut at the /. list-listeners --service-identifier <ARN> now fails with service "arn:aws:vpc-lattice:us-east-1:000000000000:service" not found, and get-service and create-listener with an ARN fail the same way. Splitting r.URL.EscapedPath() and unescaping each segment would fix it. Fine as a follow-up.

@thzgajendra
thzgajendra merged commit 8c0bf2d into stackshy:development Oct 13, 2026
23 checks passed
@thzgajendra
thzgajendra deleted the fix/vpclattice-parity-1451 branch October 13, 2026 14:13
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.

AWS VPCLattice: open real-cloud parity gaps

2 participants