Skip to content

fix(rds): resolve tags by ARN resource type, not only DB instances - #1649

Merged
hectorvent merged 4 commits into
floci-io:mainfrom
dnlopes:fix/rds-list-tags-for-subnet-group
Jul 7, 2026
Merged

fix(rds): resolve tags by ARN resource type, not only DB instances#1649
hectorvent merged 4 commits into
floci-io:mainfrom
dnlopes:fix/rds-list-tags-for-subnet-group

Conversation

@dnlopes

@dnlopes dnlopes commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

RDS tag operations (ListTagsForResource, AddTagsToResource, RemoveTagsFromResource) resolved every ResourceName to a DB instance, so any non-instance ARN (DB subnet group, cluster, …) failed with 404 DBInstanceNotFound even when the resource existed.

This PR resolves the tagging ResourceName by its ARN resource-type segment (db, cluster, subgrp) and dispatches to the matching store. Tag storage is added to DbCluster and DbSubnetGroup, mirroring DbInstance.

  • ARNs are parsed strictly: malformed ARNs and non-RDS ARNs return 400 InvalidParameterValue; a bare (non-ARN) name still resolves to a DB instance for backwards compatibility.
  • Missing resources return the resource-specific fault (e.g. DBSubnetGroupNotFoundFault); unsupported RDS resource types return InvalidParameterValue.
  • modifyDbSubnetGroup preserves existing tags when subnets are updated.

Type of change

  • Bug fix (fix:)
  • New feature (feat:)
  • Breaking change (feat!: or fix!:)
  • Docs / chore

AWS Compatibility

This is a bug fix to management-API behavior.

Incorrect behavior: the AWS Crossplane / Upbound provider calls ListTagsForResource on every observe of a DB subnet group. Floci returned:

ListTagsForResource → 404 DBInstanceNotFound:
DB instance arn:aws:rds:us-east-1:000000000000:subgrp:<name> not found.

so the managed resource never reconciled. Reproducible with the AWS CLI (AWS CLI v2):

aws rds create-db-subnet-group --db-subnet-group-name pg --db-subnet-group-description t --subnet-ids subnet-a subnet-b
aws rds list-tags-for-resource --resource-name arn:aws:rds:us-east-1:000000000000:subgrp:pg
# before: An error occurred (DBInstanceNotFound) ...
# after:  returns the tag list

Behavior now matches AWS: tags resolve per resource type, and modifying a subnet group preserves its tags.

Checklist

  • ./mvnw test passes locally
  • New or updated integration test added
  • Commit messages follow Conventional Commits

Copilot AI review requested due to automatic review settings June 30, 2026 11:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves RDS tagging compatibility by resolving ResourceName based on the ARN resource-type segment (e.g., db, cluster, subgrp) instead of always treating it as a DB instance, preventing misleading DBInstanceNotFound failures for valid non-instance resources.

Changes:

  • Updated RdsService tag operations to dispatch tag reads/writes to instances, clusters, or subnet groups based on parsed ARN resource type.
  • Added tag storage support to DbCluster and DbSubnetGroup models (mirroring DbInstance).
  • Expanded RdsServiceTest coverage to include subnet group and cluster tag round-trips via ARN, missing-subnet-group fault, and unsupported ARN type rejection.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/main/java/io/github/hectorvent/floci/services/rds/RdsService.java Resolves tag targets by ARN resource type and persists tags to the correct backing store.
src/main/java/io/github/hectorvent/floci/services/rds/model/DbSubnetGroup.java Adds tag storage to DB subnet group model.
src/main/java/io/github/hectorvent/floci/services/rds/model/DbCluster.java Adds tag storage to DB cluster model.
src/test/java/io/github/hectorvent/floci/services/rds/RdsServiceTest.java Adds regression and behavior tests for ARN-based tag dispatch and error handling.

ListTagsForResource, AddTagsToResource and RemoveTagsFromResource
unconditionally resolved every ResourceName to a DB instance, so any
non-instance ARN (e.g. a DB subnet group) returned 404 DBInstanceNotFound.
This broke tools such as the Crossplane Upbound AWS provider, which calls
ListTagsForResource on every observe of a DB subnet group and never sees
it become ready.

