github.com/mweagle/Sparta@v1.15.0/decorator/apigateway_domain.go (about)

     1  package decorator
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/aws/aws-sdk-go/aws/session"
     8  	sparta "github.com/mweagle/Sparta"
     9  	spartaCF "github.com/mweagle/Sparta/aws/cloudformation"
    10  	gocf "github.com/mweagle/go-cloudformation"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  const (
    16  	// APIGatewayMappingEntry is the keyname used to store the API Gateway mappings
    17  	APIGatewayMappingEntry = "APIGatewayMappings"
    18  )
    19  
    20  // APIGatewayDomainDecorator returns a ServiceDecoratorHookHandler
    21  // implementation that registers a custom domain for an API Gateway
    22  // service
    23  func APIGatewayDomainDecorator(apiGateway *sparta.API,
    24  	acmCertARN gocf.Stringable,
    25  	basePath string,
    26  	domainName string) sparta.ServiceDecoratorHookHandler {
    27  
    28  	// Attach the domain decorator to the API GW instance
    29  	domainDecorator := func(context map[string]interface{},
    30  		serviceName string,
    31  		template *gocf.Template,
    32  		S3Bucket string,
    33  		S3Key string,
    34  		buildID string,
    35  		awsSession *session.Session,
    36  		noop bool,
    37  		logger *logrus.Logger) error {
    38  
    39  		domainParts := strings.Split(domainName, ".")
    40  		if len(domainParts) != 3 {
    41  			return errors.Errorf("Invalid domain name supplied to APIGatewayDomainDecorator: %s",
    42  				domainName)
    43  		}
    44  		// Add the mapping
    45  		template.Mappings = map[string]*gocf.Mapping{
    46  			APIGatewayMappingEntry: spartaCF.APIGatewayMapping,
    47  		}
    48  		// Resource names
    49  		domainInfoResourceName := sparta.CloudFormationResourceName(apiGateway.LogicalResourceName(),
    50  			"Domain")
    51  		basePathMappingResourceName := sparta.CloudFormationResourceName(apiGateway.LogicalResourceName(), "BasePathMapping")
    52  		dnsRecordResourceName := sparta.CloudFormationResourceName(apiGateway.LogicalResourceName(),
    53  			"CloudFrontDNS")
    54  
    55  		// Then add all the resources
    56  		domainInfo := &gocf.APIGatewayDomainName{
    57  			DomainName: gocf.String(domainName),
    58  		}
    59  		apiGatewayType := ""
    60  		apiGWEndpointConfiguration := apiGateway.EndpointConfiguration
    61  		if apiGWEndpointConfiguration != nil && apiGWEndpointConfiguration.Types != nil {
    62  			typesList := apiGWEndpointConfiguration.Types
    63  			if len(typesList.Literal) == 1 {
    64  				apiGatewayType = typesList.Literal[0].Literal
    65  			} else {
    66  				return errors.Errorf("Invalid API GW types provided to decorator: %#v",
    67  					apiGWEndpointConfiguration.Types)
    68  			}
    69  		}
    70  		attrName := ""
    71  		switch apiGatewayType {
    72  		case "REGIONAL":
    73  			{
    74  				domainInfo.RegionalCertificateArn = acmCertARN.String()
    75  				domainInfo.EndpointConfiguration = &gocf.APIGatewayDomainNameEndpointConfiguration{
    76  					Types: gocf.StringList(gocf.String("REGIONAL")),
    77  				}
    78  				attrName = "RegionalDomainName"
    79  			}
    80  		case "EDGE":
    81  			{
    82  				domainInfo.CertificateArn = acmCertARN.String()
    83  				domainInfo.EndpointConfiguration = &gocf.APIGatewayDomainNameEndpointConfiguration{
    84  					Types: gocf.StringList(gocf.String("EDGE")),
    85  				}
    86  				attrName = "DistributionDomainName"
    87  			}
    88  		default:
    89  			return errors.Errorf("Unsupported API Gateway type: %#v", apiGatewayType)
    90  		}
    91  		template.AddResource(domainInfoResourceName, domainInfo)
    92  
    93  		basePathMapping := gocf.APIGatewayBasePathMapping{
    94  			BasePath:   gocf.String(basePath),
    95  			DomainName: gocf.Ref(domainInfoResourceName).String(),
    96  			RestAPIID:  gocf.Ref(apiGateway.LogicalResourceName()).String(),
    97  		}
    98  		mappingResource := template.AddResource(basePathMappingResourceName, basePathMapping)
    99  		mappingResource.DependsOn = []string{domainInfoResourceName,
   100  			apiGateway.LogicalResourceName()}
   101  
   102  		// Use the HostedZoneName to create the record
   103  		domainZone := domainParts[1:]
   104  		dnsRecordResource := &gocf.Route53RecordSet{
   105  			HostedZoneName: gocf.String(fmt.Sprintf("%s.", strings.Join(domainZone, "."))),
   106  			Name:           gocf.String(fmt.Sprintf("%s.", domainName)),
   107  			Type:           gocf.String("A"),
   108  			AliasTarget: &gocf.Route53RecordSetAliasTarget{
   109  				HostedZoneID: gocf.FindInMap(APIGatewayMappingEntry,
   110  					gocf.Ref("AWS::Region"),
   111  					gocf.String(spartaCF.HostedZoneID)),
   112  				DNSName: gocf.GetAtt(domainInfoResourceName, attrName).String(),
   113  			},
   114  		}
   115  		template.AddResource(dnsRecordResourceName, dnsRecordResource)
   116  
   117  		// Add an output...
   118  		template.Outputs["APIGatewayCustomDomain"] = &gocf.Output{
   119  			Description: "Custom API Gateway Domain",
   120  			Value:       gocf.String(domainName),
   121  		}
   122  		return nil
   123  	}
   124  	return sparta.ServiceDecoratorHookFunc(domainDecorator)
   125  }