Merge pull request #9024 from tomesm/support_launch_template

Added Launch Template support for instance interruption behavior
This commit is contained in:
Kubernetes Prow Robot 2020-05-17 15:35:36 -07:00 committed by GitHub
commit bda2a15ee6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 116 additions and 25 deletions

View File

@ -217,6 +217,10 @@ spec:
image:
description: Image is the instance (ami etc) we should use
type: string
instanceInterruptionBehavior:
description: InstanceInterruptionBehavior defines if a spot instance
should be terminated, hibernated, or stopped after interruption
type: string
instanceProtection:
description: InstanceProtection makes new instances in an autoscaling
group protected from scale in

View File

@ -161,6 +161,9 @@ type InstanceGroupSpec struct {
SysctlParameters []string `json:"sysctlParameters,omitempty"`
// RollingUpdate defines the rolling-update behavior
RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"`
// InstanceInterruptionBehavior defines if a spot instance should be terminated, hibernated,
// or stopped after interruption
InstanceInterruptionBehavior *string `json:"instanceInterruptionBehavior,omitempty"`
}
const (

View File

@ -157,6 +157,9 @@ type InstanceGroupSpec struct {
SysctlParameters []string `json:"sysctlParameters,omitempty"`
// RollingUpdate defines the rolling-update behavior
RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"`
// InstanceInterruptionBehavior defines if a spot instance should be terminated, hibernated,
// or stopped after interruption
InstanceInterruptionBehavior *string `json:"instanceInterruptionBehavior,omitempty"`
}
const (

View File

@ -3370,6 +3370,7 @@ func autoConvert_v1alpha2_InstanceGroupSpec_To_kops_InstanceGroupSpec(in *Instan
} else {
out.RollingUpdate = nil
}
out.InstanceInterruptionBehavior = in.InstanceInterruptionBehavior
return nil
}
@ -3508,6 +3509,7 @@ func autoConvert_kops_InstanceGroupSpec_To_v1alpha2_InstanceGroupSpec(in *kops.I
} else {
out.RollingUpdate = nil
}
out.InstanceInterruptionBehavior = in.InstanceInterruptionBehavior
return nil
}

View File

@ -1797,6 +1797,11 @@ func (in *InstanceGroupSpec) DeepCopyInto(out *InstanceGroupSpec) {
*out = new(RollingUpdate)
(*in).DeepCopyInto(*out)
}
if in.InstanceInterruptionBehavior != nil {
in, out := &in.InstanceInterruptionBehavior, &out.InstanceInterruptionBehavior
*out = new(string)
**out = **in
}
return
}

View File

@ -51,6 +51,8 @@ func awsValidateInstanceGroup(ig *kops.InstanceGroup) field.ErrorList {
allErrs = append(allErrs, awsValidateSpotDurationInMinute(field.NewPath(ig.GetName(), "spec", "spotDurationInMinutes"), ig)...)
allErrs = append(allErrs, awsValidateInstanceInterruptionBehavior(field.NewPath(ig.GetName(), "spec", "instanceInterruptionBehavior"), ig)...)
return allErrs
}
@ -120,3 +122,13 @@ func awsValidateSpotDurationInMinute(fieldPath *field.Path, ig *kops.InstanceGro
}
return allErrs
}
func awsValidateInstanceInterruptionBehavior(fieldPath *field.Path, ig *kops.InstanceGroup) field.ErrorList {
allErrs := field.ErrorList{}
if ig.Spec.InstanceInterruptionBehavior != nil {
validInterruptionBehaviors := []string{"terminate", "hibernate", "stop"}
instanceInterruptionBehavior := *ig.Spec.InstanceInterruptionBehavior
allErrs = append(allErrs, IsValidValue(fieldPath, &instanceInterruptionBehavior, validInterruptionBehaviors)...)
}
return allErrs
}

View File