Parse the ARN's resource-type segment and dispatch to the matching store
(db, cluster, subgrp): return the resource's tags, the resource-specific
NotFound fault when it is missing, and InvalidParameterValue for
unsupported resource types. Add tag storage to DbCluster and DbSubnetGroup
mirroring DbInstance. A bare (non-ARN) resource name still resolves to a
DB instance for backwards compatibility.
@dnlopes
dnlopes force-pushed the fix/rds-list-tags-for-subnet-group branch from c175bbe to 8110e8e Compare June 30, 2026 11:22
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes RDS tag operations (ListTagsForResource, AddTagsToResource, RemoveTagsFromResource) which previously resolved every ResourceName to a DB instance, causing DBInstanceNotFound errors for non-instance ARNs like subnet groups and clusters.

  • Introduces resolveTagHandle() that parses the ARN resource-type segment (db, cluster, subgrp) and dispatches to the correct store, with strict validation for malformed/non-RDS ARNs.
  • Adds tags field to DbCluster and DbSubnetGroup models, mirroring DbInstance.
  • Fixes modifyDbSubnetGroup to preserve existing tags when subnets are updated, a previously reported bug.

Confidence Score: 5/5

Safe to merge. The change is a well-scoped bug fix with clean ARN routing logic, defensive-copy tag fields, and thorough unit test coverage.

The resolveTagHandle dispatch is correct for all three modelled resource types, ARN parsing delegates to the existing validated AwsArnUtils.parse, and error cases (missing resource, non-RDS ARN, malformed ARN, unsupported type) all produce the right AWS-compatible faults. The previously reported tag-loss bug in modifyDbSubnetGroup is correctly fixed. Models follow the existing defensive-copy pattern. No structural issues found.

No files require special attention.

Important Files Changed

Filename Overview
src/main/java/io/github/hectorvent/floci/services/rds/RdsService.java Core logic change: replaces the single-type dbInstanceIdentifierFromResourceName helper with resolveTagHandle, which parses the ARN resource-type and dispatches to the correct store. Tag preservation in modifyDbSubnetGroup is also fixed.
src/main/java/io/github/hectorvent/floci/services/rds/model/DbCluster.java Adds tags field with defensive-copy getter/setter, matching the existing DbInstance pattern.
src/main/java/io/github/hectorvent/floci/services/rds/model/DbSubnetGroup.java Adds tags field with defensive-copy getter/setter, matching the existing DbInstance pattern.
src/test/java/io/github/hectorvent/floci/services/rds/RdsServiceTest.java Adds 7 focused unit tests covering subnet-group and cluster tag round-trips by ARN, tag preservation across modifyDbSubnetGroup, and rejection of unsupported/malformed/non-RDS ARNs.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["listTagsForResource / addTagsToResource / removeTagsFromResource\n(resourceName)"] --> B{starts with 'arn:'?}
    B -- No --> C["type = 'db'\nid = resourceName\n(backwards-compat)"]
    B -- Yes --> D[AwsArnUtils.parse]
    D -- malformed --> E["400 InvalidParameterValue"]
    D --> F{service == 'rds'?}
    F -- No --> E
    F -- Yes --> G["split resource on ':'\ntype = left, id = right"]
    G -- no ':' found --> E
    G --> H{type?}
    C --> H
    H -- db --> I["getDbInstance(id)\n→ TagHandle(instance.getTags, save λ)"]
    H -- cluster --> J["getDbCluster(id)\n→ TagHandle(cluster.getTags, save λ)"]
    H -- subgrp --> K["getDbSubnetGroup(id)\n→ TagHandle(group.getTags, save λ)"]
    H -- other --> L["400 InvalidParameterValue\n(not yet implemented by Floci)"]
    I -- not found --> M["404 DBInstanceNotFound"]
    J -- not found --> N["404 DBClusterNotFoundFault"]
    K -- not found --> O["404 DBSubnetGroupNotFoundFault"]
    I --> P[Return / mutate tags]
    J --> P
    K --> P
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["listTagsForResource / addTagsToResource / removeTagsFromResource\n(resourceName)"] --> B{starts with 'arn:'?}
    B -- No --> C["type = 'db'\nid = resourceName\n(backwards-compat)"]
    B -- Yes --> D[AwsArnUtils.parse]
    D -- malformed --> E["400 InvalidParameterValue"]
    D --> F{service == 'rds'?}
    F -- No --> E
    F -- Yes --> G["split resource on ':'\ntype = left, id = right"]
    G -- no ':' found --> E
    G --> H{type?}
    C --> H
    H -- db --> I["getDbInstance(id)\n→ TagHandle(instance.getTags, save λ)"]
    H -- cluster --> J["getDbCluster(id)\n→ TagHandle(cluster.getTags, save λ)"]
    H -- subgrp --> K["getDbSubnetGroup(id)\n→ TagHandle(group.getTags, save λ)"]
    H -- other --> L["400 InvalidParameterValue\n(not yet implemented by Floci)"]
    I -- not found --> M["404 DBInstanceNotFound"]
    J -- not found --> N["404 DBClusterNotFoundFault"]
    K -- not found --> O["404 DBSubnetGroupNotFoundFault"]
    I --> P[Return / mutate tags]
    J --> P
    K --> P
