-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1473 from snyk/fix/aws_api_gateway_base_path_mapp…
…ing_reconciler Import managed api gateway mappings in the state
- Loading branch information
Showing
5 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
79 changes: 79 additions & 0 deletions
79
pkg/middlewares/aws_api_gateway_base_path_mapping_reconciler.go
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package middlewares | ||
|
||
import ( | ||
"github.com/snyk/driftctl/pkg/resource" | ||
"github.com/snyk/driftctl/pkg/resource/aws" | ||
) | ||
|
||
// AwsApiGatewayBasePathMappingReconciler is used to reconcile API Gateway base path mapping (v1 and v2) | ||
// from both remote and state resources because v1|v2 AWS SDK list endpoints return all mappings | ||
// without distinction. | ||
type AwsApiGatewayBasePathMappingReconciler struct{} | ||
|
||
func NewAwsApiGatewayBasePathMappingReconciler() AwsApiGatewayBasePathMappingReconciler { | ||
return AwsApiGatewayBasePathMappingReconciler{} | ||
} | ||
|
||
func (m AwsApiGatewayBasePathMappingReconciler) Execute(remoteResources, resourcesFromState *[]*resource.Resource) error { | ||
newRemoteResources := make([]*resource.Resource, 0) | ||
managedApiMapping := make([]*resource.Resource, 0) | ||
unmanagedApiMapping := make([]*resource.Resource, 0) | ||
for _, res := range *remoteResources { | ||
// Ignore all resources other than aws_api_gateway_base_path_mapping and aws_apigatewayv2_api_mapping | ||
if res.ResourceType() != aws.AwsApiGatewayBasePathMappingResourceType && | ||
res.ResourceType() != aws.AwsApiGatewayV2MappingResourceType { | ||
newRemoteResources = append(newRemoteResources, res) | ||
continue | ||
} | ||
|
||
// Find a matching state resources | ||
existInState := false | ||
for _, stateResource := range *resourcesFromState { | ||
if res.Equal(stateResource) { | ||
existInState = true | ||
break | ||
} | ||
} | ||
|
||
// Keep track of the resource if it's managed in IaC | ||
if existInState { | ||
managedApiMapping = append(managedApiMapping, res) | ||
continue | ||
} | ||
|
||
// If we're here, it means that we are left with unmanaged path mappings | ||
// in both v1 and v2 format. Let's group real and duplicate path mappings | ||
// in a slice | ||
unmanagedApiMapping = append(unmanagedApiMapping, res) | ||
} | ||
|
||
// We only want to show to our end users unmanaged v1 path mappings | ||
// To do that, we're going to loop over unmanaged path mappings to delete duplicates | ||
// and leave after that only v1 path mappings (e.g. remove v2 ones) | ||
deduplicatedUnmanagedMappings := make([]*resource.Resource, 0, len(unmanagedApiMapping)) | ||
for _, unmanaged := range unmanagedApiMapping { | ||
// Remove duplicates (e.g. same id, the opposite type) | ||
isDuplicate := false | ||
for _, managed := range managedApiMapping { | ||
if managed.ResourceId() == unmanaged.ResourceId() { | ||
isDuplicate = true | ||
break | ||
} | ||
} | ||
if isDuplicate { | ||
continue | ||
} | ||
|
||
// Now keep only v1 path mappings | ||
if unmanaged.ResourceType() == aws.AwsApiGatewayBasePathMappingResourceType { | ||
deduplicatedUnmanagedMappings = append(deduplicatedUnmanagedMappings, unmanaged) | ||
} | ||
} | ||
|
||
// Finally, add both managed and unmanaged resources to remote resources | ||
newRemoteResources = append(newRemoteResources, managedApiMapping...) | ||
newRemoteResources = append(newRemoteResources, deduplicatedUnmanagedMappings...) | ||
|
||
*remoteResources = newRemoteResources | ||
return nil | ||
} |
184 changes: 184 additions & 0 deletions
184
pkg/middlewares/aws_api_gateway_base_path_mapping_reconciler_test.go
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package middlewares | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws/awsutil" | ||
"github.com/r3labs/diff/v2" | ||
"github.com/snyk/driftctl/pkg/resource" | ||
"github.com/snyk/driftctl/pkg/resource/aws" | ||
) | ||
|
||
func TestAwsApiGatewayBasePathMappingReconciler_Execute(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
resourcesFromState []*resource.Resource | ||
remoteResources []*resource.Resource | ||
expected []*resource.Resource | ||
}{ | ||
{ | ||
name: "with managed resources", | ||
resourcesFromState: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
}, | ||
remoteResources: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
}, | ||
expected: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "with unmanaged resources", | ||
resourcesFromState: []*resource.Resource{}, | ||
remoteResources: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
}, | ||
expected: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "with deleted resources", | ||
resourcesFromState: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
}, | ||
remoteResources: []*resource.Resource{}, | ||
expected: []*resource.Resource{}, | ||
}, | ||
{ | ||
name: "with a mix of managed, unmanaged and deleted resources", | ||
resourcesFromState: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping4", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
}, | ||
remoteResources: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping3", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping3", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
}, | ||
expected: []*resource.Resource{ | ||
{ | ||
Id: "mapping1", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping2", | ||
Type: aws.AwsApiGatewayV2MappingResourceType, | ||
}, | ||
{ | ||
Id: "mapping3", | ||
Type: aws.AwsApiGatewayBasePathMappingResourceType, | ||
}, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
m := NewAwsApiGatewayBasePathMappingReconciler() | ||
err := m.Execute(&tt.remoteResources, &tt.resourcesFromState) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
changelog, err := diff.Diff(tt.expected, tt.remoteResources) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if len(changelog) > 0 { | ||
for _, change := range changelog { | ||
t.Errorf("%s got = %v, want %v", strings.Join(change.Path, "."), awsutil.Prettify(change.From), awsutil.Prettify(change.To)) | ||
} | ||
} | ||
}) | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
pkg/resource/aws/testdata/acc/aws_api_gateway_base_path_mapping/.driftignore
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
* | ||
!aws_api_gateway_base_path_mapping | ||
|
||
# We include drifts from aws_apigatewayv2_api_mapping as well to avoid regression regarding this bug: | ||
# https://github.com/snyk/driftctl/issues/1442 | ||
!aws_apigatewayv2_api_mapping |
3 changes: 3 additions & 0 deletions
3
pkg/resource/aws/testdata/acc/aws_api_gateway_base_path_mapping/.terraform.lock.hcl
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.