@ -134,6 +134,32 @@ func TestValidateInstanceGroupSpec(t *testing.T) {
},
ExpectedErrors: []string{},
},
{
Input: kops.InstanceGroupSpec{
InstanceInterruptionBehavior: fi.String("invalidValue"),
},
ExpectedErrors: []string{
"Unsupported value::test-nodes.spec.instanceInterruptionBehavior",
},
},
{
Input: kops.InstanceGroupSpec{
InstanceInterruptionBehavior: fi.String("terminate"),
},
ExpectedErrors: []string{},
},
{
Input: kops.InstanceGroupSpec{
InstanceInterruptionBehavior: fi.String("hibernate"),
},
ExpectedErrors: []string{},
},
{
Input: kops.InstanceGroupSpec{
InstanceInterruptionBehavior: fi.String("stop"),
},
ExpectedErrors: []string{},
},
}
for _, g := range grid {
ig := &kops.InstanceGroup{

View File

@ -1963,6 +1963,11 @@ func (in *InstanceGroupSpec) DeepCopyInto(out *InstanceGroupSpec) {
*out = new(RollingUpdate)
(*in).DeepCopyInto(*out)
}
if in.InstanceInterruptionBehavior != nil {
in, out := &in.InstanceInterruptionBehavior, &out.InstanceInterruptionBehavior
*out = new(string)
**out = **in
}
return
}

View File

@ -134,6 +134,9 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.ModelBuilde
if ig.Spec.SpotDurationInMinutes != nil {
lt.SpotDurationInMinutes = ig.Spec.SpotDurationInMinutes
}
if ig.Spec.InstanceInterruptionBehavior != nil {
lt.InstanceInterruptionBehavior = ig.Spec.InstanceInterruptionBehavior
}
return lt, nil
}

View File

@ -606,6 +606,7 @@
"MarketType": "spot",
"SpotOptions": {
"BlockDurationMinutes": 120,
"InstanceInterruptionBehavior": "hibernate",
"MaxPrice": "0.1"
}
},

View File

@ -73,6 +73,7 @@ spec:
instanceProtection: true
maxPrice: "0.1"
spotDurationInMinutes: 120
instanceInterruptionBehavior: "hibernate"
subnets:
- us-test-1b
---

View File

@ -531,6 +531,7 @@ resource "aws_launch_template" "nodes-launchtemplates-example-com" {
market_type = "spot"
spot_options {
block_duration_minutes = 120
instance_interruption_behavior = "hibernate"
max_price = "0.1"
}
}

View File