Loading

Reviews (5): Last reviewed commit: "fix(rds): reject type-less RDS ARNs in t..." | Re-trigger Greptile

dnlopes added 2 commits June 30, 2026 12:25
Address PR review: only fall back to a DB-instance lookup for bare,
non-ARN resource names. Strings that start with arn: are now parsed
strictly and must be RDS ARNs; malformed ARNs and ARNs for other
services return 400 InvalidParameterValue instead of a misleading
DBInstanceNotFound/DBClusterNotFoundFault.
Address PR review: buildSubnetGroup constructs a fresh DbSubnetGroup with
an empty tag map, so modifying a subnet group's subnets dropped any tags
added via AddTagsToResource. Carry the existing group's tags onto the
rebuilt object before persisting, matching AWS ModifyDBSubnetGroup.
@dnlopes

dnlopes commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @greptile-apps — valid P1, fixed in 68fd0ee. modifyDbSubnetGroup now carries the existing group's tags onto the object rebuilt by buildSubnetGroup before persisting, so tags added via AddTagsToResource survive a subnet update (matching AWS ModifyDBSubnetGroup). Added a dbSubnetGroupTagsSurviveModify regression test.

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

The fix looks correct and complete. The one-liner at RdsService.java:772:

group.setTags(existing.getTags());

is exactly the right place — it runs after buildSubnetGroup constructs the fresh object (which initialises tags to an empty map) but before subnetGroups.put, so the persisted object carries the pre-existing tags.

The dbSubnetGroupTagsSurviveModify regression test covers the full round-trip: add tag → modify subnets → assert tag still present. That's the minimal but sufficient proof.

No further concerns on this thread. LGTM.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

@hectorvent hectorvent added bug Something isn't working rds Amazon Relational Database Service (RDS) labels Jul 1, 2026
@hectorvent

Copy link
Copy Markdown
Collaborator

Thanks @dnlopes, this is a clean fix and the Crossplane repro makes the motivation clear. Reviewed against the RDS model and the moto reference.

What checks out:

  • The malformed and non RDS ARN handling matches the reference behavior exactly, including the Invalid resource name: <arn> message (verified against moto _get_resource_for_tagging).
  • DBClusterNotFoundFault is declared for all three tagging ops in service-2.json. DBSubnetGroupNotFoundFault is not declared on them, but it exists as a service level exception shape and Query SDK clients map error codes service wide, so throwing it here is the right call. The model's tagging error lists are simply incomplete, and returning an empty list for a missing resource (what moto does) would be worse.
  • Tag preservation in modifyDbSubnetGroup fixes a real state loss bug, with a test.

Two small non blocking notes:

  1. A type less RDS ARN falls into the sep < 0 branch and is treated as a DB instance id, while real AWS rejects it:
aws rds list-tags-for-resource --resource-name arn:aws:rds:us-east-1:000000000000:mydb
# real AWS: InvalidParameterValue, the resource part must be <type>:<id>
# this PR:  resolved as DB instance "mydb" → DBInstanceNotFound

Treating sep < 0 inside an ARN as InvalidParameterValue (keeping the bare name path as is) would close it.

  1. Valid RDS types Floci does not model yet (og, pg, snapshot, ...) return Tagging is not supported for resource. Those types are taggable on real AWS, so wording it as a Floci scope limit would avoid implying AWS semantics.

Neither needs to hold up the merge. Nice work.

…d-type message

Review feedback from floci-io#1649:
- An RDS ARN whose resource part is not <type>:<id> now returns 400
  InvalidParameterValue (matching real AWS) instead of falling back to a
  DB-instance lookup; bare non-ARN names keep resolving as DB instances.
- The unsupported-resource-type message now states the Floci scope
  limitation instead of implying those types are untaggable on AWS.
@dnlopes

dnlopes commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Hello @hectorvent, both notes addressed in b8358e1:

  1. Type-less RDS ARN: resolveTagHandle no longer falls back to a DB-instance lookup when an RDS ARN's resource part has no : separator — it now returns 400 InvalidParameterValue with the same Invalid resource name: <arn> message as the other invalid-ARN paths, matching real AWS. The bare (non-ARN) name path is unchanged.
  2. Unsupported-type wording: the message for valid-but-unmodeled RDS types (og, pg, snapshot, ...) is now Tagging for resource type '<type>' is not yet implemented by Floci: <arn>, so it reads as a Floci scope limit rather than implying those types aren't taggable on AWS.

@hectorvent hectorvent 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 @dnlopes

@hectorvent
hectorvent merged commit 98464b4 into floci-io:main Jul 7, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working rds Amazon Relational Database Service (RDS) waiting-contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants