Merge pull request #13946 from jonasasx/feature/gce_spot

Adding GCE SPOT support
This commit is contained in:
Kubernetes Prow Robot 2022-07-16 14:06:53 -07:00 committed by GitHub
commit 02433a6785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 84 additions and 9 deletions

View File

@ -222,6 +222,12 @@ spec:
type: array type: array
type: object type: object
type: array type: array
gcpProvisioningModel:
description: 'GCPProvisioningModel: Specifies the provisioning model
of the GCP instance. Valid values: ''STANDARD'': (default) standard
provisioning with user controlled run time, no discounts ''SPOT'':
heavily discounted, no guaranteed run time.'
type: string
guestAccelerators: guestAccelerators:
description: GuestAccelerators configures additional accelerators description: GuestAccelerators configures additional accelerators
items: items:

View File

@ -199,6 +199,11 @@ type InstanceGroupSpec struct {
// MaxInstanceLifetime to the maximum amount of time, in seconds, that an instance can be in service. // MaxInstanceLifetime to the maximum amount of time, in seconds, that an instance can be in service.
// Value expected must be in form of duration ("ms", "s", "m", "h") // Value expected must be in form of duration ("ms", "s", "m", "h")
MaxInstanceLifetime *metav1.Duration `json:"maxInstanceLifetime,omitempty"` MaxInstanceLifetime *metav1.Duration `json:"maxInstanceLifetime,omitempty"`
// GCPProvisioningModel: Specifies the provisioning model of the GCP instance.
// Valid values:
// 'STANDARD': (default) standard provisioning with user controlled run time, no discounts
// 'SPOT': heavily discounted, no guaranteed run time.
GCPProvisioningModel *string `json:"gcpProvisioningModel,omitempty"`
} }
const ( const (

View File

@ -165,6 +165,11 @@ type InstanceGroupSpec struct {
// MaxInstanceLifetime to the maximum amount of time, in seconds, that an instance can be in service. // MaxInstanceLifetime to the maximum amount of time, in seconds, that an instance can be in service.
// Value expected must be in form of duration ("ms", "s", "m", "h") // Value expected must be in form of duration ("ms", "s", "m", "h")
MaxInstanceLifetime *metav1.Duration `json:"maxInstanceLifetime,omitempty"` MaxInstanceLifetime *metav1.Duration `json:"maxInstanceLifetime,omitempty"`
// GCPProvisioningModel: Specifies the provisioning model of the GCP instance.
// Valid values:
// 'STANDARD': (default) standard provisioning with user controlled run time, no discounts
// 'SPOT': heavily discounted, no guaranteed run time.
GCPProvisioningModel *string `json:"gcpProvisioningModel,omitempty"`
} }
// InstanceMetadataOptions defines the EC2 instance metadata service options (AWS Only) // InstanceMetadataOptions defines the EC2 instance metadata service options (AWS Only)

View File

@ -4474,6 +4474,7 @@ func autoConvert_v1alpha2_InstanceGroupSpec_To_kops_InstanceGroupSpec(in *Instan
out.GuestAccelerators = nil out.GuestAccelerators = nil
} }
out.MaxInstanceLifetime = in.MaxInstanceLifetime out.MaxInstanceLifetime = in.MaxInstanceLifetime
out.GCPProvisioningModel = in.GCPProvisioningModel
return nil return nil
} }
@ -4658,6 +4659,7 @@ func autoConvert_kops_InstanceGroupSpec_To_v1alpha2_InstanceGroupSpec(in *kops.I
out.GuestAccelerators = nil out.GuestAccelerators = nil
} }
out.MaxInstanceLifetime = in.MaxInstanceLifetime out.MaxInstanceLifetime = in.MaxInstanceLifetime
out.GCPProvisioningModel = in.GCPProvisioningModel
return nil return nil
} }

View File

@ -2544,6 +2544,11 @@ func (in *InstanceGroupSpec) DeepCopyInto(out *InstanceGroupSpec) {
*out = new(v1.Duration) *out = new(v1.Duration)
**out = **in **out = **in
} }
if in.GCPProvisioningModel != nil {
in, out := &in.GCPProvisioningModel, &out.GCPProvisioningModel
*out = new(string)
**out = **in
}
return return
} }

View File

@ -162,6 +162,11 @@ type InstanceGroupSpec struct {
// MaxInstanceLifetime to the maximum amount of time, in seconds, that an instance can be in service. // MaxInstanceLifetime to the maximum amount of time, in seconds, that an instance can be in service.
// Value expected must be in form of duration ("ms", "s", "m", "h") // Value expected must be in form of duration ("ms", "s", "m", "h")
MaxInstanceLifetime *metav1.Duration `json:"maxInstanceLifetime,omitempty"` MaxInstanceLifetime *metav1.Duration `json:"maxInstanceLifetime,omitempty"`
// GCPProvisioningModel: Specifies the provisioning model of the GCP instance.
// Valid values:
// 'STANDARD': (default) standard provisioning with user controlled run time, no discounts
// 'SPOT': heavily discounted, no guaranteed run time.
GCPProvisioningModel *string `json:"gcpProvisioningModel,omitempty"`
} }
// InstanceMetadataOptions defines the EC2 instance metadata service options (AWS Only) // InstanceMetadataOptions defines the EC2 instance metadata service options (AWS Only)

View File

@ -4613,6 +4613,7 @@ func autoConvert_v1alpha3_InstanceGroupSpec_To_kops_InstanceGroupSpec(in *Instan
out.GuestAccelerators = nil out.GuestAccelerators = nil
} }
out.MaxInstanceLifetime = in.MaxInstanceLifetime out.MaxInstanceLifetime = in.MaxInstanceLifetime
out.GCPProvisioningModel = in.GCPProvisioningModel
return nil return nil
} }
@ -4797,6 +4798,7 @@ func autoConvert_kops_InstanceGroupSpec_To_v1alpha3_InstanceGroupSpec(in *kops.I
out.GuestAccelerators = nil out.GuestAccelerators = nil
} }
out.MaxInstanceLifetime = in.MaxInstanceLifetime out.MaxInstanceLifetime = in.MaxInstanceLifetime
out.GCPProvisioningModel = in.GCPProvisioningModel
return nil return nil
} }

View File

@ -2550,6 +2550,11 @@ func (in *InstanceGroupSpec) DeepCopyInto(out *InstanceGroupSpec) {
*out = new(v1.Duration) *out = new(v1.Duration)
**out = **in **out = **in
} }
if in.GCPProvisioningModel != nil {
in, out := &in.GCPProvisioningModel, &out.GCPProvisioningModel
*out = new(string)
**out = **in
}
return return
} }

View File

@ -19,6 +19,7 @@ package validation
import ( import (
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
) )
func gceValidateCluster(c *kops.Cluster) field.ErrorList { func gceValidateCluster(c *kops.Cluster) field.ErrorList {
@ -45,3 +46,13 @@ func gceValidateCluster(c *kops.Cluster) field.ErrorList {
return allErrs return allErrs
} }
func gceValidateInstanceGroup(ig *kops.InstanceGroup, cloud gce.GCECloud) field.ErrorList {
allErrs := field.ErrorList{}
if ig.Spec.GCPProvisioningModel != nil {
fieldSpec := field.NewPath("spec")
allErrs = append(allErrs, IsValidValue(fieldSpec.Child("gcpProvisioningModel"), ig.Spec.GCPProvisioningModel, []string{"STANDARD", "SPOT"})...)
}
return allErrs
}

View File

@ -31,6 +31,7 @@ import (
"k8s.io/kops/pkg/apis/kops/util" "k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
) )
// ValidateInstanceGroup is responsible for validating the configuration of a instancegroup // ValidateInstanceGroup is responsible for validating the configuration of a instancegroup
@ -147,8 +148,13 @@ func ValidateInstanceGroup(g *kops.InstanceGroup, cloud fi.Cloud, strict bool) f
allErrs = append(allErrs, validateIGCloudLabels(g, field.NewPath("spec", "cloudLabels"))...) allErrs = append(allErrs, validateIGCloudLabels(g, field.NewPath("spec", "cloudLabels"))...)
} }
if cloud != nil && cloud.ProviderID() == kops.CloudProviderAWS { if cloud != nil {
allErrs = append(allErrs, awsValidateInstanceGroup(g, cloud.(awsup.AWSCloud))...) switch cloud.ProviderID() {
case kops.CloudProviderAWS:
allErrs = append(allErrs, awsValidateInstanceGroup(g, cloud.(awsup.AWSCloud))...)
case kops.CloudProviderGCE:
allErrs = append(allErrs, gceValidateInstanceGroup(g, cloud.(gce.GCECloud))...)
}
} }
for i, lb := range g.Spec.ExternalLoadBalancers { for i, lb := range g.Spec.ExternalLoadBalancers {

View File

@ -2713,6 +2713,11 @@ func (in *InstanceGroupSpec) DeepCopyInto(out *InstanceGroupSpec) {
*out = new(v1.Duration) *out = new(v1.Duration)
**out = **in **out = **in
} }
if in.GCPProvisioningModel != nil {
in, out := &in.GCPProvisioningModel, &out.GCPProvisioningModel
*out = new(string)
**out = **in
}
return return
} }

View File

