Skip to content
This repository has been archived by the owner on Jun 15, 2021. It is now read-only.

Commit

Permalink
feat: add CF stackNameOverrides in cluster.yaml (kubernetes-retired#1484
Browse files Browse the repository at this point in the history
)

* feat: add CF stackNameOverride in cluster.yaml

Add option in cluster.yaml to override the name of CF controlPlane,
network and etcd stacks.

This is useful for migration of CF stacks from kube-aws <0.11.x without
creating a new VPC.

* wrap stackNameOverrides into cloudformation config

Because it is part of the cloudformation config in cluster.yaml.
  • Loading branch information
koen92 authored and Kevin Taylor committed Jan 9, 2019
1 parent 21c8401 commit 5be1d5e
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 11 deletions.
6 changes: 3 additions & 3 deletions core/controlplane/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func NewCluster(cfgRef *config.Cluster, opts config.StackTemplateOptions, plugin
clusterRef := newClusterRef(cfg, session)
// TODO Do this in a cleaner way e.g. in config.go
clusterRef.KubeResourcesAutosave.S3Path = model.NewS3Folders(cfg.DeploymentSettings.S3URI, clusterRef.ClusterName).ClusterBackups().Path()
stackConfig, err := clusterRef.StackConfig(config.ControlPlaneStackName, opts, session, plugins)
stackConfig, err := clusterRef.StackConfig(clusterRef.ControlPlaneStackName(), opts, session, plugins)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -279,15 +279,15 @@ func (c *Cluster) Validate() error {

// NestedStackName returns a sanitized name of this control-plane which is usable as a valid cloudformation nested stack name
func (c Cluster) NestedStackName() string {
return naming.FromStackToCfnResource(config.ControlPlaneStackName)
return naming.FromStackToCfnResource(c.ControlPlaneStackName())
}

func (c *Cluster) String() string {
return fmt.Sprintf("{Config:%+v}", *c.StackConfig.Config)
}

func (c *ClusterRef) Destroy() error {
return cfnstack.NewDestroyer(config.ControlPlaneStackName, c.session, c.CloudFormation.RoleARN).Destroy()
return cfnstack.NewDestroyer(c.Cluster.ControlPlaneStackName(), c.session, c.CloudFormation.RoleARN).Destroy()
}

func (c *ClusterRef) validateKeyPair(ec2Svc ec2Service) error {
Expand Down
33 changes: 33 additions & 0 deletions core/controlplane/cluster/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,39 @@ func (svc dummyEC2Service) DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (*
return output, nil
}

func TestStackNameDefaults(t *testing.T) {
configBody := defaultConfigValues(t, "")
clusterConfig, err := config.ClusterFromBytes([]byte(configBody))
if err != nil {
t.Errorf("could not get valid cluster config in `%s`: %v", configBody, err)
}
c := &ClusterRef{Cluster: clusterConfig}

assert.Equal(t, "control-plane", c.ControlPlaneStackName(), "Invalid ControlPlane Stackname, should be set to 'control-plane' if no override is provided.")
assert.Equal(t, "network", c.NetworkStackName(), "Invalid Network Stackname, should be set to 'network' if no override is provided.")
assert.Equal(t, "etcd", c.EtcdStackName(), "Invalid Etcd Stackname, should be set to 'etcd' if no override is provided.")
}

func TestStackNameOverrides(t *testing.T) {
stackNameOverrideConfig := `
cloudformation:
stackNameOverrides:
controlPlane: "control-plane-override"
network: "network-override"
etcd: "etcd-override"
`
configBody := defaultConfigValues(t, stackNameOverrideConfig)
clusterConfig, err := config.ClusterFromBytes([]byte(configBody))
if err != nil {
t.Errorf("could not get valid cluster config in `%s`: %v", configBody, err)
}
c := &ClusterRef{Cluster: clusterConfig}

assert.Equal(t, "control-plane-override", c.ControlPlaneStackName(), "Invalid ControlPlane Stackname, should be overridden with 'control-plane-override'.")
assert.Equal(t, "network-override", c.NetworkStackName(), "Invalid Network Stackname, should overridden to 'network-override'.")
assert.Equal(t, "etcd-override", c.EtcdStackName(), "Invalid Etcd Stackname, should be overridden with 'etcd-override'.")
}

func TestExistingVPCValidation(t *testing.T) {

goodExistingVPCConfigs := []string{
Expand Down
30 changes: 27 additions & 3 deletions core/controlplane/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ const (
// This is not needed to be unique in an AWS account because the actual name of a nested stack is generated randomly
// by CloudFormation by including the logical name.
// This is NOT intended to be used to reference stack name from cloud-config as the target of awscli or cfn-bootstrap-tools commands e.g. `cfn-init` and `cfn-signal`
ControlPlaneStackName = "control-plane"
// These names can be overridden in cluster.yaml
controlPlaneStackName = "control-plane"
networkStackName = "network"
etcdStackName = "etcd"
)

func NewDefaultCluster() *Cluster {
Expand Down Expand Up @@ -1097,6 +1100,27 @@ type Config struct {
ControllerFlags pluginmodel.ControllerFlags
}

func (c Cluster) ControlPlaneStackName() string {
if c.CloudFormation.StackNameOverrides.ControlPlane != "" {
return c.CloudFormation.StackNameOverrides.ControlPlane
}
return controlPlaneStackName
}

func (c Cluster) NetworkStackName() string {
if c.CloudFormation.StackNameOverrides.Network != "" {
return c.CloudFormation.StackNameOverrides.Network
}
return networkStackName
}

func (c Cluster) EtcdStackName() string {
if c.CloudFormation.StackNameOverrides.Etcd != "" {
return c.CloudFormation.StackNameOverrides.Etcd
}
return etcdStackName
}

func (c Cluster) StackNameEnvFileName() string {
return "/etc/environment"
}
Expand Down Expand Up @@ -1277,7 +1301,7 @@ func (c Cluster) validate() error {

clusterNamePlaceholder := "<my-cluster-name>"
nestedStackNamePlaceHolder := "<my-nested-stack-name>"
replacer := strings.NewReplacer(clusterNamePlaceholder, "", nestedStackNamePlaceHolder, ControlPlaneStackName)
replacer := strings.NewReplacer(clusterNamePlaceholder, "", nestedStackNamePlaceHolder, c.ControlPlaneStackName())
simulatedLcName := fmt.Sprintf("%s-%s-1N2C4K3LLBEDZ-%sLC-BC2S9P3JG2QD", clusterNamePlaceholder, nestedStackNamePlaceHolder, c.Controller.LogicalName())
limit := 63 - len(replacer.Replace(simulatedLcName))
if c.Experimental.AwsNodeLabels.Enabled && len(c.ClusterName) > limit {
Expand All @@ -1293,7 +1317,7 @@ func (c Cluster) validate() error {
return e
}
} else {
if e := cfnresource.ValidateUnstableRoleNameLength(c.ClusterName, naming.FromStackToCfnResource(ControlPlaneStackName), c.Controller.IAMConfig.Role.Name, c.Region.String()); e != nil {
if e := cfnresource.ValidateUnstableRoleNameLength(c.ClusterName, naming.FromStackToCfnResource(c.ControlPlaneStackName()), c.Controller.IAMConfig.Role.Name, c.Region.String()); e != nil {
return e
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/etcd/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func NewCluster(cfgRef *controlplaneconfig.Cluster, opts controlplaneconfig.Stac
clusterRef.KubeAWSVersion = controlplanecluster.VERSION
clusterRef.HostOS = cfgRef.HostOS

cpStackConfig, err := clusterRef.StackConfig("etcd", opts, session, plugins)
cpStackConfig, err := clusterRef.StackConfig(cfgRef.EtcdStackName(), opts, session, plugins)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -309,7 +309,7 @@ func (c *Cluster) String() string {
}

func (c *ClusterRef) Destroy() error {
return cfnstack.NewDestroyer("etcd", c.session, c.CloudFormation.RoleARN).Destroy()
return cfnstack.NewDestroyer(c.EtcdStackName(), c.session, c.CloudFormation.RoleARN).Destroy()
}

// lookupExistingEtcdEndpoints supports the migration from embedded etcd servers to their own stack
Expand Down
4 changes: 2 additions & 2 deletions core/network/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func NewCluster(cfgRef *config.Cluster, opts config.StackTemplateOptions, plugin
// TODO Do this in a cleaner way e.g. in config.go
clusterRef.KubeResourcesAutosave.S3Path = model.NewS3Folders(cfg.DeploymentSettings.S3URI, clusterRef.ClusterName).ClusterBackups().Path()

stackConfig, err := clusterRef.StackConfig("network", opts, session, plugins)
stackConfig, err := clusterRef.StackConfig(cfgRef.NetworkStackName(), opts, session, plugins)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -215,5 +215,5 @@ func (c *Cluster) String() string {
}

func (c *ClusterRef) Destroy() error {
return cfnstack.NewDestroyer("network", c.session, c.CloudFormation.RoleARN).Destroy()
return cfnstack.NewDestroyer(c.NetworkStackName(), c.session, c.CloudFormation.RoleARN).Destroy()
}
16 changes: 16 additions & 0 deletions core/root/config/templates/cluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ amiId: "{{.AmiId}}"
# Container Linux has automatic updates https://coreos.com/os/docs/latest/update-strategies.html. This can be a risk in certain situations and this is why is disabled by default and you can enable it by setting this param to false.
disableContainerLinuxAutomaticUpdates: true

# Override the CloudFormation logical sub-stack names of control plane, etcd and/or network.
# Changing these names causes CF to delete the old and create a new stack and thus deleting and recreating all components in the stack.
#
# From kube-aws 0.11.x and onwards, separate networking and etcd CF stacks have been introduced.
# Because CF does not support migration of resources between stacks (or renaming stacks), a new VPC will be created if you upgrade an existing 0.10.x cluster.
# Creating a new VPC and deleting the old one can cause a lot of issues and might not be desired.
# To make this migration more feasible it is posible to change the name of the network stack to the old controleplane stack to keep the vpc components in the same stack. This way CF will not create a new vpc.
# In this scenario the name of the networkstack becomes "ControlPlane". (which is the name of the stack which holds the vpc components in a kube-aws pre 0.11 cluster)
# For clearity it is advised also rename the stack of the controlPlane components to, for example, "masters"
# Note that this will keep some legacy naming around in your configs and CF stacks...
#cloudformation:
# stackNameOverrides:
# controlPlane: "control-plane"
# network: "network"
# etcd: "etcd"

# The ID of hosted zone to add the externalDNSName to.
# Either specify hostedZoneId or hostedZone, but not both
#hostedZoneId: ""
Expand Down
3 changes: 2 additions & 1 deletion model/cloudformation.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package model

type CloudFormation struct {
RoleARN string `yaml:"roleARN,omitempty"`
RoleARN string `yaml:"roleARN,omitempty"`
StackNameOverrides StackNameOverrides `yaml:"stackNameOverrides,omitempty"`
}
7 changes: 7 additions & 0 deletions model/stack_name_overrides.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package model

type StackNameOverrides struct {
ControlPlane string `yaml:"controlPlane,omitempty"`
Network string `yaml:"network,omitempty"`
Etcd string `yaml:"etcd,omitempty"`
}

0 comments on commit 5be1d5e

Please sign in to comment.