Skip to content

Commit c083c4b

Browse files
committed
Ensure generated CFN stack names fit API requirements on string length and charset
1 parent a7a4bbb commit c083c4b

File tree

3 files changed

+49
-6
lines changed

3 files changed

+49
-6
lines changed

ecs-cli/modules/cli/servicediscovery/servicediscovery_app.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import (
2929
)
3030

3131
const (
32-
privateDNSNamespaceStackNameFormat = "amazon-ecs-cli-setup-%s-%s-private-dns-namespace"
33-
serviceDiscoveryServiceStackNameFormat = "amazon-ecs-cli-setup-%s-%s-service-discovery-service"
32+
privateDNSNamespaceStackNameFormat = "amazon-ecs-cli-setup-private-dns-namespace-%s-%s"
33+
serviceDiscoveryServiceStackNameFormat = "amazon-ecs-cli-setup-service-discovery-service-%s-%s"
3434
)
3535

3636
const (

ecs-cli/modules/cli/servicediscovery/servicediscovery_app_test.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package servicediscovery
1616
import (
1717
"flag"
1818
"fmt"
19+
"strings"
1920
"testing"
2021

2122
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/cloudformation"
@@ -38,8 +39,8 @@ const (
3839
testServiceName = "service"
3940
testDescription = "clyde loves pudding"
4041
otherTestDescription = "pudding plays hard to get"
41-
testNamespaceStackName = "amazon-ecs-cli-setup-cluster-service-private-dns-namespace"
42-
testSDSStackName = "amazon-ecs-cli-setup-cluster-service-service-discovery-service"
42+
testNamespaceStackName = "amazon-ecs-cli-setup-private-dns-namespace-cluster-service"
43+
testSDSStackName = "amazon-ecs-cli-setup-service-discovery-service-cluster-service"
4344
testNamespaceName = "corp"
4445
otherTestNamespaceName = "dumpling"
4546
testVPCID = "vpc-8BAADF00D"
@@ -615,6 +616,22 @@ func TestDeleteServiceDiscoveryStackNotFoundError(t *testing.T) {
615616
assert.Error(t, err, "Expected error calling delete")
616617
}
617618

619+
func TestCFNStackName(t *testing.T) {
620+
// underscore is allowed in cluster and service names, but not CFNStack names
621+
clusterName := "supercalifragilisticexpialidocious_________1234_"
622+
serviceName := "anotherreallylongstring_______________________________________hi______________________________________________________wassup__________________________________________123456789"
623+
624+
sdsStackName := cfnStackName(serviceDiscoveryServiceStackNameFormat, clusterName, serviceName)
625+
namespaceStackName := cfnStackName(privateDNSNamespaceStackNameFormat, clusterName, serviceName)
626+
627+
// underscore is allowed in cluster and service names, but not CFNStack names
628+
assert.False(t, strings.Contains(sdsStackName, "_"), "Underscores are not allowed in CFN Stack names")
629+
assert.False(t, strings.Contains(namespaceStackName, "_"), "Underscores are not allowed in CFN Stack names")
630+
// CFN Stacknames must be no longer than 128 characters
631+
assert.True(t, len(sdsStackName) <= 128, "CFN Stack names must be no longer than 128 characters")
632+
assert.True(t, len(namespaceStackName) <= 128, "CFN Stack names must be no longer than 128 characters")
633+
}
634+
618635
func testCreateServiceDiscovery(t *testing.T, networkMode string, ecsParamsSD *utils.ServiceDiscovery, c *cli.Context, validateNamespace validateNamespaceParamsFunc, validateSDS validateSDSParamsFunc, createNamespace bool) (*ecs.ServiceRegistry, error) {
619636
ctrl := gomock.NewController(t)
620637
defer ctrl.Finish()

ecs-cli/modules/cli/servicediscovery/servicediscovery_helper.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package servicediscovery
1515

1616
import (
1717
"fmt"
18+
"regexp"
1819
"strconv"
1920

2021
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/cloudformation"
@@ -45,6 +46,10 @@ const (
4546
parameterKeyHealthCheckCustomConfigFailureThreshold = "FailureThreshold"
4647
)
4748

49+
const (
50+
cfnStackNameMaxLength = 128
51+
)
52+
4853
var requiredParamsSDS = []string{parameterKeyNamespaceID, parameterKeySDSName, parameterKeyDNSType}
4954
var requiredParamsNamespace = []string{parameterKeyVPCID, parameterKeyNamespaceName}
5055

@@ -212,8 +217,29 @@ func getOutputIDFromStack(cfnClient cloudformation.CloudformationClient, stackNa
212217
return nil, fmt.Errorf("Failed to find output %s in stack %s", outputKey, stackName)
213218
}
214219

215-
func cfnStackName(stackName, cluster, service string) string {
216-
return fmt.Sprintf(stackName, cluster, service)
220+
func cfnStackName(stackNameFmt, cluster, service string) string {
221+
maxLength := (cfnStackNameMaxLength - len(stackNameFmt)) / 2
222+
name := fmt.Sprintf(stackNameFmt, truncate(cluster, maxLength), truncate(service, maxLength))
223+
return sanitize(name)
224+
}
225+
226+
// Makes the given string a valid CFN stack name
227+
// by replacing all characters that are not alphanumeric or hyphen with 0
228+
// and truncating at 128 characters
229+
func sanitize(s string) string {
230+
reg, err := regexp.Compile("[^a-zA-Z0-9-]+")
231+
if err != nil {
232+
// the regex compiles, the unit tests verify this, there's no need to return this error
233+
logrus.Fatal(err)
234+
}
235+
return reg.ReplaceAllString(s, "0")
236+
}
237+
238+
func truncate(s string, length int) string {
239+
if len(s) > length {
240+
return s[:length]
241+
}
242+
return s
217243
}
218244

219245
func getSDSCFNParams(namespaceID, sdsName, networkMode string, input *utils.ServiceDiscovery) *cloudformation.CfnStackParams {

0 commit comments

Comments
 (0)