Skip to content
This repository was archived by the owner on Sep 30, 2020. It is now read-only.

Commit 1f08f3b

Browse files
authored
Merge pull request #443 from mumoshu/validate-iam-role-name-len
Documentation and validation for too long IAM role names
2 parents 0eef4f2 + 68439d1 commit 1f08f3b

File tree

8 files changed

+94
-14
lines changed

8 files changed

+94
-14
lines changed

cfnresource/naming.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package cfnresource
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
func ValidateRoleNameLength(clusterName string, nestedStackLogicalName string, managedIAMRoleName string, region string) error {
8+
name := fmt.Sprintf("%s-%s-PRK1CVQNY7XZ-%s-%s", clusterName, nestedStackLogicalName, region, managedIAMRoleName)
9+
if len(name) > 64 {
10+
limit := 64 - len(name) + len(clusterName) + len(nestedStackLogicalName) + len(managedIAMRoleName)
11+
return fmt.Errorf("IAM role name(=%s) will be %d characters long. It exceeds the AWS limit of 64 characters: cluster name(=%s) + nested stack name(=%s) + managed iam role name(=%s) should be less than or equal to %d", name, len(name), clusterName, nestedStackLogicalName, managedIAMRoleName, limit)
12+
}
13+
return nil
14+
}

cfnresource/naming_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package cfnresource
2+
3+
import "testing"
4+
5+
func TestValidateRoleNameLength(t *testing.T) {
6+
t.Run("WhenMax", func(t *testing.T) {
7+
if e := ValidateRoleNameLength("my-firstcluster", "prodWorkerks", "prod-workers", "us-east-1"); e != nil {
8+
t.Errorf("expected validation to succeed but failed: %v", e)
9+
}
10+
})
11+
t.Run("WhenTooLong", func(t *testing.T) {
12+
if e := ValidateRoleNameLength("my-secondcluster", "prodWorkerks", "prod-workers", "us-east-1"); e == nil {
13+
t.Error("expected validation to fail but succeeded")
14+
}
15+
})
16+
}

core/controlplane/config/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"unicode/utf8"
1313

1414
"github.com/coreos/go-semver/semver"
15+
"github.com/coreos/kube-aws/cfnresource"
1516
"github.com/coreos/kube-aws/coreos/amiregistry"
1617
"github.com/coreos/kube-aws/filereader/userdatatemplate"
1718
"github.com/coreos/kube-aws/model"
@@ -846,6 +847,13 @@ func (c Config) InternetGatewayRef() string {
846847
}
847848
}
848849