@ -70,6 +70,9 @@ 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 (

View File

@ -128,7 +128,15 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *Launch
}
lc.UserData = aws.String(base64.StdEncoding.EncodeToString(d))
}
// @step: add instanceInterruptionBehavior
if t.InstanceInterruptionBehavior != nil {
s := &ec2.LaunchTemplateSpotMarketOptionsRequest{
InstanceInterruptionBehavior: t.InstanceInterruptionBehavior,
}
lc.InstanceMarketOptions = &ec2.LaunchTemplateInstanceMarketOptionsRequest{
SpotOptions: s,
}
}
// @step: attempt to create the launch template
err = func() error {
for attempt := 0; attempt < 10; attempt++ {
@ -223,6 +231,10 @@ func (t *LaunchTemplate) Find(c *fi.Context) (*LaunchTemplate, error) {
if lt.LaunchTemplateData.IamInstanceProfile != nil {
actual.IAMInstanceProfile = &IAMInstanceProfile{Name: lt.LaunchTemplateData.IamInstanceProfile.Name}
}
// @step: add instanceInterruptionBehavior if there is one
if lt.LaunchTemplateData.InstanceMarketOptions != nil && lt.LaunchTemplateData.InstanceMarketOptions.SpotOptions != nil {
actual.InstanceInterruptionBehavior = lt.LaunchTemplateData.InstanceMarketOptions.SpotOptions.InstanceInterruptionBehavior
}
// @step: get the image is order to find out the root device name as using the index
// is not variable, under conditions they move

View File

@ -64,8 +64,8 @@ type cloudformationLaunchTemplateIAMProfile struct {
type cloudformationLaunchTemplateMarketOptionsSpotOptions struct {
// BlockDurationMinutes is required duration in minutes. This value must be a multiple of 60.
BlockDurationMinutes *int64 `json:"BlockDurationMinutes,omitempty"`
// InstancesInterruptionBehavior is the behavior when a Spot Instance is interrupted. Can be hibernate, stop, or terminate
InstancesInterruptionBehavior *string `json:"InstancesInterruptionBehavior,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
@ -185,6 +185,9 @@ func (t *LaunchTemplate) RenderCloudformation(target *cloudformation.Cloudformat
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}
}

View File

@ -39,6 +39,7 @@ func TestLaunchTemplateCloudformationRender(t *testing.T) {
RootVolumeSize: fi.Int64(64),
SpotPrice: "10",
SpotDurationInMinutes: fi.Int64(120),
InstanceInterruptionBehavior: fi.String("hibernate"),
SSHKey: &SSHKey{
Name: fi.String("mykey"),
},
@ -67,6 +68,7 @@ func TestLaunchTemplateCloudformationRender(t *testing.T) {
"MarketType": "spot",
"SpotOptions": {
"BlockDurationMinutes": 120,
"InstanceInterruptionBehavior": "hibernate",
"MaxPrice": "10"
}
},

View File

@ -61,8 +61,8 @@ type terraformLaunchTemplateIAMProfile struct {
type terraformLaunchTemplateMarketOptionsSpotOptions struct {
// BlockDurationMinutes is required duration in minutes. This value must be a multiple of 60.
BlockDurationMinutes *int64 `json:"block_duration_minutes,omitempty" cty:"block_duration_minutes"`
// InstancesInterruptionBehavior is the behavior when a Spot Instance is interrupted. Can be hibernate, stop, or terminate
InstancesInterruptionBehavior *string `json:"instances_interruption_behavior,omitempty" cty:"instances_interruption_behavior"`
// InstanceInterruptionBehavior is the behavior when a Spot Instance is interrupted. Can be hibernate, stop, or terminate
InstanceInterruptionBehavior *string `json:"instance_interruption_behavior,omitempty" cty:"instance_interruption_behavior"`
// MaxPrice is the maximum hourly price you're willing to pay for the Spot Instances
MaxPrice *string `json:"max_price,omitempty" cty:"max_price"`
// SpotInstanceType is the Spot Instance request type. Can be one-time, or persistent
@ -183,6 +183,9 @@ func (t *LaunchTemplate) RenderTerraform(target *terraform.TerraformTarget, a, e
if e.SpotDurationInMinutes != nil {
marketSpotOptions.BlockDurationMinutes = e.SpotDurationInMinutes
}
if e.InstanceInterruptionBehavior != nil {
marketSpotOptions.InstanceInterruptionBehavior = e.InstanceInterruptionBehavior
}
tf.MarketOptions = []*terraformLaunchTemplateMarketOptions{
{
MarketType: fi.String("spot"),

View File

@ -36,6 +36,7 @@ func TestLaunchTemplateTerraformRender(t *testing.T) {
InstanceType: fi.String("t2.medium"),
SpotPrice: "0.1",
SpotDurationInMinutes: fi.Int64(60),
InstanceInterruptionBehavior: fi.String("hibernate"),
RootVolumeOptimization: fi.Bool(true),
RootVolumeIops: fi.Int64(100),
RootVolumeSize: fi.Int64(64),
@ -62,6 +63,7 @@ resource "aws_launch_template" "test" {
market_type = "spot"
spot_options {
block_duration_minutes = 60
instance_interruption_behavior = "hibernate"
max_price = "0.1"
}
}