@ -89,8 +89,8 @@ func (b *AutoscalingGroupModelBuilder) buildInstanceTemplate(c *fi.ModelBuilderC
BootDiskSizeGB: i64(int64(volumeSize)), BootDiskSizeGB: i64(int64(volumeSize)),
BootDiskImage: s(ig.Spec.Image), BootDiskImage: s(ig.Spec.Image),
// TODO: Support preemptible nodes? Preemptible: fi.Bool(fi.StringValue(ig.Spec.GCPProvisioningModel) == "SPOT"),
Preemptible: fi.Bool(false), GCPProvisioningModel: ig.Spec.GCPProvisioningModel,
HasExternalIP: fi.Bool(b.Cluster.Spec.Topology.Masters == kops.TopologyPublic), HasExternalIP: fi.Bool(b.Cluster.Spec.Topology.Masters == kops.TopologyPublic),

View File

@ -554,6 +554,7 @@ resource "google_compute_instance_template" "master-us-test1-a-ha-gce-example-co
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -600,6 +601,7 @@ resource "google_compute_instance_template" "master-us-test1-b-ha-gce-example-co
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -646,6 +648,7 @@ resource "google_compute_instance_template" "master-us-test1-c-ha-gce-example-co
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -692,6 +695,7 @@ resource "google_compute_instance_template" "nodes-ha-gce-example-com" {
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.node.email email = google_service_account.node.email

View File

@ -458,6 +458,7 @@ resource "google_compute_instance_template" "master-us-test1-a-minimal-gce-examp
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -504,6 +505,7 @@ resource "google_compute_instance_template" "nodes-minimal-gce-example-com" {
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.node.email email = google_service_account.node.email

View File

@ -451,6 +451,7 @@ resource "google_compute_instance_template" "master-us-test1-a-minimal-gce-ilb-e
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -495,6 +496,7 @@ resource "google_compute_instance_template" "nodes-minimal-gce-ilb-example-com"
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.node.email email = google_service_account.node.email

View File

@ -459,6 +459,7 @@ resource "google_compute_instance_template" "master-us-test1-a-minimal-gce-with-
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -503,6 +504,7 @@ resource "google_compute_instance_template" "nodes-minimal-gce-with-a-very-very-
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.node.email email = google_service_account.node.email

View File

@ -458,6 +458,7 @@ resource "google_compute_instance_template" "master-us-test1-a-minimal-gce-with-
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -504,6 +505,7 @@ resource "google_compute_instance_template" "nodes-minimal-gce-with-a-very-very-
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.node.email email = google_service_account.node.email

View File

@ -448,6 +448,7 @@ resource "google_compute_instance_template" "master-us-test1-a-minimal-gce-priva
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.control-plane.email email = google_service_account.control-plane.email
@ -492,6 +493,7 @@ resource "google_compute_instance_template" "nodes-minimal-gce-private-example-c
automatic_restart = true automatic_restart = true
on_host_maintenance = "MIGRATE" on_host_maintenance = "MIGRATE"
preemptible = false preemptible = false
provisioning_model = "STANDARD"
} }
service_account { service_account {
email = google_service_account.node.email email = google_service_account.node.email

View File

@ -51,10 +51,11 @@ type InstanceTemplate struct {
Lifecycle fi.Lifecycle Lifecycle fi.Lifecycle
Network *Network Network *Network
Tags []string Tags []string
Labels map[string]string Labels map[string]string
Preemptible *bool Preemptible *bool
GCPProvisioningModel *string
BootDiskImage *string BootDiskImage *string
BootDiskSizeGB *int64 BootDiskSizeGB *int64
@ -132,6 +133,7 @@ func (e *InstanceTemplate) Find(c *fi.Context) (*InstanceTemplate, error) {
if p.Scheduling != nil { if p.Scheduling != nil {
actual.Preemptible = &p.Scheduling.Preemptible actual.Preemptible = &p.Scheduling.Preemptible
actual.GCPProvisioningModel = &p.Scheduling.ProvisioningModel
} }
if len(p.NetworkInterfaces) != 0 { if len(p.NetworkInterfaces) != 0 {
ni := p.NetworkInterfaces[0] ni := p.NetworkInterfaces[0]
@ -249,7 +251,7 @@ func (e *InstanceTemplate) mapToGCE(project string, region string) (*compute.Ins
scheduling = &compute.Scheduling{ scheduling = &compute.Scheduling{
AutomaticRestart: fi.Bool(false), AutomaticRestart: fi.Bool(false),
OnHostMaintenance: "TERMINATE", OnHostMaintenance: "TERMINATE",
ProvisioningModel: "STANDARD", // TODO: Support Spot? ProvisioningModel: fi.StringValue(e.GCPProvisioningModel),
Preemptible: true, Preemptible: true,
} }
} else { } else {
@ -501,6 +503,7 @@ type terraformScheduling struct {
AutomaticRestart bool `cty:"automatic_restart"` AutomaticRestart bool `cty:"automatic_restart"`
OnHostMaintenance string `cty:"on_host_maintenance"` OnHostMaintenance string `cty:"on_host_maintenance"`
Preemptible bool `cty:"preemptible"` Preemptible bool `cty:"preemptible"`
ProvisioningModel string `cty:"provisioning_model"`
} }
type terraformInstanceTemplateAttachedDisk struct { type terraformInstanceTemplateAttachedDisk struct {
@ -649,6 +652,7 @@ func (_ *InstanceTemplate) RenderTerraform(t *terraform.TerraformTarget, a, e, c
AutomaticRestart: fi.BoolValue(i.Properties.Scheduling.AutomaticRestart), AutomaticRestart: fi.BoolValue(i.Properties.Scheduling.AutomaticRestart),
OnHostMaintenance: i.Properties.Scheduling.OnHostMaintenance, OnHostMaintenance: i.Properties.Scheduling.OnHostMaintenance,
Preemptible: i.Properties.Scheduling.Preemptible, Preemptible: i.Properties.Scheduling.Preemptible,
ProvisioningModel: i.Properties.Scheduling.ProvisioningModel,
} }
} }