850+
// NestedStackName returns a sanitized name of this control-plane which is usable as a valid cloudformation nested stack name
851+
func (c Cluster) NestedStackName() string {
852+
// Convert stack name into something valid as a cfn resource name or
853+
// we'll end up with cfn errors like "Template format error: Resource name test5-controlplane is non alphanumeric"
854+
return strings.Title(strings.Replace(c.StackName(), "-", "", -1))
855+
}
856+
849857
func (c Cluster) valid() error {
850858
validClusterNaming := regexp.MustCompile("^[a-zA-Z0-9-:]+$")
851859
if !validClusterNaming.MatchString(c.ClusterName) {
@@ -951,6 +959,10 @@ func (c Cluster) valid() error {
951959
fmt.Println(`WARNING: instance types "t2.nano" and "t2.micro" are not recommended. See https://github.com/coreos/kube-aws/issues/258 for more information`)
952960
}
953961

962+
if e := cfnresource.ValidateRoleNameLength(c.ClusterName, c.NestedStackName(), c.Controller.ManagedIamRoleName, c.Region.String()); e != nil {
963+
return e
964+
}
965+
954966
return nil
955967
}
956968

core/controlplane/config/templates/cluster.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ kmsKeyArn: "{{.KMSKeyARN}}"
6363
# # Role will be created with "Ref": {"AWS::StackName"}-{"AWS::Region"}-yourManagedRole
6464
# # to follow the recommendation in AWS documentation http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
6565
# # This is also intended to be used in combination with .experimental.kube2IamSupport. See #297 for more information.
66+
# #
67+
# # ATTENTION: Consider limiting number of characters in clusterName and managedIamRoleName to avoid the resulting IAM
68+
# # role name's length from exceeding the AWS limit: 64
69+
# # See https://github.com/kubernetes-incubator/kube-aws/issues/347
6670
# managedIamRoleName: "yourManagedRole"
6771
# # If omitted, public subnets are created by kube-aws and used for controller nodes
6872
# subnets:
@@ -143,6 +147,10 @@ worker:
143147
# # Role will be created with "Ref": {"AWS::StackName"}-{"AWS::Region"}-yourManagedRole
144148
# # to follow the recommendation in AWS documentation http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
145149
# # This is also intended to be used in combination with kube2IamSupport. See #297 for more information.
150+
# #
151+
# # ATTENTION: Consider limiting number of characters in clusterName, nodePools[].name and managedIamRoleName to
152+
# # avoid the resulting IAM role name's length from exceeding the AWS limit: 64
153+
# # See https://github.com/kubernetes-incubator/kube-aws/issues/347
146154
# managedIamRoleName: "yourManagedRole"
147155
#
148156
# # Additional EBS volumes mounted on the worker

core/nodepool/config/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99

1010
"errors"
11+
"github.com/coreos/kube-aws/cfnresource"
1112
cfg "github.com/coreos/kube-aws/core/controlplane/config"
1213
"github.com/coreos/kube-aws/coreos/amiregistry"
1314
"github.com/coreos/kube-aws/filereader/userdatatemplate"
@@ -56,6 +57,13 @@ type StackTemplateOptions struct {
5657
SkipWait bool
5758
}
5859

60+
// NestedStackName returns a sanitized name of this node pool which is usable as a valid cloudformation nested stack name
61+
func (c ProvidedConfig) NestedStackName() string {
62+
// Convert stack name into something valid as a cfn resource name or
63+
// we'll end up with cfn errors like "Template format error: Resource name test5-controlplane is non alphanumeric"
64+
return strings.Title(strings.Replace(c.StackName(), "-", "", -1))
65+
}
66+
5967
func (c ProvidedConfig) StackConfig(opts StackTemplateOptions) (*StackConfig, error) {
6068
var err error
6169
stackConfig := StackConfig{}
@@ -261,6 +269,10 @@ func (c ProvidedConfig) valid() error {
261269
return fmt.Errorf("awsNodeLabels can't be enabled for node pool because the total number of characters in clusterName(=\"%s\") + node pool's name(=\"%s\") exceeds the limit of %d", c.ClusterName, c.NodePoolName, limit)
262270
}
263271

272+
if e := cfnresource.ValidateRoleNameLength(c.ClusterName, c.NestedStackName(), c.ManagedIamRoleName, c.Region.String()); e != nil {
273+
return e
274+
}
275+
264276
return nil
265277
}
266278

core/root/template_params.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
controlplane "github.com/coreos/kube-aws/core/controlplane/cluster"
66
nodepool "github.com/coreos/kube-aws/core/nodepool/cluster"
7-
"strings"
87
)
98

