Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/cmd/lib_cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,13 @@ func confirmInstallClusterConfig(clusterConfig *clusterconfig.Config, awsCreds *

spotSuffix := ""
if clusterConfig.Spot != nil && *clusterConfig.Spot {
spotSuffix = " (not accounting for spot instances)"
spotSuffix = " (on-demand pricing)"
}

if *clusterConfig.MinInstances == *clusterConfig.MaxInstances {
fmt.Printf("this cluster will cost %s per hour%s\n\n", s.DollarsAndCents(totalMaxPrice), spotSuffix)
} else {
fmt.Printf("this cluster will cost %s per hour if the minimum number of instances are running and %s per hour if the maximum number of instances are running%s\n\n", s.DollarsAndCents(totalMinPrice), s.DollarsAndCents(totalMaxPrice), spotSuffix)
fmt.Printf("this cluster will cost %s - %s per hour based on the cluster size%s\n\n", s.DollarsAndCents(totalMinPrice), s.DollarsAndCents(totalMaxPrice), spotSuffix)
}

if clusterConfig.Spot != nil && *clusterConfig.Spot {
Expand Down
18 changes: 11 additions & 7 deletions pkg/lib/clusterconfig/clusterconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ func (cc *Config) Validate(accessKeyID string, secretAccessKey string) error {

if cc.Spot != nil && *cc.Spot {
chosenInstance := aws.InstanceMetadatas[*cc.Region][*cc.InstanceType]
compatibleSpots := CompatibleSpotInstances(accessKeyID, secretAccessKey, chosenInstance, _spotInstanceDistributionLength)
compatibleSpots := CompatibleSpotInstances(accessKeyID, secretAccessKey, chosenInstance, cc.SpotConfig.MaxPrice, _spotInstanceDistributionLength)
if len(compatibleSpots) == 0 {
return errors.Wrap(ErrorNoCompatibleSpotInstanceFound(chosenInstance.Type), InstanceTypeKey)
}
Expand All @@ -428,7 +428,7 @@ func (cc *Config) Validate(accessKeyID string, secretAccessKey string) error {
}

instanceMetadata := aws.InstanceMetadatas[*cc.Region][instanceType]
err := CheckSpotInstanceCompatibility(accessKeyID, secretAccessKey, chosenInstance, instanceMetadata)
err := CheckSpotInstanceCompatibility(accessKeyID, secretAccessKey, chosenInstance, instanceMetadata, cc.SpotConfig.MaxPrice)
if err != nil {
return errors.Wrap(err, InstanceDistributionKey)
}
Expand Down Expand Up @@ -473,7 +473,7 @@ func CheckCortexSupport(instanceMetadata aws.InstanceMetadata) error {
return nil
}

func CheckSpotInstanceCompatibility(accessKeyID string, secretAccessKey string, target aws.InstanceMetadata, suggested aws.InstanceMetadata) error {
func CheckSpotInstanceCompatibility(accessKeyID string, secretAccessKey string, target aws.InstanceMetadata, suggested aws.InstanceMetadata, maxPrice *float64) error {
if target.GPU > suggested.GPU {
return ErrorIncompatibleSpotInstanceTypeGPU(target, suggested)
}
Expand All @@ -491,13 +491,17 @@ func CheckSpotInstanceCompatibility(accessKeyID string, secretAccessKey string,
return err
}

if target.Price < suggestedInstancePrice {
if (maxPrice == nil || *maxPrice == target.Price) && target.Price < suggestedInstancePrice {
return ErrorSpotPriceGreaterThanTargetOnDemand(suggestedInstancePrice, target, suggested)
}

if maxPrice != nil && *maxPrice < suggestedInstancePrice {
return ErrorSpotPriceGreaterThanMaxPrice(suggestedInstancePrice, *maxPrice, suggested)
}
return nil
}

func CompatibleSpotInstances(accessKeyID string, secretAccessKey string, targetInstance aws.InstanceMetadata, numInstances int) []aws.InstanceMetadata {
func CompatibleSpotInstances(accessKeyID string, secretAccessKey string, targetInstance aws.InstanceMetadata, maxPrice *float64, numInstances int) []aws.InstanceMetadata {
compatibleInstances := []aws.InstanceMetadata{}
instanceMap := aws.InstanceMetadatas[targetInstance.Region]
availableInstances := []aws.InstanceMetadata{}
Expand All @@ -518,7 +522,7 @@ func CompatibleSpotInstances(accessKeyID string, secretAccessKey string, targetI
continue
}

if err := CheckSpotInstanceCompatibility(accessKeyID, secretAccessKey, targetInstance, instanceMetadata); err != nil {
if err := CheckSpotInstanceCompatibility(accessKeyID, secretAccessKey, targetInstance, instanceMetadata, maxPrice); err != nil {
continue
}

Expand All @@ -537,7 +541,7 @@ func AutoGenerateSpotConfig(accessKeyID string, secretAccessKey string, spotConf
if len(spotConfig.InstanceDistribution) == 0 {
spotConfig.InstanceDistribution = append(spotConfig.InstanceDistribution, chosenInstance.Type)

compatibleSpots := CompatibleSpotInstances(accessKeyID, secretAccessKey, chosenInstance, _spotInstanceDistributionLength)
compatibleSpots := CompatibleSpotInstances(accessKeyID, secretAccessKey, chosenInstance, spotConfig.MaxPrice, _spotInstanceDistributionLength)
if len(compatibleSpots) == 0 {
return errors.Wrap(ErrorNoCompatibleSpotInstanceFound(chosenInstance.Type), InstanceTypeKey)
}
Expand Down
11 changes: 10 additions & 1 deletion pkg/lib/clusterconfig/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (
ErrIncompatibleSpotInstanceTypeCPU
ErrIncompatibleSpotInstanceTypeGPU
ErrSpotPriceGreaterThanTargetOnDemand
ErrSpotPriceGreaterThanMaxPrice
ErrInstanceTypeNotSupported
ErrAtLeastOneInstanceDistribution
ErrNoCompatibleSpotInstanceFound
Expand All @@ -58,6 +59,7 @@ var (
"err_incompatible_spot_instance_type_cpu",
"err_incompatible_spot_instance_type_gpu",
"err_spot_price_greater_than_target_on_demand",
"err_spot_price_greater_than_max_price",
"err_instance_type_not_supported",
"err_at_least_one_instance_distribution",
"err_no_compatible_spot_instance_found",
Expand Down Expand Up @@ -166,7 +168,14 @@ func ErrorIncompatibleSpotInstanceTypeGPU(target aws.InstanceMetadata, suggested
func ErrorSpotPriceGreaterThanTargetOnDemand(suggestedSpotPrice float64, target aws.InstanceMetadata, suggested aws.InstanceMetadata) error {
return errors.WithStack(Error{
Kind: ErrSpotPriceGreaterThanTargetOnDemand,
message: fmt.Sprintf("%s will not be allocated because its current spot price is %g which is greater than than %s's on-demand price of %g", suggested.Type, suggestedSpotPrice, target.Type, target.Price),
message: fmt.Sprintf("%s will not be allocated because its current spot price is $%g which is greater than than %s's on-demand price of $%g", suggested.Type, suggestedSpotPrice, target.Type, target.Price),
})
}

func ErrorSpotPriceGreaterThanMaxPrice(suggestedSpotPrice float64, maxPrice float64, suggested aws.InstanceMetadata) error {
return errors.WithStack(Error{
Kind: ErrSpotPriceGreaterThanMaxPrice,
message: fmt.Sprintf("%s will not be allocated because its current spot price is $%g which is greater than the configured max price $%g", suggested.Type, suggestedSpotPrice, maxPrice),
})
}

Expand Down