driftctl/pkg/middlewares/aws_route_table_expander.go

200 lines
6.8 KiB
Go

package middlewares
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/cloudskiff/driftctl/pkg/alerter"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
)
type invalidRouteAlert struct {
message string
}
func newInvalidRouteAlert(awsRouteTableResourceType, tableId string) *invalidRouteAlert {
message := fmt.Sprintf("Skipped invalid route found in state for %s.%s", awsRouteTableResourceType, tableId)
return &invalidRouteAlert{message}
}
func (i *invalidRouteAlert) Message() string {
return i.message
}
func (i *invalidRouteAlert) ShouldIgnoreResource() bool {
return false
}
// Explodes routes found in aws_default_route_table.route and aws_route_table.route to dedicated resources
type AwsRouteTableExpander struct {
alerter alerter.AlerterInterface
resourceFactory resource.ResourceFactory
}
func NewAwsRouteTableExpander(alerter alerter.AlerterInterface, resourceFactory resource.ResourceFactory) AwsRouteTableExpander {
return AwsRouteTableExpander{
alerter,
resourceFactory,
}
}
func (m AwsRouteTableExpander) Execute(remoteResources, resourcesFromState *[]resource.Resource) error {
newList := make([]resource.Resource, 0, len(*resourcesFromState))
for _, res := range *resourcesFromState {
// Ignore all resources other than (default) routes tables
if res.TerraformType() != aws.AwsRouteTableResourceType &&
res.TerraformType() != aws.AwsDefaultRouteTableResourceType {
newList = append(newList, res)
continue
}
table, _ := res.(*resource.AbstractResource)
newList = append(newList, res)
var err error
if res.TerraformType() == aws.AwsDefaultRouteTableResourceType {
err = m.handleDefaultTable(table, &newList, *resourcesFromState)
} else {
err = m.handleTable(table, &newList, *resourcesFromState)
}
if err != nil {
return err
}
}
newRemoteResources := make([]resource.Resource, 0)
for _, remoteRes := range *remoteResources {
if remoteRes.TerraformType() != aws.AwsRouteTableResourceType &&
remoteRes.TerraformType() != aws.AwsDefaultRouteTableResourceType {
newRemoteResources = append(newRemoteResources, remoteRes)
continue
}
table, _ := remoteRes.(*resource.AbstractResource)
table.Attrs.SafeDelete([]string{"route"})
newRemoteResources = append(newRemoteResources, table)
}
*resourcesFromState = newList
*remoteResources = newRemoteResources
return nil
}
func (m *AwsRouteTableExpander) handleTable(table *resource.AbstractResource, results *[]resource.Resource, resourcesFromState []resource.Resource) error {
routes, exist := table.Attrs.Get("route")
if !exist || routes == nil {
return nil
}
for _, route := range routes.([]interface{}) {
route := route.(map[string]interface{})
cidrBlock := ""
if route["cidr_block"] != nil {
cidrBlock = route["cidr_block"].(string)
}
ipv6CidrBlock := ""
if route["ipv6_cidr_block"] != nil {
ipv6CidrBlock = route["ipv6_cidr_block"].(string)
}
routeId, err := aws.CalculateRouteID(&table.Id, &cidrBlock, &ipv6CidrBlock)
if err != nil {
m.alerter.SendAlert(aws.AwsRouteTableResourceType, newInvalidRouteAlert(aws.AwsRouteTableResourceType, table.Id))
continue
}
data := map[string]interface{}{
"destination_cidr_block": route["cidr_block"],
"destination_ipv6_cidr_block": route["ipv6_cidr_block"],
"egress_only_gateway_id": route["egress_only_gateway_id"],
"gateway_id": route["gateway_id"],
"id": routeId,
"instance_id": route["instance_id"],
"instance_owner_id": "",
"local_gateway_id": route["local_gateway_id"],
"nat_gateway_id": route["nat_gateway_id"],
"network_interface_id": route["network_interface_id"],
"origin": "CreateRoute",
"route_table_id": table.Id,
"state": "active",
"transit_gateway_id": route["transit_gateway_id"],
"vpc_endpoint_id": route["vpc_endpoint_id"],
"vpc_peering_connection_id": route["vpc_peering_connection_id"],
}
// Don't expand if the route already exists as a dedicated resource
if m.routeExists(routeId, resourcesFromState) {
continue
}
newRes := m.resourceFactory.CreateAbstractResource(aws.AwsRouteResourceType, routeId, data)
*results = append(*results, newRes)
logrus.WithFields(logrus.Fields{
"route": routeId,
}).Debug("Created new route from route table")
}
table.Attrs.SafeDelete([]string{"route"})
return nil
}
func (m *AwsRouteTableExpander) handleDefaultTable(table *resource.AbstractResource, results *[]resource.Resource, resourcesFromState []resource.Resource) error {
routes, exist := table.Attrs.Get("route")
if !exist || routes == nil {
return nil
}
for _, route := range routes.([]interface{}) {
route := route.(map[string]interface{})
cidrBlock := ""
if route["cidr_block"] != nil {
cidrBlock = route["cidr_block"].(string)
}
ipv6CidrBlock := ""
if route["ipv6_cidr_block"] != nil {
ipv6CidrBlock = route["ipv6_cidr_block"].(string)
}
routeId, err := aws.CalculateRouteID(&table.Id, &cidrBlock, &ipv6CidrBlock)
if err != nil {
m.alerter.SendAlert(aws.AwsDefaultRouteTableResourceType, newInvalidRouteAlert(aws.AwsDefaultRouteTableResourceType, table.Id))
continue
}
data := map[string]interface{}{
"destination_cidr_block": route["cidr_block"],
"destination_ipv6_cidr_block": route["ipv6_cidr_block"],
"egress_only_gateway_id": route["egress_only_gateway_id"],
"gateway_id": route["gateway_id"],
"id": routeId,
"instance_id": route["instance_id"],
"nat_gateway_id": route["nat_gateway_id"],
"network_interface_id": route["network_interface_id"],
"origin": "CreateRoute",
"route_table_id": table.Id,
"state": "active",
"transit_gateway_id": route["transit_gateway_id"],
"vpc_endpoint_id": route["vpc_endpoint_id"],
"vpc_peering_connection_id": route["vpc_peering_connection_id"],
}
// Don't expand if the route already exists as a dedicated resource
if m.routeExists(routeId, resourcesFromState) {
continue
}
newRes := m.resourceFactory.CreateAbstractResource(aws.AwsRouteResourceType, routeId, data)
*results = append(*results, newRes)
logrus.WithFields(logrus.Fields{
"route": routeId,
}).Debug("Created new route from default route table")
}
table.Attrs.SafeDelete([]string{"route"})
return nil
}
func (m *AwsRouteTableExpander) routeExists(routeId string, resourcesFromState []resource.Resource) bool {
for _, res := range resourcesFromState {
if res.TerraformType() == aws.AwsRouteResourceType && res.TerraformId() == routeId {
return true
}
}
return false
}