109
type TemplateParams struct {
@@ -32,8 +31,7 @@ type controlPlane struct {
3231
}
3332

3433
func (p controlPlane) Name() string {
35-
stackName := p.controlPlane.StackName()
36-
return strings.Title(strings.Replace(stackName, "-", "", -1))
34+
return p.controlPlane.NestedStackName()
3735
}
3836

3937
func (p controlPlane) Tags() map[string]string {
@@ -55,10 +53,7 @@ type nodePool struct {
5553
}
5654

5755
func (p nodePool) Name() string {
58-
// Convert stack name into something valid as a cfn resource name or
59-
// we'll end up with cfn errors like "Template format error: Resource name test5-controlplane is non alphanumeric"
60-
stackName := p.nodePool.StackName()
61-
return strings.Title(strings.Replace(stackName, "-", "", -1))
56+
return p.nodePool.NestedStackName()
6257
}
6358

6459
func (p nodePool) Tags() map[string]string {

test/integration/aws_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,8 @@ func (s kubeAwsSettings) withClusterName(n string) kubeAwsSettings {
9999
s.clusterName = n
100100
return s
101101
}
102+
103+
func (s kubeAwsSettings) withRegion(r string) kubeAwsSettings {
104+
s.region = r
105+
return s
106+
}

test/integration/maincluster_test.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,23 +1110,23 @@ worker:
11101110
context: "WithKube2IamSupport",
11111111
configYaml: minimalValidConfigYaml + `
11121112
controller:
1113-
managedIamRoleName: mycontrollerrole
1113+
managedIamRoleName: myrole1
11141114
experimental:
11151115
kube2IamSupport:
11161116
enabled: true
11171117
worker:
11181118
nodePools:
11191119
- name: pool1
1120-
managedIamRoleName: myworkerrole
1120+
managedIamRoleName: myrole2
11211121
kube2IamSupport:
11221122
enabled: true
11231123
`,
11241124
assertConfig: []ConfigTester{
11251125
hasDefaultEtcdSettings,
11261126
asgBasedNodePoolHasWaitSignalEnabled,
11271127
func(c *config.Config, t *testing.T) {
1128-
expectedControllerRoleName := "mycontrollerrole"
1129-
expectedWorkerRoleName := "myworkerrole"
1128+
expectedControllerRoleName := "myrole1"
1129+
expectedWorkerRoleName := "myrole2"
11301130

11311131
if expectedControllerRoleName != c.Controller.ManagedIamRoleName {
11321132
t.Errorf("controller's managedIamRoleName didn't match : expected=%v actual=%v", expectedControllerRoleName, c.Controller.ManagedIamRoleName)
@@ -2333,14 +2333,14 @@ routeTableId: rtb-1a2b3c4d
23332333
worker:
23342334
nodePools:
23352335
- name: pool1
2336-
managedIamRoleName: "yourManagedRole"
2336+
managedIamRoleName: "myManagedRole"
23372337
`,
23382338
assertConfig: []ConfigTester{
23392339
hasDefaultEtcdSettings,
23402340
hasDefaultExperimentalFeatures,
23412341
func(c *config.Config, t *testing.T) {
2342-
if c.NodePools[0].ManagedIamRoleName != "yourManagedRole" {
2343-
t.Errorf("managedIamRoleName: expected=yourManagedRole actual=%s", c.NodePools[0].ManagedIamRoleName)
2342+
if c.NodePools[0].ManagedIamRoleName != "myManagedRole" {
2343+
t.Errorf("managedIamRoleName: expected=myManagedRole actual=%s", c.NodePools[0].ManagedIamRoleName)
23442344
}
23452345
},
23462346
},
@@ -2818,6 +2818,24 @@ worker:
28182818
`,
28192819
expectedErrorMessage: "unknown keys found in worker.nodePools[0].clusterAutoscaler: baz",
28202820
},
2821+
{
2822+
context: "WithTooLongControllerIAMRoleName",
2823+
configYaml: kubeAwsSettings.withClusterName("kubeaws-it-main").withRegion("ap-northeast-1").minimumValidClusterYaml() + `
2824+
controller:
2825+
managedIamRoleName: foobarba
2826+
`,
2827+
expectedErrorMessage: "IAM role name(=kubeaws-it-main-Controlplane-PRK1CVQNY7XZ-ap-northeast-1-foobarba) will be 65 characters long. It exceeds the AWS limit of 64 characters: cluster name(=kubeaws-it-main) + nested stack name(=Controlplane) + managed iam role name(=foobarba) should be less than or equal to 34",
2828+
},
2829+
{
2830+
context: "WithTooLongWorkerIAMRoleName",
2831+
configYaml: kubeAwsSettings.withClusterName("kubeaws-it-main").withRegion("ap-northeast-1").minimumValidClusterYaml() + `
2832+
worker:
2833+
nodePools:
2834+
- name: pool1
2835+
managedIamRoleName: foobarbazbaraaa
2836+
`,
2837+
expectedErrorMessage: "IAM role name(=kubeaws-it-main-Pool1-PRK1CVQNY7XZ-ap-northeast-1-foobarbazbaraaa) will be 65 characters long. It exceeds the AWS limit of 64 characters: cluster name(=kubeaws-it-main) + nested stack name(=Pool1) + managed iam role name(=foobarbazbaraaa) should be less than or equal to 34",
2838+
},
28212839
}
28222840

28232841
for _, invalidCase := range parseErrorCases {

0 commit comments

Comments
 (0)