mirror of https://github.com/kubernetes/kops.git
304 lines
12 KiB
Go
304 lines
12 KiB
Go
/*
|
|
Copyright 2019 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package awstasks
|
|
|
|
import (
|
|
"encoding/base64"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"k8s.io/kops/upup/pkg/fi"
|
|
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
|
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
|
)
|
|
|
|
type cloudformationLaunchTemplateNetworkInterface struct {
|
|
// AssociatePublicIPAddress associates a public ip address with the network interface. Boolean value.
|
|
AssociatePublicIPAddress *bool `json:"AssociatePublicIpAddress,omitempty"`
|
|
// DeleteOnTermination indicates whether the network interface should be destroyed on instance termination.
|
|
DeleteOnTermination *bool `json:"DeleteOnTermination,omitempty"`
|
|
// DeviceIndex is the device index for the network interface attachment.
|
|
DeviceIndex *int `json:"DeviceIndex,omitempty"`
|
|
// SecurityGroups is a list of security group ids.
|
|
SecurityGroups []*cloudformation.Literal `json:"Groups,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateMonitoring struct {
|
|
// Enabled indicates that monitoring is enabled
|
|
Enabled *bool `json:"Enabled,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplatePlacement struct {
|
|
// Affinity is he affinity setting for an instance on a Dedicated Host.
|
|
Affinity *string `json:"Affinity,omitempty"`
|
|
// AvailabilityZone is the Availability Zone for the instance.
|
|
AvailabilityZone *string `json:"AvailabilityZone,omitempty"`
|
|
// GroupName is the name of the placement group for the instance.
|
|
GroupName *string `json:"GroupName,omitempty"`
|
|
// HostID is the ID of the Dedicated Host for the instance.
|
|
HostID *string `json:"HostId,omitempty"`
|
|
// SpreadDomain are reserved for future use.
|
|
SpreadDomain *string `json:"SpreadDomain,omitempty"`
|
|
// Tenancy ist he tenancy of the instance. Can be default, dedicated, or host.
|
|
Tenancy *string `json:"Tenancy,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateIAMProfile struct {
|
|
// Name is the name of the profile
|
|
Name *cloudformation.Literal `json:"Name,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateMarketOptionsSpotOptions struct {
|
|
// BlockDurationMinutes is required duration in minutes. This value must be a multiple of 60.
|
|
BlockDurationMinutes *int64 `json:"BlockDurationMinutes,omitempty"`
|
|
// InstanceInterruptionBehavior is the behavior when a Spot Instance is interrupted. Can be hibernate, stop, or terminate
|
|
InstanceInterruptionBehavior *string `json:"InstanceInterruptionBehavior,omitempty"`
|
|
// MaxPrice is the maximum hourly price you're willing to pay for the Spot Instances
|
|
MaxPrice *string `json:"MaxPrice,omitempty"`
|
|
// SpotInstanceType is the Spot Instance request type. Can be one-time, or persistent
|
|
SpotInstanceType *string `json:"SpotInstanceType,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateMarketOptions struct {
|
|
// MarketType is the option type
|
|
MarketType *string `json:"MarketType,omitempty"`
|
|
// SpotOptions are the set of options
|
|
SpotOptions *cloudformationLaunchTemplateMarketOptionsSpotOptions `json:"SpotOptions,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateBlockDeviceEBS struct {
|
|
// VolumeType is the ebs type to use
|
|
VolumeType *string `json:"VolumeType,omitempty"`
|
|
// VolumeSize is the volume size
|
|
VolumeSize *int64 `json:"VolumeSize,omitempty"`
|
|
// IOPS is the provisioned iops
|
|
IOPS *int64 `json:"Iops,omitempty"`
|
|
// Throughput is the gp3 volume throughput
|
|
Throughput *int64 `json:"Throughput,omitempty"`
|
|
// DeleteOnTermination indicates the volume should die with the instance
|
|
DeleteOnTermination *bool `json:"DeleteOnTermination,omitempty"`
|
|
// Encrypted indicates the device is encrypted
|
|
Encrypted *bool `json:"Encrypted,omitempty"`
|
|
// KmsKeyID is the encryption key identifier for the volume
|
|
KmsKeyID *string `json:"KmsKeyId,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateBlockDevice struct {
|
|
// DeviceName is the name of the device
|
|
DeviceName *string `json:"DeviceName,omitempty"`
|
|
// VirtualName is used for the ephemeral devices
|
|
VirtualName *string `json:"VirtualName,omitempty"`
|
|
// EBS defines the ebs spec
|
|
EBS *cloudformationLaunchTemplateBlockDeviceEBS `json:"Ebs,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateTagSpecification struct {
|
|
// ResourceType is the type of resource to tag.
|
|
ResourceType *string `json:"ResourceType,omitempty"`
|
|
// Tags are the tags to apply to the resource.
|
|
Tags []cloudformationTag `json:"Tags,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateInstanceMetadataOptions struct {
|
|
// HTTPPutResponseHopLimit is the desired HTTP PUT response hop limit for instance metadata requests.
|
|
HTTPPutResponseHopLimit *int64 `json:"HttpPutResponseHopLimit,omitempty"`
|
|
// HTTPTokens is the state of token usage for your instance metadata requests.
|
|
HTTPTokens *string `json:"HttpTokens,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplateData struct {
|
|
// BlockDeviceMappings is the device mappings
|
|
BlockDeviceMappings []*cloudformationLaunchTemplateBlockDevice `json:"BlockDeviceMappings,omitempty"`
|
|
// EBSOptimized indicates if the root device is ebs optimized
|
|
EBSOptimized *bool `json:"EbsOptimized,omitempty"`
|
|
// IAMInstanceProfile is the IAM profile to assign to the nodes
|
|
IAMInstanceProfile *cloudformationLaunchTemplateIAMProfile `json:"IamInstanceProfile,omitempty"`
|
|
// ImageID is the ami to use for the instances
|
|
ImageID *string `json:"ImageId,omitempty"`
|
|
// InstanceType is the type of instance
|
|
InstanceType *string `json:"InstanceType,omitempty"`
|
|
// KeyName is the ssh key to use
|
|
KeyName *string `json:"KeyName,omitempty"`
|
|
// MarketOptions are the spot pricing options
|
|
MarketOptions *cloudformationLaunchTemplateMarketOptions `json:"InstanceMarketOptions,omitempty"`
|
|
// MetadataOptions are the instance metadata options.
|
|
MetadataOptions *cloudformationLaunchTemplateInstanceMetadataOptions `json:"MetadataOptions,omitempty"`
|
|
// Monitoring are the instance monitoring options
|
|
Monitoring *cloudformationLaunchTemplateMonitoring `json:"Monitoring,omitempty"`
|
|
// NetworkInterfaces are the networking options
|
|
NetworkInterfaces []*cloudformationLaunchTemplateNetworkInterface `json:"NetworkInterfaces,omitempty"`
|
|
// Placement are the tenancy options
|
|
Placement []*cloudformationLaunchTemplatePlacement `json:"Placement,omitempty"`
|
|
// TagSpecifications are the tags to apply to a resource when it is created.
|
|
TagSpecifications []*cloudformationLaunchTemplateTagSpecification `json:"TagSpecifications,omitempty"`
|
|
// UserData is the user data for the instances
|
|
UserData *string `json:"UserData,omitempty"`
|
|
}
|
|
|
|
type cloudformationLaunchTemplate struct {
|
|
// LaunchTemplateName is the name of the launch template
|
|
LaunchTemplateName *string `json:"LaunchTemplateName,omitempty"`
|
|
// LaunchTemplateData is the data request
|
|
LaunchTemplateData *cloudformationLaunchTemplateData `json:"LaunchTemplateData,omitempty"`
|
|
}
|
|
|
|
// CloudformationLink returns the cloudformation link for us
|
|
func (t *LaunchTemplate) CloudformationLink() *cloudformation.Literal {
|
|
return cloudformation.Ref("AWS::EC2::LaunchTemplate", fi.StringValue(t.Name))
|
|
}
|
|
|
|
// CloudformationLink returns the cloudformation version.
|
|
func (t *LaunchTemplate) CloudformationVersion() *cloudformation.Literal {
|
|
return cloudformation.GetAtt("AWS::EC2::LaunchTemplate", fi.StringValue(t.Name), "LatestVersionNumber")
|
|
}
|
|
|
|
// RenderCloudformation is responsible for rendering the cloudformation json
|
|
func (t *LaunchTemplate) RenderCloudformation(target *cloudformation.CloudformationTarget, a, e, changes *LaunchTemplate) error {
|
|
var err error
|
|
|
|
cloud := target.Cloud.(awsup.AWSCloud)
|
|
|
|
var image *string
|
|
if e.ImageID != nil {
|
|
im, err := cloud.ResolveImage(fi.StringValue(e.ImageID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
image = im.ImageId
|
|
}
|
|
|
|
launchTemplateData := &cloudformationLaunchTemplateData{
|
|
EBSOptimized: e.RootVolumeOptimization,
|
|
ImageID: image,
|
|
InstanceType: e.InstanceType,
|
|
MetadataOptions: &cloudformationLaunchTemplateInstanceMetadataOptions{
|
|
HTTPTokens: e.HTTPTokens,
|
|
HTTPPutResponseHopLimit: e.HTTPPutResponseHopLimit,
|
|
},
|
|
NetworkInterfaces: []*cloudformationLaunchTemplateNetworkInterface{
|
|
{
|
|
AssociatePublicIPAddress: e.AssociatePublicIP,
|
|
DeleteOnTermination: fi.Bool(true),
|
|
DeviceIndex: fi.Int(0),
|
|
},
|
|
},
|
|
}
|
|
|
|
if fi.StringValue(e.SpotPrice) != "" {
|
|
marketSpotOptions := cloudformationLaunchTemplateMarketOptionsSpotOptions{MaxPrice: e.SpotPrice}
|
|
if e.SpotDurationInMinutes != nil {
|
|
marketSpotOptions.BlockDurationMinutes = e.SpotDurationInMinutes
|
|
}
|
|
if e.InstanceInterruptionBehavior != nil {
|
|
marketSpotOptions.InstanceInterruptionBehavior = e.InstanceInterruptionBehavior
|
|
}
|
|
launchTemplateData.MarketOptions = &cloudformationLaunchTemplateMarketOptions{MarketType: fi.String("spot"), SpotOptions: &marketSpotOptions}
|
|
}
|
|
|
|
cf := &cloudformationLaunchTemplate{
|
|
LaunchTemplateName: fi.String(fi.StringValue(e.Name)),
|
|
LaunchTemplateData: launchTemplateData,
|
|
}
|
|
data := cf.LaunchTemplateData
|
|
|
|
for _, x := range e.SecurityGroups {
|
|
data.NetworkInterfaces[0].SecurityGroups = append(data.NetworkInterfaces[0].SecurityGroups, x.CloudformationLink())
|
|
}
|
|
if e.SSHKey != nil {
|
|
data.KeyName = e.SSHKey.Name
|
|
}
|
|
if e.Tenancy != nil {
|
|
data.Placement = []*cloudformationLaunchTemplatePlacement{{Tenancy: e.Tenancy}}
|
|
}
|
|
if e.InstanceMonitoring != nil {
|
|
data.Monitoring = &cloudformationLaunchTemplateMonitoring{
|
|
Enabled: e.InstanceMonitoring,
|
|
}
|
|
}
|
|
if e.IAMInstanceProfile != nil {
|
|
data.IAMInstanceProfile = &cloudformationLaunchTemplateIAMProfile{
|
|
Name: e.IAMInstanceProfile.CloudformationLink(),
|
|
}
|
|
}
|
|
if e.UserData != nil {
|
|
d, err := fi.ResourceAsBytes(e.UserData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data.UserData = aws.String(base64.StdEncoding.EncodeToString(d))
|
|
}
|
|
devices, err := e.buildRootDevice(cloud)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
additionals, err := buildAdditionalDevices(e.BlockDeviceMappings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for name, x := range devices {
|
|
data.BlockDeviceMappings = append(data.BlockDeviceMappings, &cloudformationLaunchTemplateBlockDevice{
|
|
DeviceName: fi.String(name),
|
|
EBS: &cloudformationLaunchTemplateBlockDeviceEBS{
|
|
DeleteOnTermination: fi.Bool(true),
|
|
IOPS: x.EbsVolumeIops,
|
|
Throughput: x.EbsVolumeThroughput,
|
|
VolumeSize: x.EbsVolumeSize,
|
|
VolumeType: x.EbsVolumeType,
|
|
Encrypted: x.EbsEncrypted,
|
|
KmsKeyID: x.EbsKmsKey,
|
|
},
|
|
})
|
|
}
|
|
for name, x := range additionals {
|
|
data.BlockDeviceMappings = append(data.BlockDeviceMappings, &cloudformationLaunchTemplateBlockDevice{
|
|
DeviceName: fi.String(name),
|
|
EBS: &cloudformationLaunchTemplateBlockDeviceEBS{
|
|
DeleteOnTermination: fi.Bool(true),
|
|
IOPS: x.EbsVolumeIops,
|
|
VolumeSize: x.EbsVolumeSize,
|
|
VolumeType: x.EbsVolumeType,
|
|
Encrypted: x.EbsEncrypted,
|
|
KmsKeyID: x.EbsKmsKey,
|
|
},
|
|
})
|
|
}
|
|
|
|
devices, err = buildEphemeralDevices(cloud, fi.StringValue(e.InstanceType))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for n, x := range devices {
|
|
data.BlockDeviceMappings = append(data.BlockDeviceMappings, &cloudformationLaunchTemplateBlockDevice{
|
|
VirtualName: x.VirtualName,
|
|
DeviceName: fi.String(n),
|
|
})
|
|
}
|
|
|
|
if e.Tags != nil {
|
|
tags := buildCloudformationTags(t.Tags)
|
|
data.TagSpecifications = append(data.TagSpecifications, &cloudformationLaunchTemplateTagSpecification{
|
|
ResourceType: fi.String("instance"),
|
|
Tags: tags,
|
|
})
|
|
data.TagSpecifications = append(data.TagSpecifications, &cloudformationLaunchTemplateTagSpecification{
|
|
ResourceType: fi.String("volume"),
|
|
Tags: tags,
|
|
})
|
|
}
|
|
|
|
return target.RenderResource("AWS::EC2::LaunchTemplate", fi.StringValue(e.Name), cf)
|
|
}
|