Repository navigation
fix(aws): skip resources deleted mid-walk in every RGT discovery walker - #1681
Merged
Satyam-Trivedi-ZS merged 3 commits intoOct 23, 2026
Merged
Satyam-Trivedi-ZS merged 3 commits into
Satyam-Trivedi-ZS merged 3 commits into
Conversation
4 of 10 tasks
NitinKumar004
approved these changes
Oct 23, 2026
NitinKumar004
left a comment
Collaborator
There was a problem hiding this comment.
What I checked
- Scope: only the three commits after
0be1e711(2a4e543, 6f7903e, 9a6db07). The Keyspaces commits belong to #1645. - Walker coverage. I went through all 20 collectors in
taggedDiscovery, plussagemakerDiscovery,rdsDiscovery(RDS and Redshift),eksDiscoveryand theservices/resourcediscoverywalkers. Every one that reads tags with a second call after its list call now skips a NotFound: Kafka, Kinesis, OpenSearch, Glue and GuardDuty throughproject, SageMaker and Redshift in their own loops. ECS, SFN, SSM, KMS, VPC Lattice, MemoryDB, Keyspaces, Resolver, EventBridge and CloudTrail already went throughproject. Network Firewall, SESv2, ACM, WAFv2 and Config take tags from the list result. S3, DynamoDB and EKS already skipped NotFound. On Azure,applyStorageAttrsandapplyDatabaseAttrsmake a second read, butBucketAttributesandTableAttributesnever return an error, so nothing is missing there. - Pagination.
listAllstops on an empty token. The Kinesis adapter returns""whenHasMoreStreamsis false, so the old!HasMore || NextToken == ""exit condition still holds. - Only NotFound is skipped. Mutations:
- Remove the skip in
project: the Kafka, Kinesis, OpenSearch, GuardDuty and Glue churn cases fail, and so do the Keyspaces andprojecttests. - Make
projectskip every error:TestProjectSkipsVanishedResourcefails. - Make SageMaker skip every error: the
other_errorcase fails. - Remove the Redshift skip: the churn case and the
not_foundcase fail. - Remove the GuardDuty re-list: the churn and deterministic GuardDuty cases fail.
- Remove Glue's
GetJob: the deterministic Glue case fails.
- Remove the skip in
- GuardDuty.
GetDetectorreturns BadRequest for a missing detector, which matches real GuardDuty. If the re-list itself fails, the original error is returned. NotFound is reported only when the detector is no longer listed. That logic is right. - Gates on 9a6db07:
go build ./...ok.go vet ./providers/aws/ok. gofmt is clean on the four touched files.go mod tidyandgo generate ./...give no diff.go test -race -count=3on the new tests: ok.go testok onproviders/aws,server/aws/resourcegroupstaggingapi,server/aws/resourceexplorer2,services/resourcediscoveryand the RGT tests incompat/aws.- golangci-lint 2.14.0 on
providers/aws, run without--new-from-rev, reports 20 issues on the head and the same 20 on the merge base (0be1e71). Nothing is new. The only finding in a touched file is the existing gocyclo 11 onDiscoverDatabases, which this PR doesn't change.
cloudemu serve, using the AWS CLI and SDK v2:- I created tagged (
env=prod) Kinesis, MSK, Glue, OpenSearch, GuardDuty, SageMaker model and Redshift resources.get-resources --tag-filters env=prodlists all 7. - For 20 seconds, 12 SDK workers created and deleted Kinesis, MSK, Glue, OpenSearch, SageMaker and Redshift resources, while 4 workers called GetResources and checked that all 7 long-lived resources were in each result.
- Head: 67,688 calls with 0 failures (123,600 churn rounds). A second run gave 63,715 calls with 0 failures. A parallel loop of 25
aws resourcegroupstaggingapi get-resourcescalls got 25/25 results with all 7 resources. - Base (0be1e71), same run: 1,768 of 57,010 calls failed, with
walkGeneric: NotFounderrors for Kinesis and SageMaker andwalkRelationalDB: NotFoundfor Redshift.
- I created tagged (
- Terraform hashicorp/aws 6.66.0, private provider download (
aws_kinesis_stream,aws_glue_jobandaws_guardduty_detector, all tagged): apply, then plan exit 0. Then I changed the tags toenv=staging, applied (3 changed in place) and plan exit 0. GetResources withenv=staginglists all three. Destroy works, and GetResources then shows none of them. - Commits and PR body have no attribution lines.
High
None.
Medium
None.
Low
- Nothing tests that a GuardDuty error propagates. If
guardDutyRowturns everyGetDetectorerror into NotFound, so a failing live detector is silently dropped, the whole suite still passes. Add a case that wraps GuardDuty soGetDetectorfails with a non-NotFound error while the detector is still listed, and assert thatdiscoverGuardDutyreturns that error.TestSageMakerDiscoverySkipsVanishedResourcealready does this for SageMaker. - A walker with a wrong ARN is only partly guarded. With the NotFound skip, a walker whose tag read misses its own resource now drops it silently instead of failing the call. The new tests catch this for the seven walkers in this PR: if I change the OpenSearch ARN to
domain/x<name>, both new tests fail.compat/aws/rgt_full_coverage_compat_test.gocovers KMS, SSM, ECS, SFN, Glue, Keyspaces, GuardDuty, ACM, SageMaker and Redshift. Nothing covers the other tag-read walkers: VPC Lattice, MemoryDB, Route 53 Resolver, EventBridge and CloudTrail. If I change VPC Lattice's tag read tos.ARN+"x", every test inproviders/awsstill passes. All five pass the ARN their list call returns, and I checked that each one shows up with its tags today, so nothing is broken now. A single table test that creates one tagged resource per collector and checks that each one comes back fromtaggedDiscovery.DiscoverResourceswould close the gap for all 20 collectors and for any new one. - Pre-existing Glue bug, worth a ticket (the PR body already mentions part of it). Glue's tag store accepts any ARN, and
DeleteJobdoesn't clear it. In Terraform withskip_requesting_account_id = true, the provider reads and writes tags under an ARN with no account ID. Those tags survive a destroy, and the next apply shows a tag diff against the old value. Discovery itself is fine, because the walker uses the account-qualified ARN. - Pre-existing, outside this PR's scope: a walker error reaches the wire as HTTP 400
InvalidParameterException: walkGeneric: ..., as seen on the base run. For an internal failure, real Resource Groups Tagging API returnsInternalServiceException(500).
Further notes
providers/aws/rgt_discovery.go:47: Withprojectskipping NotFound, a collector whose tag read misses its own resource (a wrong ARN, for example) now drops rows silently. The new tests guard the seven walkers this PR touches, but nothing guards VPC Lattice, MemoryDB, Resolver, EventBridge or CloudTrail. If I change the VPC Lattice tag read tos.ARN+"x", every test in providers/aws still passes. A table test that creates one tagged resource per collector and checks it comes back from DiscoverResources would cover all of them, and any new one.
| func (d taggedDiscovery) guardDutyRow(ctx context.Context, id string) (resourcediscovery.DiscoveredResource, error) { | ||
| det, err := d.p.GuardDuty.GetDetector(ctx, id) | ||
| if err != nil { | ||
| if ids, lerr := d.listGuardDutyDetectors(ctx); lerr == nil && !slices.Contains(ids, id) { |
Collaborator
There was a problem hiding this comment.
The re-list logic looks right, but no test covers the branch where the detector is still listed. If every GetDetector error becomes NotFound here, the suite still passes. Could you add a case that wraps GuardDuty so GetDetector returns a non-NotFound error for a detector that still exists, and assert that discoverGuardDuty returns that error?
…ass project The Kafka, Kinesis, GuardDuty and Glue walkers and the OpenSearch walker read tags in a loop of their own instead of going through project, so a resource deleted between the list call and its tag read failed the whole GetResources call for every service. Paginated walkers now collect every page with listAll and then go through project, which skips a NotFound tag read and propagates any other error. - Kafka, Kinesis and OpenSearch tag reads return NotFound for a deleted resource, so project skips them as is. - GetDetector reports a missing detector as BadRequestException, as real GuardDuty does, so the GuardDuty row re-lists detectors when GetDetector fails and reports NotFound only when the detector is gone. - Glue GetTags never fails for a deleted job, so the walk never aborted, but it emitted a row for the deleted job. The Glue row now checks the job with GetJob first.
…iscovery sagemakerDiscovery and the Redshift part of rdsDiscovery list resources and then read each one's tags in a separate call. Both tag reads return NotFound for a resource that is gone, which failed the whole discovery walk (Resource Groups Tagging API GetResources and Resource Explorer) when a delete landed in between. Skip that resource, the same as project does. Any other error still propagates.
…s separately - TestDiscoverySurvivesConcurrentDelete runs each walker 2000 times next to a create/delete loop for Kafka, Kinesis, OpenSearch, GuardDuty, Glue, SageMaker and Redshift. Before the fix every case except Glue failed within the first 512 walks. - TestTaggedWalkersSkipResourceDeletedAfterList deletes a resource after listing and projects the stale list through each walker's row function. - TestSageMakerDiscoverySkipsVanishedResource and TestRedshiftDiscoverySkipsVanishedCluster check that NotFound is skipped and any other tag-read error propagates.
Satyam-Trivedi-ZS
force-pushed
the
fix/aws-rgt-discovery-1667
branch
from
October 23, 2026 20:57
9a6db07 to
dfc5964
Compare
Satyam-Trivedi-ZS
marked this pull request as ready for review
October 23, 2026 20:57
Satyam-Trivedi-ZS
merged commit Oct 23, 2026
7ef1708
into
stackshy:development
39 of 41 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #1645 (merged); rebased onto
development, so the diff is now only this PR's changes.Summary
GetResourcescall (or Resource Explorer search). The walker skips that resource. Any other error still propagates.Changes
I checked every walker in
providers/aws/rgt_discovery.go,providers/aws/sagemaker_discovery.go,providers/aws/rds_discovery.goandservices/resourcediscovery/walkers.go. For each tag read, I called it on an ARN that no longer exists to see the error it actually returns:ListTagsForResourcereturns NotFoundprojectListTagsForResourcereturns NotFoundprojectListTagsreturns NotFoundprojectGetDetectorreturns BadRequestException (cerrors.InvalidArgument), not NotFoundGetTagsreturns no error, because the tag store is keyed by ARNGetJobfirst, thenprojectListTagsreturns NotFoundDescribeTagsreturns NotFoundlistAllhelper and then go throughproject, the same path as the other walkers. Each has a named row function (kafkaRow,kinesisRow,glueJobRow,guardDutyRow,openSearchRow) so the tests can drive it directly.GetDetectorreports a missing detector as BadRequestException, which is what real GuardDuty returns ("The request is rejected because the input detectorId is not owned by the current account"), socerrors.IsNotFounddoesn't match it. I didn't change the provider's error, because that would change the wire contract. WhenGetDetectorfails, the row re-lists detectors. If the detector is gone, it reports NotFound soprojectskips it. Otherwise the original error propagates. The re-list only runs on the error path, and there is at most one detector per account and Region.GetTagscan't tell a deleted job apart, soglueJobRowcallsGetJobfirst, which returns NotFound for a deleted job.cerrors.IsNotFoundand still return any other error.project.services/resourcediscovery, the S3 and DynamoDB walkers already skip NotFound. EKS discovery already skips NotFound. The other walkers take tags from the list result.docs/coverageis unchanged.Provider Coverage
Checklist
go test ./..., run asgo test -p 2in chunks of 30 packages)golangci-lint run --new-from-rev=origin/fix/aws-keyspaces-parity-1512 ./providers/aws/: 0 issues)cloudemu_test.go(not needed: provider-internal discovery fix)Test Plan
New tests in
providers/aws/rgt_discovery_test.go:TestDiscoverySurvivesConcurrentDeleteruns each walker 2000 times next to a create/delete loop, for Kafka, Kinesis, OpenSearch, GuardDuty, Glue, SageMaker and Redshift. The long-lived tagged resource must show up on every walk. Before the fix, under-race, it failed on Kafka (walk 28), Kinesis (walk 45), OpenSearch (walk 9), GuardDuty (walk 512,InvalidArgument: ... not owned by the current account), SageMaker (walk 20) and Redshift (walk 7). Glue passed, as expected. After the fix, all seven pass, also with-race -count=5.TestTaggedWalkersSkipResourceDeletedAfterListis deterministic. It lists, deletes one resource, then projects the stale list through each walker's row function (Kafka, Kinesis, OpenSearch, Glue, GuardDuty). Only the live, tagged resource comes back. The GuardDuty case also asserts thatGetDetectoron the deleted detector returns a non-NotFound error, which is the reason that walker needs the re-list.TestSageMakerDiscoverySkipsVanishedResourceandTestRedshiftDiscoverySkipsVanishedClusterwrap the mock so one tag read fails. NotFound is skipped and another error propagates. Both NotFound cases fail without the fix.Commands:
go build ./...: ok.go test -race ./providers/aws/ ./services/resourcediscovery/... ./server/aws/...(all 76server/awspackages, includingresourcegroupstaggingapiandresourceexplorer2): ok.go test -p 2 -count=1over all 657 packages in chunks of 30: no failures.golangci-lint run --allow-parallel-runners --new-from-rev=origin/fix/aws-keyspaces-parity-1512 ./providers/aws/: 0 issues, so no new findings in touched packages.Against
cloudemu serve -providers aws -aws-port 4613(AWS CLI 1.44.87 / botocore 1.42.97, always with--endpoint-url http://127.0.0.1:4613):env=prod) Kinesis stream, MSK cluster, Glue job and OpenSearch domain, andget-resources --tag-filters Key=env,Values=prodlisted all four.GetResourcesin a loop. Each call also checked that the four long-lived resources were in the result.aws resourcegroupstaggingapi get-resourcescalls.GetResourcescalls, 0 failed. 14,784 create/delete rounds. CLI loop 30 ok, 0 failed.0be1e711), same run: 7 of 12,286 calls failed withInvalidParameterException: walkGeneric: NotFound: ..., for Kinesis streams, MSK clusters and OpenSearch domains. The 30-call CLI loop alone was too slow to hit the window. The GuardDuty case didn't trigger over HTTP in this run, so the in-process tests are the evidence for it.Known limits:
DeleteJobdoesn't clear the job's entry in the ARN-keyed tag store, so a re-created job with the same name inherits the old tags. I haven't changed that here, because it is a Glue provider bug, not a discovery one.Related Issues
Part of #1667