14360: Merge branch 'master' into 14360-dispatch-cloud
[arvados.git] / lib / dispatchcloud / node_size.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package dispatchcloud
6
7 import (
8         "errors"
9         "sort"
10
11         "git.curoverse.com/arvados.git/sdk/go/arvados"
12 )
13
14 var ErrInstanceTypesNotConfigured = errors.New("site configuration does not list any instance types")
15
16 var discountConfiguredRAMPercent = 5
17
18 // ConstraintsNotSatisfiableError includes a list of available instance types
19 // to be reported back to the user.
20 type ConstraintsNotSatisfiableError struct {
21         error
22         AvailableTypes []arvados.InstanceType
23 }
24
25 // ChooseInstanceType returns the cheapest available
26 // arvados.InstanceType big enough to run ctr.
27 func ChooseInstanceType(cc *arvados.Cluster, ctr *arvados.Container) (best arvados.InstanceType, err error) {
28         if len(cc.InstanceTypes) == 0 {
29                 err = ErrInstanceTypesNotConfigured
30                 return
31         }
32
33         needScratch := int64(0)
34         for _, m := range ctr.Mounts {
35                 if m.Kind == "tmp" {
36                         needScratch += m.Capacity
37                 }
38         }
39
40         needVCPUs := ctr.RuntimeConstraints.VCPUs
41
42         needRAM := ctr.RuntimeConstraints.RAM + ctr.RuntimeConstraints.KeepCacheRAM
43         needRAM = (needRAM * 100) / int64(100-discountConfiguredRAMPercent)
44
45         ok := false
46         for _, it := range cc.InstanceTypes {
47                 switch {
48                 case ok && it.Price > best.Price:
49                 case int64(it.Scratch) < needScratch:
50                 case int64(it.RAM) < needRAM:
51                 case it.VCPUs < needVCPUs:
52                 case it.Preemptible != ctr.SchedulingParameters.Preemptible:
53                 case it.Price == best.Price && (it.RAM < best.RAM || it.VCPUs < best.VCPUs):
54                         // Equal price, but worse specs
55                 default:
56                         // Lower price || (same price && better specs)
57                         best = it
58                         ok = true
59                 }
60         }
61         if !ok {
62                 availableTypes := make([]arvados.InstanceType, 0, len(cc.InstanceTypes))
63                 for _, t := range cc.InstanceTypes {
64                         availableTypes = append(availableTypes, t)
65                 }
66                 sort.Slice(availableTypes, func(a, b int) bool {
67                         return availableTypes[a].Price < availableTypes[b].Price
68                 })
69                 err = ConstraintsNotSatisfiableError{
70                         errors.New("constraints not satisfiable by any configured instance type"),
71                         availableTypes,
72                 }
73                 return
74         }
75         return
76 }