diff --git a/cmd/openshift-install/features.go b/cmd/openshift-install/features.go new file mode 100644 index 00000000000..2b5650f73a8 --- /dev/null +++ b/cmd/openshift-install/features.go @@ -0,0 +1,42 @@ +package main + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + outputJSON = false + hiddenFeatures = []string{ + "terraform-spot-masters", + } +) + +func newListFeaturesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-hidden-features", + Short: "List supported hidden features", + Long: "", + Hidden: true, + Args: cobra.ExactArgs(0), + Run: func(_ *cobra.Command, _ []string) { + var out string + if outputJSON { + outb, err := json.Marshal(hiddenFeatures) + if err != nil { + logrus.Fatalf("failed to marshal output: %s", err.Error()) + } + out = string(outb) + } else { + out = strings.Join(hiddenFeatures, "\n") + } + fmt.Println(out) + }, + } + cmd.PersistentFlags().BoolVar(&outputJSON, "json", false, "print output in json format") + return cmd +} diff --git a/cmd/openshift-install/main.go b/cmd/openshift-install/main.go index 97228eaab8c..b147ea604b8 100644 --- a/cmd/openshift-install/main.go +++ b/cmd/openshift-install/main.go @@ -55,6 +55,7 @@ func installerMain() { newCompletionCmd(), newExplainCmd(), newAgentCmd(ctx), + newListFeaturesCmd(), } { rootCmd.AddCommand(subCmd) } diff --git a/data/data/aws/bootstrap/main.tf b/data/data/aws/bootstrap/main.tf index 963b662cf9a..e3a0954892d 100644 --- a/data/data/aws/bootstrap/main.tf +++ b/data/data/aws/bootstrap/main.tf @@ -163,6 +163,16 @@ resource "aws_instance" "bootstrap" { vpc_security_group_ids = [var.master_sg_id, aws_security_group.bootstrap.id] associate_public_ip_address = local.public_endpoints && var.aws_public_ipv4_pool == "" + dynamic "instance_market_options" { + for_each = var.aws_master_use_spot_instance ? [1] : [] + content { + market_type = "spot" + spot_options { + spot_instance_type = "one-time" + } + } + } + lifecycle { # Ignore changes in the AMI which force recreation of the resource. This # avoids accidental deletion of nodes whenever a new OS release comes out. diff --git a/data/data/aws/cluster/main.tf b/data/data/aws/cluster/main.tf index e6ebcf6b81a..5912484aef8 100644 --- a/data/data/aws/cluster/main.tf +++ b/data/data/aws/cluster/main.tf @@ -55,6 +55,7 @@ module "masters" { user_data_ign = var.ignition_master publish_strategy = var.aws_publish_strategy iam_role_name = var.aws_master_iam_role_name + use_spot_instance = var.aws_master_use_spot_instance } module "iam" { diff --git a/data/data/aws/cluster/master/main.tf b/data/data/aws/cluster/master/main.tf index 6a61e831c6e..22c198327c1 100644 --- a/data/data/aws/cluster/master/main.tf +++ b/data/data/aws/cluster/master/main.tf @@ -146,6 +146,16 @@ resource "aws_instance" "master" { device_index = 0 } + dynamic "instance_market_options" { + for_each = var.use_spot_instance ? [1] : [] + content { + market_type = "spot" + spot_options { + spot_instance_type = "one-time" + } + } + } + lifecycle { # Ignore changes in the AMI which force recreation of the resource. This # avoids accidental deletion of nodes whenever a new CoreOS Release comes diff --git a/data/data/aws/cluster/master/variables.tf b/data/data/aws/cluster/master/variables.tf index 1d785767aff..34638960e7e 100644 --- a/data/data/aws/cluster/master/variables.tf +++ b/data/data/aws/cluster/master/variables.tf @@ -101,3 +101,8 @@ variable "iam_role_name" { type = string description = "The name of the existing role to use for the instance profile" } + +variable "use_spot_instance" { + type = bool + description = "Whether to use Spot instances for masters" +} diff --git a/data/data/aws/variables-aws.tf b/data/data/aws/variables-aws.tf index 57c9c0262f3..8a8a9a842f8 100644 --- a/data/data/aws/variables-aws.tf +++ b/data/data/aws/variables-aws.tf @@ -239,3 +239,9 @@ EOF default = "" } + +variable "aws_master_use_spot_instance" { + type = bool + description = "(optional) Whether to use instances from the Spot market for masters" + default = false +} diff --git a/pkg/tfvars/aws/aws.go b/pkg/tfvars/aws/aws.go index 2c2280b7b6d..1d9ba7ad87d 100644 --- a/pkg/tfvars/aws/aws.go +++ b/pkg/tfvars/aws/aws.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/pkg/errors" + "github.com/sirupsen/logrus" machinev1beta1 "github.com/openshift/api/machine/v1beta1" "github.com/openshift/installer/pkg/asset/ignition/bootstrap" @@ -50,6 +51,7 @@ type Config struct { PreserveBootstrapIgnition bool `json:"aws_preserve_bootstrap_ignition"` MasterSecurityGroups []string `json:"aws_master_security_groups,omitempty"` PublicIpv4Pool string `json:"aws_public_ipv4_pool"` + MasterUseSpotInstance bool `json:"aws_master_use_spot_instance,omitempty"` } // TFVarsSources contains the parameters to be converted into Terraform variables @@ -187,6 +189,11 @@ func TFVars(sources TFVarsSources) ([]byte, error) { return nil, errors.New("EBS IOPS must be configured for the io1 root volume") } + useSpotInstances := masterConfig.SpotMarketOptions != nil + if useSpotInstances { + logrus.Warn("Found Spot instance configuration. Please be warned, this is not advised.") + } + cfg := &Config{ CustomEndpoints: endpoints, Region: masterConfig.Placement.Region, @@ -211,6 +218,7 @@ func TFVars(sources TFVarsSources) ([]byte, error) { PreserveBootstrapIgnition: sources.PreserveBootstrapIgnition, MasterSecurityGroups: sources.MasterSecurityGroups, PublicIpv4Pool: sources.PublicIpv4Pool, + MasterUseSpotInstance: useSpotInstances, } stubIgn, err := bootstrap.GenerateIgnitionShimWithCertBundleAndProxy(sources.IgnitionPresignedURL, sources.AdditionalTrustBundle, sources.Proxy)