@@ -38,15 +38,28 @@ const (
3838 GPUConstraint ConstraintType = "gpu"
3939)
4040
41- // A Constraint describes the hardware constraints for a given size. At the moment we only
42- // consider the cpu cores and the memory.
41+ var allConstraintTypes = []ConstraintType {CoreConstraint , MemoryConstraint , StorageConstraint , GPUConstraint }
42+
43+ // A Constraint describes the hardware constraints for a given size.
4344type Constraint struct {
4445 Type ConstraintType `rethinkdb:"type" json:"type"`
4546 Min uint64 `rethinkdb:"min" json:"min"`
4647 Max uint64 `rethinkdb:"max" json:"max"`
4748 Identifier string `rethinkdb:"identifier" json:"identifier" description:"glob of the identifier of this type"`
4849}
4950
51+ func countCPU (cpu MetalCPU ) (model string , count uint64 ) {
52+ return cpu .Model , uint64 (cpu .Cores )
53+ }
54+
55+ func countGPU (gpu MetalGPU ) (model string , count uint64 ) {
56+ return gpu .Model , 1
57+ }
58+
59+ func countDisk (disk BlockDevice ) (model string , count uint64 ) {
60+ return disk .Name , disk .Size
61+ }
62+
5063// Sizes is a list of sizes.
5164type Sizes []Size
5265
@@ -72,55 +85,85 @@ func UnknownSize() *Size {
7285 }
7386}
7487
75- // Matches returns true if the given machine hardware is inside the min/max values of the
88+ func (c * Constraint ) inRange (value uint64 ) bool {
89+ return value >= c .Min && value <= c .Max
90+ }
91+
92+ // matches returns true if the given machine hardware is inside the min/max values of the
7693// constraint.
77- func (c * Constraint ) Matches (hw MachineHardware ) bool {
94+ func (c * Constraint ) matches (hw MachineHardware ) bool {
7895 res := false
7996 switch c .Type {
8097 case CoreConstraint :
81- res = uint64 (hw .CPUCores ) >= c .Min && uint64 (hw .CPUCores ) <= c .Max
98+ cores , _ := capacityOf (c .Identifier , hw .MetalCPUs , countCPU )
99+ res = c .inRange (cores )
82100 case MemoryConstraint :
83- res = hw . Memory >= c . Min && hw .Memory <= c . Max
101+ res = c . inRange ( hw .Memory )
84102 case StorageConstraint :
85- res = hw .DiskCapacity () >= c .Min && hw .DiskCapacity () <= c .Max
103+ capacity , _ := capacityOf (c .Identifier , hw .Disks , countDisk )
104+ res = c .inRange (capacity )
86105 case GPUConstraint :
87- for model , count := range hw .GPUModels () {
88- idMatches , err := filepath .Match (c .Identifier , model )
89- if err != nil {
90- return false
91- }
92- res = count >= c .Min && count <= c .Max && idMatches
93- if res {
94- break
95- }
96- }
97-
106+ count , _ := capacityOf (c .Identifier , hw .MetalGPUs , countGPU )
107+ res = c .inRange (count )
98108 }
99109 return res
100110}
101111
112+ // matches returns true if all provided disks and later GPUs are covered with at least one constraint.
113+ // With this we ensure that hardware matches exhaustive against the constraints.
114+ func (hw * MachineHardware ) matches (constraints []Constraint , constraintType ConstraintType ) bool {
115+ filtered := lo .Filter (constraints , func (c Constraint , _ int ) bool { return c .Type == constraintType })
116+ if len (filtered ) == 0 {
117+ return true
118+ }
119+
120+ switch constraintType {
121+ case StorageConstraint :
122+ return exhaustiveMatch (filtered , hw .Disks , countDisk )
123+ case GPUConstraint :
124+ return exhaustiveMatch (filtered , hw .MetalGPUs , countGPU )
125+ case CoreConstraint :
126+ return exhaustiveMatch (filtered , hw .MetalCPUs , countCPU )
127+ case MemoryConstraint :
128+ // Noop because we do not have different Memory types
129+ return true
130+ default :
131+ return true
132+ }
133+ }
134+
102135// FromHardware searches a Size for given hardware specs. It will search
103136// for a size where the constraints matches the given hardware.
104137func (sz Sizes ) FromHardware (hardware MachineHardware ) (* Size , error ) {
105- var found []Size
138+ var (
139+ matchedSizes []Size
140+ )
141+
106142nextsize:
107143 for _ , s := range sz {
108144 for _ , c := range s .Constraints {
109- match := c .Matches (hardware )
145+ match := c .matches (hardware )
146+ if ! match {
147+ continue nextsize
148+ }
149+ }
150+
151+ for _ , ct := range allConstraintTypes {
152+ match := hardware .matches (s .Constraints , ct )
110153 if ! match {
111154 continue nextsize
112155 }
113156 }
114- found = append (found , s )
157+ matchedSizes = append (matchedSizes , s )
115158 }
116159
117- if len (found ) == 0 {
160+ if len (matchedSizes ) == 0 {
118161 return nil , NotFound ("no size found for hardware (%s)" , hardware .ReadableSpec ())
119162 }
120- if len (found ) > 1 {
121- return nil , fmt .Errorf ("%d sizes found for hardware (%s)" , len (found ), hardware .ReadableSpec ())
163+ if len (matchedSizes ) > 1 {
164+ return nil , fmt .Errorf ("%d sizes found for hardware (%s)" , len (matchedSizes ), hardware .ReadableSpec ())
122165 }
123- return & found [0 ], nil
166+ return & matchedSizes [0 ], nil
124167}
125168
126169func (s * Size ) overlaps (so * Size ) bool {
@@ -172,23 +215,35 @@ func (c *Constraint) overlaps(other Constraint) bool {
172215
173216// Validate a size, returns error if a invalid size is passed
174217func (s * Size ) Validate (partitions PartitionMap , projects map [string ]* mdmv1.Project ) error {
175- constraintTypes := map [ConstraintType ]bool {}
218+ constraintTypes := map [ConstraintType ]uint {}
176219 for _ , c := range s .Constraints {
177220 if c .Max < c .Min {
178221 return fmt .Errorf ("size:%q type:%q max:%d is smaller than min:%d" , s .ID , c .Type , c .Max , c .Min )
179222 }
180223
181- _ , ok := constraintTypes [c .Type ]
182- if ok {
183- return fmt .Errorf ("size:%q type:%q min:%d max:%d has duplicate constraint type" , s .ID , c .Type , c .Min , c .Max )
224+ // CPU and Memory Constraints are not allowed more than once
225+ constraintTypes [c .Type ]++
226+ count := constraintTypes [c .Type ]
227+ if c .Type == CoreConstraint || c .Type == MemoryConstraint {
228+ if count > 1 {
229+ return fmt .Errorf ("size:%q type:%q min:%d max:%d has duplicate constraint type" , s .ID , c .Type , c .Min , c .Max )
230+ }
184231 }
185232
186233 // Ensure GPU Constraints always have identifier specified
187234 if c .Type == GPUConstraint && c .Identifier == "" {
188235 return fmt .Errorf ("size:%q type:%q min:%d max:%d is a gpu size but has no identifier specified" , s .ID , c .Type , c .Min , c .Max )
189236 }
190237
191- constraintTypes [c .Type ] = true
238+ // Ensure Memory Constraints do not have a identifier specified
239+ if c .Type == MemoryConstraint && c .Identifier != "" {
240+ return fmt .Errorf ("size:%q type:%q min:%d max:%d is a memory size but has a identifier specified" , s .ID , c .Type , c .Min , c .Max )
241+ }
242+
243+ if _ , err := filepath .Match (c .Identifier , "" ); err != nil {
244+ return fmt .Errorf ("size:%q type:%q min:%d max:%d identifier:%q identifier is malformed:%w" , s .ID , c .Type , c .Min , c .Max , c .Identifier , err )
245+ }
246+
192247 }
193248
194249 if err := s .Reservations .Validate (partitions , projects ); err != nil {
0 commit comments