]> git.arvados.org - arvados.git/blob - lib/dispatchcloud/node_size_test.go
23044: De-dup ContainerWebServices routing logic.
[arvados.git] / lib / dispatchcloud / node_size_test.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         "git.arvados.org/arvados.git/sdk/go/arvados"
9         check "gopkg.in/check.v1"
10 )
11
12 var _ = check.Suite(&NodeSizeSuite{})
13
14 const GiB = arvados.ByteSize(1 << 30)
15
16 type NodeSizeSuite struct{}
17
18 func (*NodeSizeSuite) TestChooseNotConfigured(c *check.C) {
19         _, err := ChooseInstanceType(&arvados.Cluster{}, &arvados.Container{
20                 RuntimeConstraints: arvados.RuntimeConstraints{
21                         RAM:   1234567890,
22                         VCPUs: 2,
23                 },
24         })
25         c.Check(err, check.Equals, ErrInstanceTypesNotConfigured)
26 }
27
28 func (*NodeSizeSuite) TestChooseUnsatisfiable(c *check.C) {
29         checkUnsatisfiable := func(ctr *arvados.Container) {
30                 _, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: map[string]arvados.InstanceType{
31                         "small1": {Price: 1.1, RAM: 1000000000, VCPUs: 2, Name: "small1"},
32                         "small2": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Name: "small2"},
33                         "small4": {Price: 4.4, RAM: 4000000000, VCPUs: 8, Name: "small4", Scratch: GiB},
34                 }}, ctr)
35                 c.Check(err, check.FitsTypeOf, ConstraintsNotSatisfiableError{})
36         }
37
38         for _, rc := range []arvados.RuntimeConstraints{
39                 {RAM: 9876543210, VCPUs: 2},
40                 {RAM: 1234567890, VCPUs: 20},
41                 {RAM: 1234567890, VCPUs: 2, KeepCacheRAM: 9876543210},
42         } {
43                 checkUnsatisfiable(&arvados.Container{RuntimeConstraints: rc})
44         }
45         checkUnsatisfiable(&arvados.Container{
46                 Mounts:             map[string]arvados.Mount{"/tmp": {Kind: "tmp", Capacity: int64(2 * GiB)}},
47                 RuntimeConstraints: arvados.RuntimeConstraints{RAM: 12345, VCPUs: 1},
48         })
49 }
50
51 func (*NodeSizeSuite) TestChoose(c *check.C) {
52         for _, menu := range []map[string]arvados.InstanceType{
53                 {
54                         "costly": {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly"},
55                         "best":   {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
56                         "small":  {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: 2 * GiB, Name: "small"},
57                 },
58                 {
59                         "costly":     {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly"},
60                         "goodenough": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "goodenough"},
61                         "best":       {Price: 2.2, RAM: 4000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
62                         "small":      {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: 2 * GiB, Name: "small"},
63                 },
64                 {
65                         "small":      {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: 2 * GiB, Name: "small"},
66                         "goodenough": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "goodenough"},
67                         "best":       {Price: 2.2, RAM: 4000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
68                         "costly":     {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly"},
69                 },
70                 {
71                         "small":  {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: GiB, Name: "small"},
72                         "nearly": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: GiB, Name: "nearly"},
73                         "best":   {Price: 3.3, RAM: 4000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
74                         "costly": {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly"},
75                 },
76                 {
77                         "small":  {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: GiB, Name: "small"},
78                         "nearly": {Price: 2.2, RAM: 1200000000, VCPUs: 4, Scratch: 2 * GiB, Name: "nearly"},
79                         "best":   {Price: 3.3, RAM: 4000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
80                         "costly": {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly"},
81                 },
82         } {
83                 best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu, Containers: arvados.ContainersConfig{
84                         LocalKeepBlobBuffersPerVCPU: 1,
85                         ReserveExtraRAM:             268435456,
86                 }}, &arvados.Container{
87                         Mounts: map[string]arvados.Mount{
88                                 "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
89                         },
90                         RuntimeConstraints: arvados.RuntimeConstraints{
91                                 VCPUs:        2,
92                                 RAM:          987654321,
93                                 KeepCacheRAM: 123456789,
94                         },
95                 })
96                 c.Assert(err, check.IsNil)
97                 c.Assert(best, check.Not(check.HasLen), 0)
98                 c.Check(best[0].Name, check.Equals, "best")
99                 c.Check(best[0].RAM >= 1234567890, check.Equals, true)
100                 c.Check(best[0].VCPUs >= 2, check.Equals, true)
101                 c.Check(best[0].Scratch >= 2*GiB, check.Equals, true)
102                 for i := range best {
103                         // If multiple instance types are returned
104                         // then they should all have the same price,
105                         // because we didn't set MaximumPriceFactor>1.
106                         c.Check(best[i].Price, check.Equals, best[0].Price)
107                 }
108         }
109 }
110
111 func (*NodeSizeSuite) TestMaximumPriceFactor(c *check.C) {
112         menu := map[string]arvados.InstanceType{
113                 "best+7":  {Price: 3.4, RAM: 8000000000, VCPUs: 8, Scratch: 64 * GiB, Name: "best+7"},
114                 "best+5":  {Price: 3.0, RAM: 8000000000, VCPUs: 8, Scratch: 16 * GiB, Name: "best+5"},
115                 "best+3":  {Price: 2.6, RAM: 4000000000, VCPUs: 8, Scratch: 16 * GiB, Name: "best+3"},
116                 "best+2":  {Price: 2.4, RAM: 4000000000, VCPUs: 8, Scratch: 4 * GiB, Name: "best+2"},
117                 "best+1":  {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 4 * GiB, Name: "best+1"},
118                 "best":    {Price: 2.0, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
119                 "small+1": {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: 16 * GiB, Name: "small+1"},
120                 "small":   {Price: 1.0, RAM: 2000000000, VCPUs: 2, Scratch: 1 * GiB, Name: "small"},
121         }
122         best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu, Containers: arvados.ContainersConfig{
123                 MaximumPriceFactor: 1.5,
124         }}, &arvados.Container{
125                 Mounts: map[string]arvados.Mount{
126                         "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
127                 },
128                 RuntimeConstraints: arvados.RuntimeConstraints{
129                         VCPUs:        2,
130                         RAM:          987654321,
131                         KeepCacheRAM: 123456789,
132                 },
133         })
134         c.Assert(err, check.IsNil)
135         c.Assert(best, check.HasLen, 5)
136         c.Check(best[0].Name, check.Equals, "best") // best price is $2
137         c.Check(best[1].Name, check.Equals, "best+1")
138         c.Check(best[2].Name, check.Equals, "best+2")
139         c.Check(best[3].Name, check.Equals, "best+3")
140         c.Check(best[4].Name, check.Equals, "best+5") // max price is $2 * 1.5 = $3
141 }
142
143 func (*NodeSizeSuite) TestChooseWithBlobBuffersOverhead(c *check.C) {
144         menu := map[string]arvados.InstanceType{
145                 "nearly": {Price: 2.2, RAM: 4000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "small"},
146                 "best":   {Price: 3.3, RAM: 8000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
147                 "costly": {Price: 4.4, RAM: 12000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly"},
148         }
149         best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu, Containers: arvados.ContainersConfig{
150                 LocalKeepBlobBuffersPerVCPU: 16, // 1 GiB per vcpu => 2 GiB
151                 ReserveExtraRAM:             268435456,
152         }}, &arvados.Container{
153                 Mounts: map[string]arvados.Mount{
154                         "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
155                 },
156                 RuntimeConstraints: arvados.RuntimeConstraints{
157                         VCPUs:        2,
158                         RAM:          987654321,
159                         KeepCacheRAM: 123456789,
160                 },
161         })
162         c.Check(err, check.IsNil)
163         c.Assert(best, check.HasLen, 1)
164         c.Check(best[0].Name, check.Equals, "best")
165 }
166
167 func (*NodeSizeSuite) TestChoosePreemptible(c *check.C) {
168         menu := map[string]arvados.InstanceType{
169                 "costly":      {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Preemptible: true, Name: "costly"},
170                 "almost best": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "almost best"},
171                 "best":        {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Preemptible: true, Name: "best"},
172                 "small":       {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: 2 * GiB, Preemptible: true, Name: "small"},
173         }
174         best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu}, &arvados.Container{
175                 Mounts: map[string]arvados.Mount{
176                         "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
177                 },
178                 RuntimeConstraints: arvados.RuntimeConstraints{
179                         VCPUs:        2,
180                         RAM:          987654321,
181                         KeepCacheRAM: 123456789,
182                 },
183                 SchedulingParameters: arvados.SchedulingParameters{
184                         Preemptible: true,
185                 },
186         })
187         c.Check(err, check.IsNil)
188         c.Assert(best, check.HasLen, 1)
189         c.Check(best[0].Name, check.Equals, "best")
190         c.Check(best[0].RAM >= 1234567890, check.Equals, true)
191         c.Check(best[0].VCPUs >= 2, check.Equals, true)
192         c.Check(best[0].Scratch >= 2*GiB, check.Equals, true)
193         c.Check(best[0].Preemptible, check.Equals, true)
194 }
195
196 func (*NodeSizeSuite) TestScratchForDockerImage(c *check.C) {
197         n := EstimateScratchSpace(&arvados.Container{
198                 ContainerImage: "d5025c0f29f6eef304a7358afa82a822+342",
199         })
200         // Actual image is 371.1 MiB (according to workbench)
201         // Estimated size is 384 MiB (402653184 bytes)
202         // Want to reserve 2x the estimated size, so 805306368 bytes
203         c.Check(n, check.Equals, int64(805306368))
204
205         n = EstimateScratchSpace(&arvados.Container{
206                 ContainerImage: "d5025c0f29f6eef304a7358afa82a822+-342",
207         })
208         // Parse error will return 0
209         c.Check(n, check.Equals, int64(0))
210
211         n = EstimateScratchSpace(&arvados.Container{
212                 ContainerImage: "d5025c0f29f6eef304a7358afa82a822+34",
213         })
214         // Short manifest will return 0
215         c.Check(n, check.Equals, int64(0))
216 }
217
218 func (*NodeSizeSuite) TestChooseGPU(c *check.C) {
219         menu := map[string]arvados.InstanceType{
220                 "costly": {Price: 4.4, RAM: 4 * GiB, VCPUs: 8, Scratch: 2 * GiB, Name: "costly",
221                         GPU: arvados.GPUFeatures{Stack: "cuda", DeviceCount: 2, HardwareTarget: "9.0", DriverVersion: "11.0", VRAM: 2 * GiB}},
222
223                 "low_capability": {Price: 2.1, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "low_capability",
224                         GPU: arvados.GPUFeatures{Stack: "cuda", DeviceCount: 1, HardwareTarget: "8.0", DriverVersion: "11.0", VRAM: 2 * GiB}},
225
226                 "best": {Price: 2.2, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "best",
227                         GPU: arvados.GPUFeatures{Stack: "cuda", DeviceCount: 1, HardwareTarget: "9.0", DriverVersion: "11.0", VRAM: 2 * GiB}},
228
229                 "low_driver": {Price: 2.1, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "low_driver",
230                         GPU: arvados.GPUFeatures{Stack: "cuda", DeviceCount: 1, HardwareTarget: "9.0", DriverVersion: "10.0", VRAM: 2 * GiB}},
231
232                 "cheap_gpu": {Price: 2.0, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "cheap_gpu",
233                         GPU: arvados.GPUFeatures{Stack: "cuda", DeviceCount: 1, HardwareTarget: "8.0", DriverVersion: "10.0", VRAM: 2 * GiB}},
234
235                 "more_vram": {Price: 2.3, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "more_vram",
236                         GPU: arvados.GPUFeatures{Stack: "cuda", DeviceCount: 1, HardwareTarget: "8.0", DriverVersion: "10.0", VRAM: 8 * GiB}},
237
238                 "invalid_gpu": {Price: 1.9, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "invalid_gpu",
239                         GPU: arvados.GPUFeatures{Stack: "cuda", DeviceCount: 1, HardwareTarget: "12.0.12", DriverVersion: "12.0.12", VRAM: 2 * GiB}},
240
241                 "gpu_rocm": {Price: 2.0, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "gpu_rocm",
242                         GPU: arvados.GPUFeatures{Stack: "rocm", DeviceCount: 1, HardwareTarget: "gfx1100", DriverVersion: "6.2", VRAM: 20 * GiB}},
243
244                 "cheap_gpu_rocm": {Price: 1.9, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "cheap_gpu_rocm",
245                         GPU: arvados.GPUFeatures{Stack: "rocm", DeviceCount: 1, HardwareTarget: "gfx1103", DriverVersion: "6.2", VRAM: 8 * GiB}},
246
247                 "unspecified_vram": {Price: 2.0, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "unspecified_vram",
248                         GPU: arvados.GPUFeatures{Stack: "rocm", DeviceCount: 1, HardwareTarget: "gfx1104", DriverVersion: "6.2", VRAM: 0}},
249
250                 "non_gpu": {Price: 1.1, RAM: 2 * GiB, VCPUs: 4, Scratch: 2 * GiB, Name: "non_gpu"},
251         }
252
253         type GPUTestCase struct {
254                 GPU              arvados.GPURuntimeConstraints
255                 SelectedInstance string
256         }
257         cases := []GPUTestCase{
258                 GPUTestCase{
259                         GPU: arvados.GPURuntimeConstraints{
260                                 Stack:          "cuda",
261                                 DeviceCount:    1,
262                                 HardwareTarget: []string{"9.0"},
263                                 DriverVersion:  "11.0",
264                                 VRAM:           2000000000,
265                         },
266                         SelectedInstance: "best",
267                 },
268                 GPUTestCase{
269                         GPU: arvados.GPURuntimeConstraints{
270                                 Stack:          "cuda",
271                                 DeviceCount:    2,
272                                 HardwareTarget: []string{"9.0"},
273                                 DriverVersion:  "11.0",
274                                 VRAM:           2000000000,
275                         },
276                         SelectedInstance: "costly",
277                 },
278                 GPUTestCase{
279                         GPU: arvados.GPURuntimeConstraints{
280                                 Stack:          "cuda",
281                                 DeviceCount:    1,
282                                 HardwareTarget: []string{"8.0"},
283                                 DriverVersion:  "11.0",
284                                 VRAM:           2000000000,
285                         },
286                         SelectedInstance: "low_capability",
287                 },
288                 GPUTestCase{
289                         GPU: arvados.GPURuntimeConstraints{
290                                 Stack:          "cuda",
291                                 DeviceCount:    1,
292                                 HardwareTarget: []string{"9.0"},
293                                 DriverVersion:  "10.0",
294                                 VRAM:           2000000000,
295                         },
296                         SelectedInstance: "low_driver",
297                 },
298                 GPUTestCase{
299                         GPU: arvados.GPURuntimeConstraints{
300                                 Stack:          "cuda",
301                                 DeviceCount:    1,
302                                 HardwareTarget: []string{"8.0"},
303                                 DriverVersion:  "11.0",
304                                 VRAM:           8000000000,
305                         },
306                         SelectedInstance: "more_vram",
307                 },
308                 GPUTestCase{
309                         GPU: arvados.GPURuntimeConstraints{
310                                 Stack:          "cuda",
311                                 DeviceCount:    1,
312                                 HardwareTarget: []string{},
313                                 DriverVersion:  "10.0",
314                                 VRAM:           2000000000,
315                         },
316                         SelectedInstance: "",
317                 },
318                 GPUTestCase{
319                         GPU: arvados.GPURuntimeConstraints{
320                                 Stack:          "rocm",
321                                 DeviceCount:    1,
322                                 HardwareTarget: []string{"gfx1100"},
323                                 DriverVersion:  "6.2",
324                                 VRAM:           2000000000,
325                         },
326                         SelectedInstance: "gpu_rocm",
327                 },
328                 GPUTestCase{
329                         GPU: arvados.GPURuntimeConstraints{
330                                 Stack:          "rocm",
331                                 DeviceCount:    1,
332                                 HardwareTarget: []string{"gfx1100", "gfx1103"},
333                                 DriverVersion:  "6.2",
334                                 VRAM:           2000000000,
335                         },
336                         SelectedInstance: "cheap_gpu_rocm",
337                 },
338                 GPUTestCase{
339                         GPU: arvados.GPURuntimeConstraints{
340                                 Stack:          "rocm",
341                                 DeviceCount:    1,
342                                 HardwareTarget: []string{"gfx1104"},
343                                 DriverVersion:  "6.2",
344                                 VRAM:           2000000000,
345                         },
346                         SelectedInstance: "unspecified_vram",
347                 },
348                 GPUTestCase{
349                         GPU: arvados.GPURuntimeConstraints{
350                                 Stack:          "",
351                                 DeviceCount:    0,
352                                 HardwareTarget: []string{""},
353                                 DriverVersion:  "",
354                                 VRAM:           0,
355                         },
356                         SelectedInstance: "non_gpu",
357                 },
358         }
359
360         for _, tc := range cases {
361                 best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu}, &arvados.Container{
362                         Mounts: map[string]arvados.Mount{
363                                 "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
364                         },
365                         RuntimeConstraints: arvados.RuntimeConstraints{
366                                 VCPUs:        2,
367                                 RAM:          987654321,
368                                 KeepCacheRAM: 123456789,
369                                 GPU:          tc.GPU,
370                         },
371                 })
372                 if len(best) > 0 {
373                         c.Check(err, check.IsNil)
374                         c.Assert(best, check.HasLen, 1)
375                         c.Check(best[0].Name, check.Equals, tc.SelectedInstance)
376                 } else {
377                         c.Check(err, check.Not(check.IsNil))
378                 }
379         }
380 }