mirror of https://github.com/kubernetes/kops.git
Use LaunchTemplate versions instead of timestamped LaunchTemplates
This commit is contained in:
parent
ecea47790b
commit
565adceab9
|
|
@ -18,6 +18,7 @@ package mockautoscaling
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc64"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
|
|
@ -30,7 +31,7 @@ func (m *MockAutoscaling) AttachInstances(input *autoscaling.AttachInstancesInpu
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.V(2).Infof("AttachInstances %v", input)
|
||||
klog.V(2).Infof("Mock AttachInstances %v", input)
|
||||
|
||||
g := m.Groups[aws.StringValue(input.AutoScalingGroupName)]
|
||||
if g == nil {
|
||||
|
|
@ -48,7 +49,8 @@ func (m *MockAutoscaling) CreateAutoScalingGroup(input *autoscaling.CreateAutoSc
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.V(2).Infof("CreateAutoScalingGroup %v", input)
|
||||
klog.V(2).Infof("Mock CreateAutoScalingGroup %v", input)
|
||||
|
||||
createdTime := time.Now().UTC()
|
||||
|
||||
g := &autoscaling.Group{
|
||||
|
|
@ -75,6 +77,11 @@ func (m *MockAutoscaling) CreateAutoScalingGroup(input *autoscaling.CreateAutoSc
|
|||
VPCZoneIdentifier: input.VPCZoneIdentifier,
|
||||
}
|
||||
|
||||
if input.LaunchTemplate != nil {
|
||||
launchTemplateCrc := crc64.Checksum([]byte(aws.StringValue(input.LaunchTemplate.LaunchTemplateName)), crc64.MakeTable(crc64.ECMA))
|
||||
g.LaunchTemplate.LaunchTemplateId = aws.String(fmt.Sprintf("lt-%x", launchTemplateCrc))
|
||||
}
|
||||
|
||||
for _, tag := range input.Tags {
|
||||
g.Tags = append(g.Tags, &autoscaling.TagDescription{
|
||||
Key: tag.Key,
|
||||
|
|
@ -97,7 +104,7 @@ func (m *MockAutoscaling) EnableMetricsCollection(request *autoscaling.EnableMet
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.Infof("EnableMetricsCollection: %v", request)
|
||||
klog.V(2).Infof("Mock EnableMetricsCollection: %v", request)
|
||||
|
||||
g := m.Groups[*request.AutoScalingGroupName]
|
||||
if g == nil {
|
||||
|
|
@ -129,7 +136,7 @@ func (m *MockAutoscaling) SuspendProcesses(input *autoscaling.ScalingProcessQuer
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.Infof("EnableMetricsCollection: %v", input)
|
||||
klog.V(2).Infof("Mock SuspendProcesses: %v", input)
|
||||
|
||||
g := m.Groups[*input.AutoScalingGroupName]
|
||||
if g == nil {
|
||||
|
|
@ -157,6 +164,8 @@ func (m *MockAutoscaling) DescribeAutoScalingGroups(input *autoscaling.DescribeA
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.V(2).Infof("Mock DescribeAutoScalingGroups: %v", input)
|
||||
|
||||
groups := []*autoscaling.Group{}
|
||||
for _, group := range m.Groups {
|
||||
match := false
|
||||
|
|
@ -236,7 +245,7 @@ func (m *MockAutoscaling) DeleteAutoScalingGroup(request *autoscaling.DeleteAuto
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.Infof("DeleteAutoScalingGroup: %v", request)
|
||||
klog.V(2).Infof("Mock DeleteAutoScalingGroup: %v", request)
|
||||
|
||||
id := aws.StringValue(request.AutoScalingGroupName)
|
||||
o := m.Groups[id]
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ type MockEC2 struct {
|
|||
|
||||
InternetGateways map[string]*ec2.InternetGateway
|
||||
|
||||
launchTemplateNumber int
|
||||
LaunchTemplates map[string]*launchTemplateInfo
|
||||
LaunchTemplates map[string]*launchTemplateInfo
|
||||
|
||||
NatGateways map[string]*ec2.NatGateway
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ package mockec2
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc64"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type launchTemplateInfo struct {
|
||||
|
|
@ -33,15 +35,18 @@ func (m *MockEC2) DescribeLaunchTemplates(request *ec2.DescribeLaunchTemplatesIn
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.V(2).Infof("Mock DescribeLaunchTemplates: %v", request)
|
||||
|
||||
o := &ec2.DescribeLaunchTemplatesOutput{}
|
||||
|
||||
if m.LaunchTemplates == nil {
|
||||
return o, nil
|
||||
}
|
||||
|
||||
for _, ltInfo := range m.LaunchTemplates {
|
||||
for id, ltInfo := range m.LaunchTemplates {
|
||||
o.LaunchTemplates = append(o.LaunchTemplates, &ec2.LaunchTemplate{
|
||||
LaunchTemplateName: ltInfo.name,
|
||||
LaunchTemplateId: aws.String(id),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +58,8 @@ func (m *MockEC2) DescribeLaunchTemplateVersions(request *ec2.DescribeLaunchTemp
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.V(2).Infof("Mock DescribeLaunchTemplateVersions: %v", request)
|
||||
|
||||
o := &ec2.DescribeLaunchTemplateVersionsOutput{}
|
||||
|
||||
if m.LaunchTemplates == nil {
|
||||
|
|
@ -78,15 +85,16 @@ func (m *MockEC2) CreateLaunchTemplate(request *ec2.CreateLaunchTemplateInput) (
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
m.launchTemplateNumber++
|
||||
n := m.launchTemplateNumber
|
||||
id := fmt.Sprintf("lt-%d", n)
|
||||
klog.V(2).Infof("Mock CreateLaunchTemplate: %v", request)
|
||||
|
||||
crc := crc64.Checksum([]byte(aws.StringValue(request.LaunchTemplateName)), crc64.MakeTable(crc64.ECMA))
|
||||
id := fmt.Sprintf("lt-%x", crc)
|
||||
|
||||
if m.LaunchTemplates == nil {
|
||||
m.LaunchTemplates = make(map[string]*launchTemplateInfo)
|
||||
}
|
||||
if m.LaunchTemplates[id] != nil {
|
||||
return nil, fmt.Errorf("duplicate LaunchTemplateName %s", id)
|
||||
return nil, fmt.Errorf("duplicate LaunchTemplateId %s", id)
|
||||
}
|
||||
resp := &ec2.ResponseLaunchTemplateData{
|
||||
DisableApiTermination: request.LaunchTemplateData.DisableApiTermination,
|
||||
|
|
@ -190,13 +198,15 @@ func (m *MockEC2) DeleteLaunchTemplate(request *ec2.DeleteLaunchTemplateInput) (
|
|||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
klog.V(2).Infof("Mock DeleteLaunchTemplate: %v", request)
|
||||
|
||||
o := &ec2.DeleteLaunchTemplateOutput{}
|
||||
|
||||
if m.LaunchTemplates == nil {
|
||||
return o, nil
|
||||
}
|
||||
for id, lt := range m.LaunchTemplates {
|
||||
if aws.StringValue(lt.name) == aws.StringValue(request.LaunchTemplateName) {
|
||||
for id := range m.LaunchTemplates {
|
||||
if id == aws.StringValue(request.LaunchTemplateId) {
|
||||
delete(m.LaunchTemplates, id)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ func ListResourcesAWS(cloud awsup.AWSCloud, clusterName string) (map[string]*res
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lts, err := FindAutoScalingLaunchTemplateConfigurations(cloud, securityGroups)
|
||||
lts, err := FindAutoScalingLaunchTemplates(cloud, clusterName, securityGroups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1179,47 +1179,34 @@ func ListAutoScalingGroups(cloud fi.Cloud, clusterName string) ([]*resources.Res
|
|||
return resourceTrackers, nil
|
||||
}
|
||||
|
||||
// FindAutoScalingLaunchTemplateConfigurations finds any launch configurations which reference the security groups
|
||||
func FindAutoScalingLaunchTemplateConfigurations(cloud fi.Cloud, securityGroups sets.String) ([]*resources.Resource, error) {
|
||||
var list []*resources.Resource
|
||||
|
||||
// FindAutoScalingLaunchTemplates finds any launch configurations which reference the security groups
|
||||
func FindAutoScalingLaunchTemplates(cloud fi.Cloud, clusterName string, securityGroups sets.String) ([]*resources.Resource, error) {
|
||||
c, ok := cloud.(awsup.AWSCloud)
|
||||
if !ok {
|
||||
return nil, errors.New("expected a aws cloud provider")
|
||||
}
|
||||
klog.V(2).Infof("Finding all Autoscaling LaunchTemplates associated to security groups")
|
||||
|
||||
resp, err := c.EC2().DescribeLaunchTemplates(&ec2.DescribeLaunchTemplatesInput{MaxResults: fi.Int64(100)})
|
||||
output, err := c.EC2().DescribeLaunchTemplates(&ec2.DescribeLaunchTemplatesInput{
|
||||
Filters: []*ec2.Filter{
|
||||
{
|
||||
Name: aws.String("tag:KubernetesCluster"),
|
||||
Values: []*string{aws.String(clusterName)},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return list, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, x := range resp.LaunchTemplates {
|
||||
// @step: grab the actual launch template
|
||||
req, err := c.EC2().DescribeLaunchTemplateVersions(&ec2.DescribeLaunchTemplateVersionsInput{
|
||||
LaunchTemplateName: x.LaunchTemplateName,
|
||||
var list []*resources.Resource
|
||||
for _, lt := range output.LaunchTemplates {
|
||||
list = append(list, &resources.Resource{
|
||||
Name: aws.StringValue(lt.LaunchTemplateName),
|
||||
ID: aws.StringValue(lt.LaunchTemplateId),
|
||||
Type: TypeAutoscalingLaunchConfig,
|
||||
Deleter: DeleteAutoScalingGroupLaunchTemplate,
|
||||
})
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
for _, j := range req.LaunchTemplateVersions {
|
||||
// @check if the security group references the security group above
|
||||
var s []*string
|
||||
for _, ni := range j.LaunchTemplateData.NetworkInterfaces {
|
||||
s = append(s, ni.Groups...)
|
||||
}
|
||||
s = append(s, j.LaunchTemplateData.SecurityGroupIds...)
|
||||
for _, y := range s {
|
||||
if securityGroups.Has(fi.StringValue(y)) {
|
||||
list = append(list, &resources.Resource{
|
||||
Name: aws.StringValue(x.LaunchTemplateName),
|
||||
ID: aws.StringValue(x.LaunchTemplateName),
|
||||
Type: TypeAutoscalingLaunchConfig,
|
||||
Deleter: DeleteAutoScalingGroupLaunchTemplate,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list, nil
|
||||
|
|
@ -1377,7 +1364,7 @@ func DeleteAutoScalingGroupLaunchTemplate(cloud fi.Cloud, r *resources.Resource)
|
|||
klog.V(2).Infof("Deleting EC2 LaunchTemplate %q", r.ID)
|
||||
|
||||
if _, err := c.EC2().DeleteLaunchTemplate(&ec2.DeleteLaunchTemplateInput{
|
||||
LaunchTemplateName: fi.String(r.ID),
|
||||
LaunchTemplateId: fi.String(r.ID),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error deleting ec2 LaunchTemplate %q: %v", r.ID, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,19 +20,16 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"k8s.io/kops/util/pkg/maps"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// CloudTagInstanceGroupRolePrefix is a cloud tag that defines the instance role
|
||||
|
|
@ -202,7 +199,10 @@ func (e *AutoscalingGroup) Find(c *fi.Context) (*AutoscalingGroup, error) {
|
|||
actual.LaunchConfiguration = &LaunchConfiguration{ID: g.LaunchConfigurationName}
|
||||
}
|
||||
if g.LaunchTemplate != nil {
|
||||
actual.LaunchTemplate = &LaunchTemplate{ID: g.LaunchTemplate.LaunchTemplateName}
|
||||
actual.LaunchTemplate = &LaunchTemplate{
|
||||
Name: g.LaunchTemplate.LaunchTemplateName,
|
||||
ID: g.LaunchTemplate.LaunchTemplateId,
|
||||
}
|
||||
}
|
||||
|
||||
if g.MixedInstancesPolicy != nil {
|
||||
|
|
@ -219,7 +219,10 @@ func (e *AutoscalingGroup) Find(c *fi.Context) (*AutoscalingGroup, error) {
|
|||
|
||||
if g.MixedInstancesPolicy.LaunchTemplate != nil {
|
||||
if g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification != nil {
|
||||
actual.LaunchTemplate = &LaunchTemplate{ID: g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.LaunchTemplateName}
|
||||
actual.LaunchTemplate = &LaunchTemplate{
|
||||
Name: g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.LaunchTemplateName,
|
||||
ID: g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.LaunchTemplateId,
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range g.MixedInstancesPolicy.LaunchTemplate.Overrides {
|
||||
|
|
@ -363,8 +366,8 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos
|
|||
},
|
||||
LaunchTemplate: &autoscaling.LaunchTemplate{
|
||||
LaunchTemplateSpecification: &autoscaling.LaunchTemplateSpecification{
|
||||
LaunchTemplateName: e.LaunchTemplate.ID,
|
||||
Version: aws.String("1"),
|
||||
LaunchTemplateName: e.LaunchTemplate.Name,
|
||||
Version: aws.String("$Latest"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -376,11 +379,11 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos
|
|||
}
|
||||
} else if e.LaunchTemplate != nil {
|
||||
request.LaunchTemplate = &autoscaling.LaunchTemplateSpecification{
|
||||
LaunchTemplateName: e.LaunchTemplate.ID,
|
||||
Version: aws.String("1"),
|
||||
LaunchTemplateName: e.LaunchTemplate.Name,
|
||||
Version: aws.String("$Latest"),
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("could not find one of launch configuration, mixed instances policy, or launch template")
|
||||
return fmt.Errorf("could not find one of launch template, mixed instances policy, or launch configuration")
|
||||
}
|
||||
|
||||
// @step: attempt to create the autoscaling group for us
|
||||
|
|
@ -441,23 +444,10 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos
|
|||
return req.MixedInstancesPolicy
|
||||
}
|
||||
|
||||
launchTemplateVersion := "1"
|
||||
if e.LaunchTemplate != nil {
|
||||
dltRequest := &ec2.DescribeLaunchTemplatesInput{
|
||||
LaunchTemplateNames: []*string{e.LaunchTemplate.ID},
|
||||
}
|
||||
dltResponse, err := t.Cloud.EC2().DescribeLaunchTemplates(dltRequest)
|
||||
if err != nil {
|
||||
klog.Warningf("could not find existing LaunchTemplate: %v", err)
|
||||
} else {
|
||||
launchTemplateVersion = strconv.FormatInt(*dltResponse.LaunchTemplates[0].LatestVersionNumber, 10)
|
||||
}
|
||||
}
|
||||
|
||||
if changes.LaunchTemplate != nil {
|
||||
spec := &autoscaling.LaunchTemplateSpecification{
|
||||
LaunchTemplateName: changes.LaunchTemplate.ID,
|
||||
Version: &launchTemplateVersion,
|
||||
LaunchTemplateName: changes.LaunchTemplate.Name,
|
||||
Version: aws.String("$Latest"),
|
||||
}
|
||||
if e.UseMixedInstancesPolicy() {
|
||||
setup(request).LaunchTemplate = &autoscaling.LaunchTemplate{LaunchTemplateSpecification: spec}
|
||||
|
|
@ -491,8 +481,8 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos
|
|||
if setup(request).LaunchTemplate == nil {
|
||||
setup(request).LaunchTemplate = &autoscaling.LaunchTemplate{
|
||||
LaunchTemplateSpecification: &autoscaling.LaunchTemplateSpecification{
|
||||
LaunchTemplateName: e.LaunchTemplate.ID,
|
||||
Version: &launchTemplateVersion,
|
||||
LaunchTemplateName: changes.LaunchTemplate.Name,
|
||||
Version: aws.String("$Latest"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,18 @@ package awstasks
|
|||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
)
|
||||
|
||||
// LaunchTemplate defines the specification for a launch template.
|
||||
// +kops:fitask
|
||||
type LaunchTemplate struct {
|
||||
// ID is the launch configuration name
|
||||
ID *string
|
||||
// Name is the name of the configuration
|
||||
Name *string
|
||||
// Lifecycle is the resource lifecycle
|
||||
|
|
@ -41,10 +42,11 @@ type LaunchTemplate struct {
|
|||
BlockDeviceMappings []*BlockDeviceMapping
|
||||
// IAMInstanceProfile is the IAM profile to assign to the nodes
|
||||
IAMInstanceProfile *IAMInstanceProfile
|
||||
// ID is the launch configuration name
|
||||
ID *string
|
||||
// ImageID is the AMI to use for the instances
|
||||
ImageID *string
|
||||
// InstanceInterruptionBehavior defines if a spot instance should be terminated, hibernated,
|
||||
// or stopped after interruption
|
||||
InstanceInterruptionBehavior *string
|
||||
// InstanceMonitoring indicates if monitoring is enabled
|
||||
InstanceMonitoring *bool
|
||||
// InstanceType is the type of instance we are using
|
||||
|
|
@ -73,9 +75,6 @@ type LaunchTemplate struct {
|
|||
Tenancy *string
|
||||
// UserData is the user data configuration
|
||||
UserData *fi.ResourceHolder
|
||||
// InstanceInterruptionBehavior defines if a spot instance should be terminated, hibernated,
|
||||
// or stopped after interruption
|
||||
InstanceInterruptionBehavior *string
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -89,11 +88,6 @@ func (t *LaunchTemplate) CompareWithID() *string {
|
|||
return t.ID
|
||||
}
|
||||
|
||||
// LaunchTemplateName returns the lanuch template name
|
||||
func (t *LaunchTemplate) LaunchTemplateName() string {
|
||||
return fmt.Sprintf("%s-%s", fi.StringValue(t.Name), fi.BuildTimestampString())
|
||||
}
|
||||
|
||||
// buildRootDevice is responsible for retrieving a boot device mapping from the image name
|
||||
func (t *LaunchTemplate) buildRootDevice(cloud awsup.AWSCloud) (map[string]*BlockDeviceMapping, error) {
|
||||
image := fi.StringValue(t.ImageID)
|
||||
|
|
@ -154,22 +148,17 @@ func (t *LaunchTemplate) CheckChanges(a, e, changes *LaunchTemplate) error {
|
|||
func (t *LaunchTemplate) FindDeletions(c *fi.Context) ([]fi.Deletion, error) {
|
||||
var removals []fi.Deletion
|
||||
|
||||
configurations, err := t.findLaunchTemplates(c)
|
||||
configurations, err := t.findAllLaunchTemplates(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(configurations) <= RetainLaunchConfigurationCount() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
configurations = configurations[:len(configurations)-RetainLaunchConfigurationCount()]
|
||||
|
||||
prefix := fmt.Sprintf("%s-", fi.StringValue(t.Name))
|
||||
for _, configuration := range configurations {
|
||||
removals = append(removals, &deleteLaunchTemplate{lc: configuration})
|
||||
if strings.HasPrefix(aws.StringValue(configuration.LaunchTemplateName), prefix) {
|
||||
removals = append(removals, &deleteLaunchTemplate{lc: configuration})
|
||||
}
|
||||
}
|
||||
|
||||
klog.V(2).Infof("will delete launch template: %v", removals)
|
||||
|
||||
return removals, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,44 +20,36 @@ import (
|
|||
"encoding/base64"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
)
|
||||
|
||||
// RenderAWS is responsible for performing creating / updating the launch template
|
||||
func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *LaunchTemplate) error {
|
||||
name := t.LaunchTemplateName()
|
||||
|
||||
func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchTemplate) error {
|
||||
// @step: resolve the image id to an AMI for us
|
||||
image, err := c.Cloud.ResolveImage(fi.StringValue(t.ImageID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// @step: lets build the launch template input
|
||||
input := &ec2.CreateLaunchTemplateInput{
|
||||
LaunchTemplateData: &ec2.RequestLaunchTemplateData{
|
||||
DisableApiTermination: fi.Bool(false),
|
||||
EbsOptimized: t.RootVolumeOptimization,
|
||||
ImageId: image.ImageId,
|
||||
InstanceType: t.InstanceType,
|
||||
NetworkInterfaces: []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
|
||||
{
|
||||
AssociatePublicIpAddress: t.AssociatePublicIP,
|
||||
DeleteOnTermination: aws.Bool(true),
|
||||
DeviceIndex: fi.Int64(0),
|
||||
},
|
||||
// @step: lets build the launch template data
|
||||
data := &ec2.RequestLaunchTemplateData{
|
||||
DisableApiTermination: fi.Bool(false),
|
||||
EbsOptimized: t.RootVolumeOptimization,
|
||||
ImageId: image.ImageId,
|
||||
InstanceType: t.InstanceType,
|
||||
NetworkInterfaces: []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
|
||||
{
|
||||
AssociatePublicIpAddress: t.AssociatePublicIP,
|
||||
DeleteOnTermination: aws.Bool(true),
|
||||
DeviceIndex: fi.Int64(0),
|
||||
},
|
||||
},
|
||||
LaunchTemplateName: aws.String(name),
|
||||
}
|
||||
lc := input.LaunchTemplateData
|
||||
|
||||
// @step: add the actual block device mappings
|
||||
rootDevices, err := t.buildRootDevice(c.Cloud)
|
||||
|
|
@ -74,54 +66,50 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *Launch
|
|||
}
|
||||
for _, x := range []map[string]*BlockDeviceMapping{rootDevices, ephemeralDevices, additionalDevices} {
|
||||
for name, device := range x {
|
||||
input.LaunchTemplateData.BlockDeviceMappings = append(input.LaunchTemplateData.BlockDeviceMappings, device.ToLaunchTemplateBootDeviceRequest(name))
|
||||
data.BlockDeviceMappings = append(data.BlockDeviceMappings, device.ToLaunchTemplateBootDeviceRequest(name))
|
||||
}
|
||||
}
|
||||
|
||||
// @step: add the ssh key
|
||||
if t.SSHKey != nil {
|
||||
lc.KeyName = t.SSHKey.Name
|
||||
data.KeyName = t.SSHKey.Name
|
||||
}
|
||||
// @step: add the security groups
|
||||
for _, sg := range t.SecurityGroups {
|
||||
lc.NetworkInterfaces[0].Groups = append(lc.NetworkInterfaces[0].Groups, sg.ID)
|
||||
data.NetworkInterfaces[0].Groups = append(data.NetworkInterfaces[0].Groups, sg.ID)
|
||||
}
|
||||
// @step: add any tenancy details
|
||||
if t.Tenancy != nil {
|
||||
lc.Placement = &ec2.LaunchTemplatePlacementRequest{Tenancy: t.Tenancy}
|
||||
data.Placement = &ec2.LaunchTemplatePlacementRequest{Tenancy: t.Tenancy}
|
||||
}
|
||||
// @step: set the instance monitoring
|
||||
lc.Monitoring = &ec2.LaunchTemplatesMonitoringRequest{Enabled: fi.Bool(false)}
|
||||
data.Monitoring = &ec2.LaunchTemplatesMonitoringRequest{Enabled: fi.Bool(false)}
|
||||
if t.InstanceMonitoring != nil {
|
||||
lc.Monitoring = &ec2.LaunchTemplatesMonitoringRequest{Enabled: t.InstanceMonitoring}
|
||||
data.Monitoring = &ec2.LaunchTemplatesMonitoringRequest{Enabled: t.InstanceMonitoring}
|
||||
}
|
||||
// @step: add the iam instance profile
|
||||
if t.IAMInstanceProfile != nil {
|
||||
lc.IamInstanceProfile = &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{
|
||||
data.IamInstanceProfile = &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{
|
||||
Name: t.IAMInstanceProfile.Name,
|
||||
}
|
||||
}
|
||||
// @step: add the tags
|
||||
var tags []*ec2.Tag
|
||||
if len(t.Tags) > 0 {
|
||||
var tags []*ec2.Tag
|
||||
for k, v := range t.Tags {
|
||||
tags = append(tags, &ec2.Tag{
|
||||
Key: aws.String(k),
|
||||
Value: aws.String(v),
|
||||
})
|
||||
}
|
||||
lc.TagSpecifications = append(lc.TagSpecifications, &ec2.LaunchTemplateTagSpecificationRequest{
|
||||
data.TagSpecifications = append(data.TagSpecifications, &ec2.LaunchTemplateTagSpecificationRequest{
|
||||
ResourceType: aws.String(ec2.ResourceTypeInstance),
|
||||
Tags: tags,
|
||||
})
|
||||
lc.TagSpecifications = append(lc.TagSpecifications, &ec2.LaunchTemplateTagSpecificationRequest{
|
||||
data.TagSpecifications = append(data.TagSpecifications, &ec2.LaunchTemplateTagSpecificationRequest{
|
||||
ResourceType: aws.String(ec2.ResourceTypeVolume),
|
||||
Tags: tags,
|
||||
})
|
||||
input.TagSpecifications = append(input.TagSpecifications, &ec2.TagSpecification{
|
||||
ResourceType: aws.String(ec2.ResourceTypeLaunchTemplate),
|
||||
Tags: tags,
|
||||
})
|
||||
}
|
||||
// @step: add the userdata
|
||||
if t.UserData != nil {
|
||||
|
|
@ -129,7 +117,7 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *Launch
|
|||
if err != nil {
|
||||
return fmt.Errorf("error rendering LaunchTemplate UserData: %v", err)
|
||||
}
|
||||
lc.UserData = aws.String(base64.StdEncoding.EncodeToString(d))
|
||||
data.UserData = aws.String(base64.StdEncoding.EncodeToString(d))
|
||||
}
|
||||
// @step: add market options
|
||||
if fi.StringValue(t.SpotPrice) != "" {
|
||||
|
|
@ -138,18 +126,40 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *Launch
|
|||
InstanceInterruptionBehavior: t.InstanceInterruptionBehavior,
|
||||
MaxPrice: t.SpotPrice,
|
||||
}
|
||||
lc.InstanceMarketOptions = &ec2.LaunchTemplateInstanceMarketOptionsRequest{
|
||||
data.InstanceMarketOptions = &ec2.LaunchTemplateInstanceMarketOptionsRequest{
|
||||
MarketType: fi.String("spot"),
|
||||
SpotOptions: s,
|
||||
}
|
||||
}
|
||||
// @step: attempt to create the launch template
|
||||
if _, err = c.Cloud.EC2().CreateLaunchTemplate(input); err != nil {
|
||||
return fmt.Errorf("error creating LaunchTemplate: %v", err)
|
||||
if a == nil {
|
||||
input := &ec2.CreateLaunchTemplateInput{
|
||||
LaunchTemplateName: t.Name,
|
||||
LaunchTemplateData: data,
|
||||
TagSpecifications: []*ec2.TagSpecification{
|
||||
{
|
||||
ResourceType: aws.String(ec2.ResourceTypeLaunchTemplate),
|
||||
Tags: tags,
|
||||
},
|
||||
},
|
||||
}
|
||||
output, err := c.Cloud.EC2().CreateLaunchTemplate(input)
|
||||
if err != nil || output.LaunchTemplate == nil {
|
||||
return fmt.Errorf("error creating LaunchTemplate %q: %v", fi.StringValue(t.Name), err)
|
||||
}
|
||||
e.ID = output.LaunchTemplate.LaunchTemplateId
|
||||
} else {
|
||||
// TODO: Update the LaunchTemplate tags
|
||||
input := &ec2.CreateLaunchTemplateVersionInput{
|
||||
LaunchTemplateName: t.Name,
|
||||
LaunchTemplateData: data,
|
||||
}
|
||||
if _, err = c.Cloud.EC2().CreateLaunchTemplateVersion(input); err != nil {
|
||||
return fmt.Errorf("error creating LaunchTemplateVersion: %v", err)
|
||||
}
|
||||
e.ID = a.ID
|
||||
}
|
||||
|
||||
ep.ID = fi.String(name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +171,7 @@ func (t *LaunchTemplate) Find(c *fi.Context) (*LaunchTemplate, error) {
|
|||
}
|
||||
|
||||
// @step: get the latest launch template version
|
||||
lt, err := t.findLatestLaunchTemplate(c)
|
||||
lt, err := t.findLatestLaunchTemplateVersion(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -173,7 +183,7 @@ func (t *LaunchTemplate) Find(c *fi.Context) (*LaunchTemplate, error) {
|
|||
|
||||
actual := &LaunchTemplate{
|
||||
AssociatePublicIP: fi.Bool(false),
|
||||
ID: lt.LaunchTemplateName,
|
||||
ID: lt.LaunchTemplateId,
|
||||
ImageID: lt.LaunchTemplateData.ImageId,
|
||||
InstanceMonitoring: fi.Bool(false),
|
||||
InstanceType: lt.LaunchTemplateData.InstanceType,
|
||||
|
|
@ -306,83 +316,39 @@ func (t *LaunchTemplate) findAllLaunchTemplates(c *fi.Context) ([]*ec2.LaunchTem
|
|||
}
|
||||
}
|
||||
|
||||
// findLaunchTemplates returns a list of launch templates
|
||||
func (t *LaunchTemplate) findLaunchTemplates(c *fi.Context) ([]*ec2.LaunchTemplateVersion, error) {
|
||||
// findLatestLaunchTemplateVersion returns the latest template
|
||||
func (t *LaunchTemplate) findLatestLaunchTemplateVersion(c *fi.Context) (*ec2.LaunchTemplateVersion, error) {
|
||||
cloud, ok := c.Cloud.(awsup.AWSCloud)
|
||||
if !ok {
|
||||
return []*ec2.LaunchTemplateVersion{}, fmt.Errorf("invalid cloud provider: %v, expected: awsup.AWSCloud", c.Cloud)
|
||||
return nil, fmt.Errorf("invalid cloud provider: %v, expected: awsup.AWSCloud", c.Cloud)
|
||||
}
|
||||
|
||||
// @step: get a list of the launch templates
|
||||
templates, err := t.findAllLaunchTemplates(c)
|
||||
input := &ec2.DescribeLaunchTemplateVersionsInput{
|
||||
LaunchTemplateName: t.Name,
|
||||
Versions: []*string{aws.String("$Latest")},
|
||||
}
|
||||
|
||||
output, err := cloud.EC2().DescribeLaunchTemplateVersions(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prefix := fmt.Sprintf("%s-", fi.StringValue(t.Name))
|
||||
|
||||
// @step: get the launch template versions for the templates we are interested in
|
||||
var list []*ec2.LaunchTemplateVersion
|
||||
var next *string
|
||||
for _, x := range templates {
|
||||
if strings.HasPrefix(aws.StringValue(x.LaunchTemplateName), prefix) {
|
||||
err := func() error {
|
||||
for {
|
||||
resp, err := cloud.EC2().DescribeLaunchTemplateVersions(&ec2.DescribeLaunchTemplateVersionsInput{
|
||||
LaunchTemplateName: x.LaunchTemplateName,
|
||||
NextToken: next,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list = append(list, resp.LaunchTemplateVersions...)
|
||||
if resp.NextToken == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
next = resp.NextToken
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if awsup.AWSErrorCode(err) == "InvalidLaunchTemplateName.NotFoundException" {
|
||||
klog.V(4).Infof("Got InvalidLaunchTemplateName.NotFoundException error describing latest launch template version: %q", aws.StringValue(t.Name))
|
||||
return nil, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// @step: sort the configurations in chronological order
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
ti := list[i].CreateTime
|
||||
tj := list[j].CreateTime
|
||||
if tj == nil {
|
||||
return true
|
||||
}
|
||||
if ti == nil {
|
||||
return false
|
||||
}
|
||||
return ti.UnixNano() < tj.UnixNano()
|
||||
})
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// findLatestLaunchTemplate returns the latest template
|
||||
func (t *LaunchTemplate) findLatestLaunchTemplate(c *fi.Context) (*ec2.LaunchTemplateVersion, error) {
|
||||
// @step: get a list of configuration
|
||||
configurations, err := t.findLaunchTemplates(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(configurations) == 0 {
|
||||
if len(output.LaunchTemplateVersions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return configurations[len(configurations)-1], nil
|
||||
return output.LaunchTemplateVersions[0], nil
|
||||
}
|
||||
|
||||
// deleteLaunchTemplate tracks a LaunchConfiguration that we're going to delete
|
||||
// It implements fi.Deletion
|
||||
type deleteLaunchTemplate struct {
|
||||
lc *ec2.LaunchTemplateVersion
|
||||
lc *ec2.LaunchTemplate
|
||||
}
|
||||
|
||||
var _ fi.Deletion = &deleteLaunchTemplate{}
|
||||
|
|
|
|||
|
|
@ -694,52 +694,44 @@ func findAutoscalingGroupLaunchConfiguration(c AWSCloud, g *autoscaling.Group) (
|
|||
}
|
||||
|
||||
// @check the launch template then
|
||||
var launchTemplate *autoscaling.LaunchTemplateSpecification
|
||||
if g.LaunchTemplate != nil {
|
||||
name = aws.StringValue(g.LaunchTemplate.LaunchTemplateName)
|
||||
version := aws.StringValue(g.LaunchTemplate.Version)
|
||||
if name != "" {
|
||||
launchTemplate := name + ":" + version
|
||||
return launchTemplate, nil
|
||||
}
|
||||
launchTemplate = g.LaunchTemplate
|
||||
} else if g.MixedInstancesPolicy != nil && g.MixedInstancesPolicy.LaunchTemplate != nil && g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification != nil {
|
||||
launchTemplate = g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification
|
||||
} else {
|
||||
return "", fmt.Errorf("error finding launch template or configuration for autoscaling group: %s", aws.StringValue(g.AutoScalingGroupName))
|
||||
}
|
||||
|
||||
// @check: ok, lets check the mixed instance policy
|
||||
if g.MixedInstancesPolicy != nil {
|
||||
if g.MixedInstancesPolicy.LaunchTemplate != nil {
|
||||
if g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification != nil {
|
||||
var version string
|
||||
name = aws.StringValue(g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.LaunchTemplateName)
|
||||
//See what version the ASG is set to use
|
||||
mixedVersion := aws.StringValue(g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.Version)
|
||||
//Correctly Handle Default and Latest Versions
|
||||
if mixedVersion == "" || mixedVersion == "$Default" || mixedVersion == "$Latest" {
|
||||
request := &ec2.DescribeLaunchTemplatesInput{
|
||||
LaunchTemplateNames: []*string{&name},
|
||||
}
|
||||
dltResponse, err := c.EC2().DescribeLaunchTemplates(request)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error describing launch templates: %v", err)
|
||||
}
|
||||
launchTemplate := dltResponse.LaunchTemplates[0]
|
||||
if mixedVersion == "" || mixedVersion == "$Default" {
|
||||
version = strconv.FormatInt(*launchTemplate.DefaultVersionNumber, 10)
|
||||
} else {
|
||||
version = strconv.FormatInt(*launchTemplate.LatestVersionNumber, 10)
|
||||
}
|
||||
} else {
|
||||
version = mixedVersion
|
||||
}
|
||||
klog.V(4).Infof("Launch Template Version Specified By ASG: %v", mixedVersion)
|
||||
klog.V(4).Infof("Launch Template Version we are using for compare: %v", version)
|
||||
if name != "" {
|
||||
launchTemplate := name + ":" + version
|
||||
return launchTemplate, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
name = aws.StringValue(launchTemplate.LaunchTemplateName)
|
||||
if name == "" {
|
||||
return "", fmt.Errorf("error finding launch template for autoscaling group: %s", aws.StringValue(g.AutoScalingGroupName))
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("error finding launch template or configuration for autoscaling group: %s", aws.StringValue(g.AutoScalingGroupName))
|
||||
version := aws.StringValue(launchTemplate.Version)
|
||||
//Correctly Handle Default and Latest Versions
|
||||
klog.V(4).Infof("Launch Template Version Specified By ASG: %v", version)
|
||||
if version == "" || version == "$Default" || version == "$Latest" {
|
||||
input := &ec2.DescribeLaunchTemplatesInput{
|
||||
LaunchTemplateNames: []*string{&name},
|
||||
}
|
||||
output, err := c.EC2().DescribeLaunchTemplates(input)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error describing launch templates: %q", err)
|
||||
}
|
||||
if len(output.LaunchTemplates) == 0 {
|
||||
return "", fmt.Errorf("error finding launch template by name: %q", name)
|
||||
}
|
||||
launchTemplate := output.LaunchTemplates[0]
|
||||
if version == "" || version == "$Default" {
|
||||
version = strconv.FormatInt(*launchTemplate.DefaultVersionNumber, 10)
|
||||
} else {
|
||||
version = strconv.FormatInt(*launchTemplate.LatestVersionNumber, 10)
|
||||
}
|
||||
}
|
||||
klog.V(4).Infof("Launch Template Version used for compare: %q", version)
|
||||
|
||||
return fmt.Sprintf("%s:%s", name, version), nil
|
||||
}
|
||||
|
||||
// findInstanceLaunchConfiguration is responsible for discoverying the launch configuration for an instance
|
||||
|
|
|
|||
Loading…
Reference in New Issue