-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
compat.go
162 lines (141 loc) · 5.51 KB
/
compat.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package manager
import (
"context"
"fmt"
"github.com/kris-nova/logger"
"github.com/pkg/errors"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
gfnt "github.com/weaveworks/goformation/v4/cloudformation/types"
"github.com/weaveworks/eksctl/pkg/cfn/builder"
"github.com/weaveworks/eksctl/pkg/cfn/outputs"
"github.com/weaveworks/eksctl/pkg/vpc"
)
// FixClusterCompatibility adds any resources missing in the CloudFormation stack in order to support new features
// like Managed Nodegroups and Fargate
func (c *StackCollection) FixClusterCompatibility(ctx context.Context) error {
logger.Info("checking cluster stack for missing resources")
stack, err := c.DescribeClusterStackIfExists(ctx)
if err != nil {
return err
}
if stack == nil {
return &StackNotFoundErr{ClusterName: c.spec.Metadata.Name}
}
var (
clusterDefaultSG string
fargateRole string
)
featureOutputs := map[string]outputs.Collector{
// available on clusters created after Managed Nodes support was out
outputs.ClusterDefaultSecurityGroup: func(v string) error {
clusterDefaultSG = v
return nil
},
// available on 1.14 clusters created after Fargate support was out
outputs.FargatePodExecutionRoleARN: func(v string) error {
fargateRole = v
return nil
},
}
if err := outputs.Collect(*stack, nil, featureOutputs); err != nil {
return err
}
stackSupportsManagedNodes := false
if clusterDefaultSG != "" {
stackSupportsManagedNodes, err = c.hasManagedToUnmanagedSG(ctx)
if err != nil {
return err
}
}
managedNodeUpdateRequired := !stackSupportsManagedNodes && len(c.spec.ManagedNodeGroups) > 0
stackSupportsFargate := fargateRole != ""
fargateUpdateRequired := !stackSupportsFargate && len(c.spec.FargateProfiles) > 0
if !managedNodeUpdateRequired && !fargateUpdateRequired {
logger.Info("cluster stack has all required resources")
return nil
}
if managedNodeUpdateRequired {
logger.Info("cluster stack is missing resources for Managed Nodegroups")
}
if fargateUpdateRequired {
logger.Info("cluster stack is missing resources for Fargate")
}
logger.Info("adding missing resources to cluster stack")
_, err = c.AppendNewClusterStackResource(ctx, false, false)
return err
}
func (c *StackCollection) hasManagedToUnmanagedSG(ctx context.Context) (bool, error) {
stackTemplate, err := c.GetStackTemplate(ctx, c.MakeClusterStackName())
if err != nil {
return false, errors.Wrap(err, "error getting cluster stack template")
}
stackResources := gjson.Get(stackTemplate, resourcesRootPath)
return builder.HasManagedNodesSG(&stackResources), nil
}
// EnsureMapPublicIPOnLaunchEnabled sets this subnet property to true when it is not set or is set to false
func (c *StackCollection) EnsureMapPublicIPOnLaunchEnabled(ctx context.Context) error {
// First, make sure we enable the options in EC2. This is to make sure the settings are applied even
// if the stacks in Cloudformation have the setting enabled (since a stack update would produce "nothing to change"
// and therefore the setting would not be updated)
publicIDs := c.spec.VPC.Subnets.Public.WithIDs()
logger.Debug("enabling attribute MapPublicIpOnLaunch via EC2 on subnets %q", publicIDs)
err := vpc.EnsureMapPublicIPOnLaunchEnabled(ctx, c.ec2API, publicIDs)
if err != nil {
return err
}
// Get stack template
stackName := c.MakeClusterStackName()
currentTemplate, err := c.GetStackTemplate(ctx, stackName)
if err != nil {
return errors.Wrapf(err, "unable to retrieve cluster stack %q", stackName)
}
// Find subnets in stack
outputTemplate := gjson.Get(currentTemplate, outputsRootPath)
publicSubnetsNames, err := getPublicSubnetResourceNames(outputTemplate.Raw)
if err != nil {
// Subnets do not appear in the stack because the VPC was imported
logger.Debug(err.Error())
return nil
}
// Modify the subnets' properties in the stack
logger.Debug("ensuring subnets have MapPublicIpOnLaunch enabled")
for _, subnet := range publicSubnetsNames {
path := subnetResourcePath(subnet)
currentValue := gjson.Get(currentTemplate, path)
if !currentValue.Exists() || !currentValue.Bool() {
currentTemplate, err = sjson.Set(currentTemplate, path, gfnt.True())
if err != nil {
return errors.Wrapf(err, "unable to set MapPublicIpOnLaunch property on subnet %q", path)
}
}
}
description := fmt.Sprintf("update public subnets %q with property MapPublicIpOnLaunch enabled", publicSubnetsNames)
if err := c.UpdateStack(ctx, UpdateStackOptions{
StackName: stackName,
ChangeSetName: c.MakeChangeSetName("update-subnets"),
Description: description,
TemplateData: TemplateBody(currentTemplate),
Wait: true,
}); err != nil {
return errors.Wrap(err, "unable to update subnets")
}
return nil
}
func subnetResourcePath(subnetName string) string {
return fmt.Sprintf("Resources.%s.Properties.MapPublicIpOnLaunch", subnetName)
}
// getPublicSubnetResourceNames returns the stack resource names for the public subnets, gotten from the stack
// output "SubnetsPublic"
func getPublicSubnetResourceNames(outputsTemplate string) ([]string, error) {
publicSubnets := gjson.Get(outputsTemplate, "SubnetsPublic.Value.Fn::Join.1.#.Ref")
if !publicSubnets.Exists() || len(publicSubnets.Array()) == 0 {
subnetsJSON := gjson.Get(outputsTemplate, "SubnetsPublic.Value")
return nil, fmt.Errorf("resource name for public subnets not found. Found %q", subnetsJSON.Raw)
}
subnetStackNames := make([]string, 0)
for _, subnet := range publicSubnets.Array() {
subnetStackNames = append(subnetStackNames, subnet.String())
}
return subnetStackNames, nil
}