internal/pkg/template/overrides.go (110 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package template
import (
"fmt"
"sort"
"strings"
"unicode"
)
const (
cdkVersion = "2.137.0"
cdkConstructsMinVersion = "10.0.0"
cdkTemplatesPath = "overrides/cdk"
yamlPatchTemplatesPath = "overrides/yamlpatch"
)
var (
cdkAliasForService = map[string]string{
"ApiGatewayV2": "apigwv2",
"AppRunner": "ar",
"AutoScalingPlans": "asgplans",
"ApplicationAutoScaling": "appasg",
"AutoScaling": "asg",
"CertificateManager": "acm",
"CloudFormation": "cfn",
"CloudFront": "cf",
"ServiceDiscovery": "sd",
"CloudWatch": "cw",
"CodeBuild": "cb",
"CodePipeline": "cp",
"DynamoDB": "ddb",
"ElasticLoadBalancingV2": "elbv2",
"OpenSearchService": "oss",
"Route53": "r53",
"StepFunctions": "sfn",
}
)
// CFNResource represents a resource rendered in a CloudFormation template.
type CFNResource struct {
Type CFNType
LogicalID string
}
// CDKImport is the interface to import a CDK package.
type CDKImport interface {
ImportName() string
ImportShortRename() string
}
type cfnResources []CFNResource
// Imports returns a list of CDK imports for a given list of CloudFormation resources.
func (rs cfnResources) Imports() []CDKImport {
// Find a unique CFN type per service.
// For example, given "AWS::ECS::Service" and "AWS::ECS::TaskDef" we want to retain only one of them.
seen := make(map[string]CFNType)
for _, r := range rs {
serviceName := strings.Split(strings.ToLower(string(r.Type)), "::")[1]
if _, ok := seen[serviceName]; ok {
continue
}
seen[serviceName] = r.Type
}
imports := make([]CDKImport, len(seen))
i := 0
for _, resourceType := range seen {
imports[i] = resourceType
i += 1
}
sort.Slice(imports, func(i, j int) bool { // Ensure the output is deterministic for unit tests.
return imports[i].ImportShortRename() < imports[j].ImportShortRename()
})
return imports
}
// CFNType is a CloudFormation resource type such as "AWS::ECS::Service".
type CFNType string
// ImportName returns the name of the CDK package for the given CloudFormation type.
func (t CFNType) ImportName() string {
parts := strings.Split(strings.ToLower(string(t)), "::")
return fmt.Sprintf("aws_%s", parts[1])
}
// ImportShortRename returns a human-friendly shortened rename of the CDK package for the given CloudFormation type.
func (t CFNType) ImportShortRename() string {
parts := strings.Split(string(t), "::")
name := parts[1]
if rename, ok := cdkAliasForService[name]; ok {
return rename
}
return strings.ToLower(name)
}
// L1ConstructName returns the name of the L1 construct representing the CloudFormation type.
func (t CFNType) L1ConstructName() string {
parts := strings.Split(string(t), "::")
return fmt.Sprintf("Cfn%s", parts[2])
}
// WalkOverridesCDKDir walks through the overrides/cdk templates and calls fn for each parsed template file.
func (t *Template) WalkOverridesCDKDir(resources []CFNResource, fn WalkDirFunc, requiresEnv bool) error {
type metadata struct {
Version string
ConstructsVersion string
Resources cfnResources
RequiresEnv bool
}
return t.walkDir(cdkTemplatesPath, cdkTemplatesPath, metadata{
Version: cdkVersion,
ConstructsVersion: cdkConstructsMinVersion,
Resources: resources,
RequiresEnv: requiresEnv,
}, fn, WithFuncs(
map[string]interface{}{
// transform all the initial capital letters into lower letters.
"lowerInitialLetters": func(serviceName string) string {
firstSmall := len(serviceName)
for i, r := range serviceName {
if unicode.IsLower(r) {
firstSmall = i
break
}
}
return strings.ToLower(serviceName[:firstSmall]) + serviceName[firstSmall:]
},
},
))
}
// WalkOverridesPatchDir walks through the overrides/yamlpatch templates and calls fn for each parsed template file.
func (t *Template) WalkOverridesPatchDir(fn WalkDirFunc) error {
return t.walkDir(yamlPatchTemplatesPath, yamlPatchTemplatesPath, struct{}{}, fn)
}