Skip to content

Commit 1ed231d

Browse files
Merge pull request #1975 from canary-technologies-corp/fix-apigateway-v1-external-name-oscillation
Fix API Gateway v1 external name oscillation causing perpetual delete/recreate
2 parents d6c6c20 + 60cc101 commit 1ed231d

10 files changed

Lines changed: 52 additions & 30 deletions

File tree

config/externalname.go

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -272,17 +272,17 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{
272272
// API Gateway domain names can be imported using their name
273273
"aws_api_gateway_domain_name": config.IdentifierFromProvider,
274274
// aws_api_gateway_gateway_response can be imported using REST-API-ID/RESPONSE-TYPE
275-
"aws_api_gateway_gateway_response": FormattedIdentifierFromProvider("/", "rest_api_id", "response_type"),
275+
"aws_api_gateway_gateway_response": apiGatewayFormattedIdentifier("aggr", "rest_api_id", "response_type"),
276276
// aws_api_gateway_integration can be imported using REST-API-ID/RESOURCE-ID/HTTP-METHOD
277-
"aws_api_gateway_integration": FormattedIdentifierFromProvider("/", "rest_api_id", "resource_id", "http_method"),
277+
"aws_api_gateway_integration": apiGatewayFormattedIdentifier("agi", "rest_api_id", "resource_id", "http_method"),
278278
// aws_api_gateway_integration_response can be imported using REST-API-ID/RESOURCE-ID/HTTP-METHOD/STATUS-CODE
279-
"aws_api_gateway_integration_response": FormattedIdentifierFromProvider("/", "rest_api_id", "resource_id", "http_method", "status_code"),
279+
"aws_api_gateway_integration_response": apiGatewayFormattedIdentifier("agir", "rest_api_id", "resource_id", "http_method", "status_code"),
280280
// aws_api_gateway_method can be imported using REST-API-ID/RESOURCE-ID/HTTP-METHOD
281-
"aws_api_gateway_method": FormattedIdentifierFromProvider("/", "rest_api_id", "resource_id", "http_method"),
281+
"aws_api_gateway_method": apiGatewayFormattedIdentifier("agm", "rest_api_id", "resource_id", "http_method"),
282282
// aws_api_gateway_method_response can be imported using REST-API-ID/RESOURCE-ID/HTTP-METHOD/STATUS-CODE
283-
"aws_api_gateway_method_response": FormattedIdentifierFromProvider("/", "rest_api_id", "resource_id", "http_method", "status_code"),
283+
"aws_api_gateway_method_response": apiGatewayFormattedIdentifier("agmr", "rest_api_id", "resource_id", "http_method", "status_code"),
284284
// aws_api_gateway_method_settings can be imported using REST-API-ID/STAGE-NAME/METHOD-PATH
285-
"aws_api_gateway_method_settings": FormattedIdentifierFromProvider("/", "rest_api_id", "stage_name", "method_path"),
285+
"aws_api_gateway_method_settings": apiGatewayFormattedIdentifier("", "rest_api_id", "stage_name", "method_path"),
286286
// aws_api_gateway_model can be imported using REST-API-ID/NAME
287287
"aws_api_gateway_model": config.IdentifierFromProvider,
288288
// aws_api_gateway_request_validator can be imported using REST-API-ID/REQUEST-VALIDATOR-ID
@@ -294,7 +294,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{
294294
// aws_api_gateway_rest_api_policy can be imported by using the REST API ID
295295
"aws_api_gateway_rest_api_policy": FormattedIdentifierFromProvider("", "rest_api_id"),
296296
// aws_api_gateway_stage can be imported using REST-API-ID/STAGE-NAME
297-
"aws_api_gateway_stage": FormattedIdentifierFromProvider("/", "rest_api_id", "stage_name"),
297+
"aws_api_gateway_stage": apiGatewayFormattedIdentifier("ags", "rest_api_id", "stage_name"),
298298
// AWS API Gateway Usage Plan can be imported using the id
299299
"aws_api_gateway_usage_plan": config.IdentifierFromProvider,
300300
// AWS API Gateway Usage Plan Key can be imported using the USAGE-PLAN-ID/USAGE-PLAN-KEY-ID
@@ -3376,6 +3376,51 @@ func apiGatewayAccount() config.ExternalName {
33763376
return e
33773377
}
33783378

3379+
// apiGatewayFormattedIdentifier configures external name for API Gateway
3380+
// v1 resources where the Terraform internal ID format differs from the import
3381+
// format. GetExternalNameFn reads tfstate fields directly (e.g.,
3382+
// tfstate["rest_api_id"]) and joins them with "/" separators. GetIDFn reads
3383+
// parameters and joins them with "-" separators, optionally adding a prefix.
3384+
// This ensures consistent values to prevent external name oscillation.
3385+
func apiGatewayFormattedIdentifier(prefix string, keys ...string) config.ExternalName {
3386+
e := config.IdentifierFromProvider
3387+
e.GetExternalNameFn = func(tfstate map[string]interface{}) (string, error) {
3388+
vals := make([]string, len(keys))
3389+
for i, key := range keys {
3390+
val, ok := tfstate[key]
3391+
if !ok {
3392+
return "", errors.Errorf("parameter %q cannot be empty", key)
3393+
}
3394+
s, ok := val.(string)
3395+
if !ok {
3396+
return "", errors.Errorf("parameter %q must be a string", key)
3397+
}
3398+
vals[i] = s
3399+
}
3400+
return strings.Join(vals, "/"), nil
3401+
}
3402+
e.GetIDFn = func(_ context.Context, _ string, parameters map[string]interface{}, _ map[string]interface{}) (string, error) {
3403+
vals := make([]string, len(keys))
3404+
for i, key := range keys {
3405+
val, ok := parameters[key]
3406+
if !ok {
3407+
return "", errors.Errorf("parameter %q cannot be empty", key)
3408+
}
3409+
s, ok := val.(string)
3410+
if !ok {
3411+
return "", errors.Errorf("parameter %q must be a string", key)
3412+
}
3413+
vals[i] = s
3414+
}
3415+
id := strings.Join(vals, "-")
3416+
if prefix != "" {
3417+
return prefix + "-" + id, nil
3418+
}
3419+
return id, nil
3420+
}
3421+
return e
3422+
}
3423+
33793424
// fullARNTemplate builds a templated string for constructing a terraform id component which is an ARN, which includes
33803425
// the aws partition, service, region, account id, and resource. This is by far the most common form of ARN.
33813426
// e.g. arn:aws:ec2:ap-south-1:123456789012:instance/i-1234567890ab

examples/apigateway/cluster/v1beta1/integrationresponse.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ apiVersion: apigateway.aws.upbound.io/v1beta1
2020
kind: IntegrationResponse
2121
metadata:
2222
annotations:
23-
# Disabling import step because of the unstable ID
24-
uptest.upbound.io/disable-import: "true"
2523
meta.upbound.io/example-id: apigateway/v1beta1/integrationresponse
2624
labels:
2725
testing.upbound.io/example-name: integration
@@ -53,7 +51,6 @@ apiVersion: apigateway.aws.upbound.io/v1beta1
5351
kind: Integration
5452
metadata:
5553
annotations:
56-
uptest.upbound.io/disable-import: "true"
5754
meta.upbound.io/example-id: apigateway/v1beta1/integrationresponse
5855
labels:
5956
testing.upbound.io/example-name: integration
@@ -76,7 +73,6 @@ apiVersion: apigateway.aws.upbound.io/v1beta1
7673
kind: Method
7774
metadata:
7875
annotations:
79-
uptest.upbound.io/disable-import: "true"
8076
meta.upbound.io/example-id: apigateway/v1beta1/integrationresponse
8177
labels:
8278
testing.upbound.io/example-name: integration
@@ -97,7 +93,6 @@ apiVersion: apigateway.aws.upbound.io/v1beta1
9793
kind: MethodResponse
9894
metadata:
9995
annotations:
100-
uptest.upbound.io/disable-import: "true"
10196
meta.upbound.io/example-id: apigateway/v1beta1/integrationresponse
10297
labels:
10398
testing.upbound.io/example-name: integration

examples/apigateway/cluster/v1beta2/integrationresponse.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ kind: IntegrationResponse
2323
metadata:
2424
annotations:
2525
meta.upbound.io/example-id: apigateway/v1beta2/restapi
26-
uptest.upbound.io/disable-import: "true"
2726
labels:
2827
testing.upbound.io/example-name: integration
2928
name: mydemointegrationresponse
@@ -57,7 +56,6 @@ kind: Integration
5756
metadata:
5857
annotations:
5958
meta.upbound.io/example-id: apigateway/v1beta2/restapi
60-
uptest.upbound.io/disable-import: "true"
6159
labels:
6260
testing.upbound.io/example-name: integration
6361
name: mydemointegration
@@ -82,7 +80,6 @@ kind: Method
8280
metadata:
8381
annotations:
8482
meta.upbound.io/example-id: apigateway/v1beta2/restapi
85-
uptest.upbound.io/disable-import: "true"
8683
labels:
8784
testing.upbound.io/example-name: integration
8885
name: mydemomethod
@@ -105,7 +102,6 @@ kind: MethodResponse
105102
metadata:
106103
annotations:
107104
meta.upbound.io/example-id: apigateway/v1beta2/restapi
108-
uptest.upbound.io/disable-import: "true"
109105
labels:
110106
testing.upbound.io/example-name: integration
111107
name: response-200

examples/apigateway/cluster/v1beta2/methodsettings.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ kind: MethodSettings
77
metadata:
88
annotations:
99
meta.upbound.io/example-id: apigateway/v1beta2/methodsettings
10-
uptest.upbound.io/disable-import: "true"
1110
labels:
1211
testing.upbound.io/example-name: example-methodsettings
1312
name: example-methodsettings-all
@@ -29,7 +28,6 @@ kind: Stage
2928
metadata:
3029
annotations:
3130
meta.upbound.io/example-id: apigateway/v1beta2/methodsettings
32-
uptest.upbound.io/disable-import: "true"
3331
labels:
3432
testing.upbound.io/example-name: example-methodsettings
3533
name: example-methodsettings-stage

examples/apigateway/cluster/v1beta2/stage.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ kind: Stage
77
metadata:
88
annotations:
99
meta.upbound.io/example-id: apigateway/v1beta2/stage
10-
uptest.upbound.io/disable-import: "true"
1110
labels:
1211
testing.upbound.io/example-name: stage
1312
name: example-stage

examples/apigateway/cluster/v1beta2/usageplan.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ kind: Stage
9090
metadata:
9191
annotations:
9292
meta.upbound.io/example-id: apigateway/v1beta2/usageplan
93-
uptest.upbound.io/disable-import: "true"
9493
labels:
9594
testing.upbound.io/example-name: stage
9695
name: development
@@ -110,7 +109,6 @@ kind: Stage
110109
metadata:
111110
annotations:
112111
meta.upbound.io/example-id: apigateway/v1beta2/usageplan
113-
uptest.upbound.io/disable-import: "true"
114112
labels:
115113
testing.upbound.io/example-name: stage
116114
name: production

examples/apigateway/namespaced/v1beta1/integrationresponse.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ kind: IntegrationResponse
2222
metadata:
2323
annotations:
2424
meta.upbound.io/example-id: apigateway/v1beta1/restapi
25-
uptest.upbound.io/disable-import: "true"
2625
labels:
2726
testing.upbound.io/example-name: integration
2827
name: mydemointegrationresponse
@@ -55,7 +54,6 @@ kind: Integration
5554
metadata:
5655
annotations:
5756
meta.upbound.io/example-id: apigateway/v1beta1/restapi
58-
uptest.upbound.io/disable-import: "true"
5957
labels:
6058
testing.upbound.io/example-name: integration
6159
name: mydemointegration
@@ -79,7 +77,6 @@ kind: Method
7977
metadata:
8078
annotations:
8179
meta.upbound.io/example-id: apigateway/v1beta1/restapi
82-
uptest.upbound.io/disable-import: "true"
8380
labels:
8481
testing.upbound.io/example-name: integration
8582
name: mydemomethod
@@ -101,7 +98,6 @@ kind: MethodResponse
10198
metadata:
10299
annotations:
103100
meta.upbound.io/example-id: apigateway/v1beta1/restapi
104-
uptest.upbound.io/disable-import: "true"
105101
labels:
106102
testing.upbound.io/example-name: integration
107103
name: response-200

examples/apigateway/namespaced/v1beta1/methodsettings.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ kind: MethodSettings
77
metadata:
88
annotations:
99
meta.upbound.io/example-id: apigateway/v1beta1/methodsettings
10-
uptest.upbound.io/disable-import: "true"
1110
labels:
1211
testing.upbound.io/example-name: example-methodsettings
1312
name: example-methodsettings-all
@@ -30,7 +29,6 @@ kind: Stage
3029
metadata:
3130
annotations:
3231
meta.upbound.io/example-id: apigateway/v1beta1/methodsettings
33-
uptest.upbound.io/disable-import: "true"
3432
labels:
3533
testing.upbound.io/example-name: example-methodsettings
3634
name: example-methodsettings-stage

examples/apigateway/namespaced/v1beta1/stage.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ kind: Stage
77
metadata:
88
annotations:
99
meta.upbound.io/example-id: apigateway/v1beta1/stage
10-
uptest.upbound.io/disable-import: "true"
1110
labels:
1211
testing.upbound.io/example-name: stage
1312
name: example-stage

examples/apigateway/namespaced/v1beta1/usageplan.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ kind: Stage
9393
metadata:
9494
annotations:
9595
meta.upbound.io/example-id: apigateway/v1beta1/usageplan
96-
uptest.upbound.io/disable-import: "true"
9796
labels:
9897
testing.upbound.io/example-name: stage
9998
name: development
@@ -114,7 +113,6 @@ kind: Stage
114113
metadata:
115114
annotations:
116115
meta.upbound.io/example-id: apigateway/v1beta1/usageplan
117-
uptest.upbound.io/disable-import: "true"
118116
labels:
119117
testing.upbound.io/example-name: stage
120118
name: production

0 commit comments

Comments
 (0)