fix(aws-ec2): tag Elastic IPs and VPC endpoints after creation - #1335
Conversation
NitinKumar004
left a comment
There was a problem hiding this comment.
The routing change is correct. Tags land on the same records TagSpecifications writes, DescribeTags reports the right resource types, and the new m.mu usage is needed. A few parity gaps on the new paths:
Medium
server/aws/ec2/tags.go:350(tagNotFoundCode): an unknowneipalloc-orvpce-id returnsInvalidID.NotFound. The EC2 error reference definesInvalidAllocationID.NotFoundandInvalidVpcEndpointId.NotFoundfor these resources. This package already emits both (address.go:114,endpoint.go:234), andtagNotFoundCodealready special-casesi-andsgr-for the same reason. Terraform's EC2 tag retry matches on the.NotFoundsuffix, so the resource-specific code matters.- Fix: add
case strings.HasPrefix(id, "eipalloc-"): return "InvalidAllocationID.NotFound"and avpce-case returning"InvalidVpcEndpointId.NotFound". Only fall back to the generic code forvpce-svc-. - Update
tags_eip_vpce_test.go:206, which pins the generic code, and the doc comment atproviders/aws/vpc/tags.go:16.
- Fix: add
providers/aws/vpc/tags.go:30(RemoveResourceTags) together withvpc.go:1268(removeTagMapKeys):aws ec2 delete-tags --resources <vpce-id>with no--tagsreturns success but leaves every tag in place (checked againstserve). The EC2 DeleteTags reference says: "If you omit this parameter, we delete all user-defined tags for the specified resources." The compute tagger already does this (providers/aws/ec2/tags.go,UntagResourcewith empty keys), so the same call now behaves differently oni-and oneipalloc-.- Fix: when
keysis empty, drop every key that does not start withaws:. Add a wire test for it.
- Fix: when
Low
server/aws/ec2/tags.go:287/:336: a mixed batch is not atomic.create-tags --resources <eip> vpce-bogus <vpce>fails with NotFound, but the EIP is still tagged (confirmed withdescribe-tags). Real EC2 validates everyResourceIdbefore it writes anything. This existed before the PR, but the PR routes more id types into it.- Fix: do a first pass that probes each id (
tagResource(ctx, id, nil)is already a no-op existence check on both taggers), then write.
- Fix: do a first pass that probes each id (
providers/aws/vpc/endpoint.go:132(ModifyVPCEndpoint) still mutatesep.SubnetIDs,ep.RouteTableIDsandep.Tagswithoutm.mu.DescribeVPCEndpointsnow reads underRLock, so a concurrentModifyVPCEndpointandDescribeVPCEndpointsstill trips-race(endpoint.go:152 against 175). Takem.mu.Lock()there too, so the lock covers every writer.- Tests:
- Nothing pins the new lock. With the
m.mu.Lock()inmutateAddressingTagsremoved, every test in the PR still passes under-race. A concurrentUpdateResourceTags+DescribeAddressestest fails right away without the lock. vpce-svc-is only covered at the provider level. Thevpc-endpoint-serviceresource type and theresource-typefilter onDescribeTagshave no wire test.
- Nothing pins the new lock. With the
Follow-ups (existed before this PR, whole handler)
validateUserTagscounts only the tags in the request. After CreateTags an EIP ended up with 52 tags; real EC2 allows 50 per resource (TagLimitExceeded).- Keys over 128 and values over 256 characters are accepted.
DeleteTags Key=env,Value=wrongdeletes the tag. Real EC2 deletes only when the value matches.aws_vpc_endpointcannot refresh in Terraform (Gateway or Interface) becauseDescribePrefixListsreturnsInvalidAction, so the Terraform check of endpoint tags is blocked for now.
Verified
go build,go vetandgo test -racepass onproviders/aws/vpc,server/aws/ec2,services/networking/...andpersist;go test ./server/aws/...passes.golangci-lint --new-from-rev=origin/developmentreports 0 issues.go generateshows no drift, and the branch merges cleanly onto currentdevelopment.servewith the aws CLI:create-tagsanddelete-tagswork oneipalloc-,vpce-andvpce-svc-, and tags from create time are kept. Thetag:andtag-keyfilters work ondescribe-addressesanddescribe-vpc-endpoints.describe-tagsworks withresource-typeelastic-ip,vpc-endpointorvpc-endpoint-service(several at once too) and withresource-id. Theaws:prefix is rejected. Tags added after creation survive a--persistrestart. Tagging on instances, volumes and security groups is unchanged.- Terraform
aws_eipwith tags: apply, "No changes", an in-place tag change, "No changes", destroy.
CreateTags and DeleteTags on an eipalloc- or vpce- id fell through the EC2 tag router to the compute tagger, which only knows instance, volume, snapshot and image ids, so both calls answered InvalidID.NotFound. Tags set through TagSpecifications at AllocateAddress / CreateVpcEndpoint worked, but nothing could change them afterwards. Route eipalloc-, vpce- and vpce-svc- ids through the networking NetworkResourceTagger onto the same records the create-time tags live on, so DescribeAddresses and DescribeVpcEndpoints (including their tag:<key> and tag-key filters) reflect tags added and removed later. DescribeTags now also reports elastic-ip, vpc-endpoint and vpc-endpoint-service tags. The tag write holds Mock.mu, and DescribeVPCEndpoints now takes the read lock, so a concurrent tag write does not race a describe of the same record.
Review follow-up for the Elastic IP / VPC endpoint tagging change. - An unknown eipalloc-, vpce- or vpce-svc- id answered the generic InvalidID.NotFound. The EC2 error reference defines InvalidAllocationID.NotFound, InvalidVpcEndpointId.NotFound and InvalidVpcEndpointServiceId.NotFound, and this package already returns them from the resources' own actions; tagNotFoundCode now does too. - DeleteTags with no Tag parameter succeeded and left every tag in place on eipalloc-/vpce- ids. EC2 deletes all user-defined tags and keeps the aws: ones; RemoveResourceTags now does that for an empty key list, and the handler resolves the omitted-Tag case itself for every tagger. - DeleteTags Key=k,Value=v deleted k whatever its value. EC2 deletes a key sent without a value regardless of value, and one sent with a value (including "") only on an exact match. - A CreateTags/DeleteTags batch was not atomic: the ids before an unknown one were written. Both handlers now read every resource first (new ResourceTags on the AWS VPC and compute mocks) and write only when the whole batch checks out. - The 50-tag limit counted only the request, so two calls could leave 52 tags on a resource. It now counts existing user tags plus new keys (aws: tags do not count, an overwritten key counts once). Keys over 128 and values over 256 Unicode characters are rejected with InvalidParameterValue, per the EC2 tag restrictions. - ModifyVPCEndpoint wrote the endpoint's fields without Mock.mu while DescribeVPCEndpoints reads them under RLock; it now takes the lock.
c81811a to
c1e8351
Compare
|
Thanks for the review. The two medium findings, the three lows and three of the four follow-ups are fixed in
Every test fails with its fix reverted. I also checked on
Departures:
Still open, and older than this PR: Gates: |
NitinKumar004
left a comment
There was a problem hiding this comment.
All the earlier findings are fixed, and I reproduced each one on serve and in Terraform. Approving.
Earlier findings
- Resource-specific NotFound codes: fixed.
create-tagsanddelete-tagsoneipalloc-nopereturnInvalidAllocationID.NotFound.vpce-0nopereturnsInvalidVpcEndpointId.NotFound.vpce-svc-0nopereturnsInvalidVpcEndpointServiceId.NotFound.rtb-0nopestill falls back toInvalidID.NotFound.
- DeleteTags with no
--tags: fixed.delete-tags --resources <vpce>clears every user tag, and the same holds forvpce-svc-and instances.aws:tags are kept. - Mixed batch not atomic: fixed.
create-tags --resources <eip> vpce-0bogus <vpce>fails and leaves no tag anywhere. The same batch throughdelete-tagsleaves the EIP's tag in place. ModifyVPCEndpointwithoutm.mu: fixed. It now takes the lock. With the lock commented out,TestAddressingTagWriteDoesNotRaceDescribereports DATA RACE, so the test pins the fix.- Tests: fixed. The concurrency test covers the lock. The
vpc-endpoint-serviceresource type and theresource-typefilter have wire tests.
Follow-ups:
- 50-tag limit: fixed. An overwrite at 50 is accepted. Adding keys past 50 returns
TagLimitExceeded, and the count stays at 50. - Key and value length: fixed. A 129-character key or a 257-character value returns
InvalidParameterValue. A 128-character key is accepted. - DeleteTags value match: fixed.
Key=env,Value=wrongkeeps the tag.Key=env,Value=prodremoves it. aws_vpc_endpointrefresh: still open, and it predates this PR. Gateway and Interface endpoints both fail onDescribePrefixLists.
New findings
Nothing blocking. Two small notes:
providers/aws/ec2/tags.go(UntagResource): with an empty key list, the compute tagger clears every tag,aws:ones included. The VPC tagger'sRemoveResourceTagsnow keeps them. The wire path is consistent, becausedeleteTagKeysresolves explicit user keys first. The typed Go API, though, behaves differently fori-/vol-than foreipalloc-/vpce-. Keepingaws:keys in the computeremoveclosure would line the two up.providers/aws/vpc/tags.go(ResourceTags): thevpc-/subnet-/sg-branches go throughStore.Updateand assignTagsback, which is a field write on a read path. Readers are no worse off than they already are againstUpdateVPCTags. Still, the doc comment's "never races" claims more than the code backs up. Soften the wording, or read under the store's read lock instead.
Checked
- aws CLI on
serve:create-tagsanddelete-tagsoneipalloc-,vpce-andvpce-svc-.tag:andtag-keyfilters ondescribe-addressesanddescribe-vpc-endpoints.describe-tagsbyresource-idand byresource-type.aws:prefix rejected withInvalidTagKey.Malformed.- Tagging on instances, volumes, VPCs, subnets and route tables is unchanged.
- Persist: tags added after creation survive a
--persistrestart. - Terraform (aws 6.66.0):
aws_eipwith tags: apply, a clean plan, a tag update, a clean plan, then destroy.aws_ec2_tagon a VPC endpoint: the same cycle.
- Merges: this PR merges cleanly with #1354 and with current
development.go test -raceonserver/aws/ec2andproviders/aws/vpcpasses on the merged tree. - Build and lint:
go build ./...andgo vetpass.go test -racepasses onproviders/aws/vpc,providers/aws/ec2,server/aws/ec2,services/networking/...andpersist.golangci-lint --new-from-rev=origin/developmentreports 0 issues.
What was broken
CreateTags/DeleteTagson an Elastic IP allocation id or a VPC endpoint id failed. With the aws-sdk-go-v2 EC2 client:Same for
vpce-…. The EC2 tag router inserver/aws/ec2/tags.goonly sentrtb-/igw-/nat-/acl-/dopt-/pcx-/pl-/eigw-/sgr-to the networking tagger; every other prefix went to the compute tagger, which only knowsi-/vol-/snap-/ami-. Tags set at create time viaTagSpecificationsworked, but nothing could change them afterwards.What changed
providers/aws/vpc/tags.go:UpdateResourceTags/RemoveResourceTagsnow handleeipalloc-,vpce-andvpce-svc-ids, writing to the same recordsAllocateAddress/CreateVpcEndpoint/CreateVpcEndpointServiceConfigurationstore their create-time tags on.vpce-svc-is matched beforevpce-since it shares the prefix. The write holdsMock.mu, like the other EIP / endpoint-service mutators.providers/aws/vpc/endpoint.go:DescribeVPCEndpointsnow takesMock.mu.RLock, so a tag write cannot race a describe of the same endpoint.server/aws/ec2/tags.go:eipalloc-andvpce-(which also coversvpce-svc-) are routed toNetworkResourceTagger.DescribeTagsnow also reports tags forelastic-ip,vpc-endpointandvpc-endpoint-serviceresources.DescribeAddressesandDescribeVpcEndpointsalready supportedtag:<key>andtag-keyfilters; they now see tags added or removed after creation.How it was tested
TestAddressingResourceTagger(providers/aws/vpc/tags_test.go): tag/untag on EIP, endpoint and endpoint service; create-time tags are kept; unknown ids of each prefix are NotFound.server/aws/ec2/tags_eip_vpce_test.go, real aws-sdk-go-v2 EC2 client againsthttptest):AllocateAddress/CreateVpcEndpointwith a TagSpecification, thenCreateTags, thenDescribeAddresses/DescribeVpcEndpointsfiltered bytag:ownerand bytag-key, thenDescribeTagsby resource-id (checking the resource type), thenDeleteTags, then Describe again. Also:CreateTagson a missingeipalloc-/vpce-id returnsInvalidID.NotFound.TestCreateTagsOnElasticIPandTestCreateTagsOnVPCEndpointfailed with theInvalidID.NotFounderror above. With the fix they pass.Commands:
The full
golangci-lint run ./...still reports issues that already exist ondevelopment; none of them are in lines this PR adds.