Fix API Gateway v1 external name oscillation causing perpetual delete/recreate#1975
Conversation
|
Tested this on my EKS cluster and verified fix |
0933c82 to
b8b1ff8
Compare
There was a problem hiding this comment.
@aditmeno thanks for the contribution and handling this 🙌 Looks good already, left a few small comments to consider.
Would you be interested in testing again with the proposed changes?
Note: I rebased your PR through Github UI, though it might have messed the commit signatures. Feel free to re-rebase. Sorry for the inconvenience
b8b1ff8 to
30a1d89
Compare
…/recreate
The 7 API Gateway v1 resources using FormattedIdentifierFromProvider("/", ...)
had a mismatch between GetExternalNameFn (which reads tfstate["id"]) and
GetIDFn (which joins parameters with "/"). The Terraform AWS provider stores
internal IDs in a different format (prefixed, dash-separated) than the import
format (slash-separated), causing the external-name annotation to change every
reconciliation and triggering perpetual delete/recreate cycles and AWS 429
throttling.
Fixes: crossplane-contrib#1974
Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
30a1d89 to
e87e6f8
Compare
Tested, no regression, PR ready for review @erhancagirici |
Summary
Fixes #1974
7 API Gateway v1 resources using
FormattedIdentifierFromProvider("/", ...)suffer from an external-name annotation oscillation bug. Each reconciliation cycle thecrossplane.io/external-nameannotation changes, triggering perpetual delete/recreate cycles and AWS 429 throttling.Root Cause
FormattedIdentifierFromProviderinheritsGetExternalNameFn = IDAsExternalName(readstfstate["id"]as-is) but overridesGetIDFnto join parameters with/. The Terraform AWS provider stores internal IDs in a different format than the import format:GetIDFnproduces (slash)tfstate["id"]stores (internal)aws_api_gateway_method{restApiId}/{resourceId}/{httpMethod}agm-{restApiId}-{resourceId}-{httpMethod}aws_api_gateway_integration{restApiId}/{resourceId}/{httpMethod}agi-{restApiId}-{resourceId}-{httpMethod}aws_api_gateway_stage{restApiId}/{stageName}ags-{restApiId}-{stageName}aws_api_gateway_method_settings{restApiId}/{stageName}/{methodPath}{restApiId}-{stageName}-{methodPath}aws_api_gateway_gateway_response{restApiId}/{responseType}aggr-{restApiId}-{responseType}aws_api_gateway_integration_response{restApiId}/{resourceId}/{httpMethod}/{statusCode}agir-{restApiId}-{resourceId}-{httpMethod}-{statusCode}aws_api_gateway_method_response{restApiId}/{resourceId}/{httpMethod}/{statusCode}agmr-{restApiId}-{resourceId}-{httpMethod}-{statusCode}Since
GetExternalNameFnreturns the internal format (e.g.,agm-abc-def-GET) but the controller expects the slash format (e.g.,abc/def/GET), the annotation oscillates every reconciliation.Fix
New helper
apiGatewayFormattedIdentifier(prefix, keys...)that bypassestfstate["id"]entirely and reads individual tfstate fields directly:GetExternalNameFn: Reads individual tfstate fields (e.g.,tfstate["rest_api_id"],tfstate["resource_id"],tfstate["http_method"]) and joins them with/separators. This avoids thetfstate["id"]format mismatch entirely.GetIDFn: Reads the same fields from parameters, joins them with-separators, and prepends the prefix (e.g.,agm-). This produces the internal format Terraform expects.This approach is simpler and more robust than parsing the internal ID format — it reads canonical field values directly from tfstate rather than reverse-engineering the prefix/separator encoding.
Round-trip:
GetExternalNameFn(tfstate) → slash format → (annotation) → GetIDFn(params) → internal format → (Terraform) → tfstate fields unchanged → GetExternalNameFn → same slash format. No oscillation.Changes
config/externalname.go: AddedapiGatewayFormattedIdentifier()helper; replaced all 7 affected resource entries.Verification
go build ./config/...— passesgo vet ./config/...— passesFull audit
All other API Gateway v1 resources were audited and confirmed unaffected — they either use
config.IdentifierFromProvider(wheretfstate["id"]is a bare ID or already matches the import format) orFormattedIdentifierFromProviderwhere the internal ID happens to match the slash format. Details in #1974.