1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
8 "git.arvados.org/arvados.git/sdk/go/arvados"
9 check "gopkg.in/check.v1"
12 var _ = check.Suite(&NodeSizeSuite{})
14 const GiB = arvados.ByteSize(1 << 30)
16 type NodeSizeSuite struct{}
18 func (*NodeSizeSuite) TestChooseNotConfigured(c *check.C) {
19 _, err := ChooseInstanceType(&arvados.Cluster{}, &arvados.Container{
20 RuntimeConstraints: arvados.RuntimeConstraints{
25 c.Check(err, check.Equals, ErrInstanceTypesNotConfigured)
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},
35 c.Check(err, check.FitsTypeOf, ConstraintsNotSatisfiableError{})
38 for _, rc := range []arvados.RuntimeConstraints{
39 {RAM: 9876543210, VCPUs: 2},
40 {RAM: 1234567890, VCPUs: 20},
41 {RAM: 1234567890, VCPUs: 2, KeepCacheRAM: 9876543210},
43 checkUnsatisfiable(&arvados.Container{RuntimeConstraints: rc})
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},
51 func (*NodeSizeSuite) TestChoose(c *check.C) {
52 for _, menu := range []map[string]arvados.InstanceType{
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"},
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"},
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"},
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"},
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"},
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)},
90 RuntimeConstraints: arvados.RuntimeConstraints{
93 KeepCacheRAM: 123456789,
96 c.Check(err, check.IsNil)
97 c.Check(best.Name, check.Equals, "best")
98 c.Check(best.RAM >= 1234567890, check.Equals, true)
99 c.Check(best.VCPUs >= 2, check.Equals, true)
100 c.Check(best.Scratch >= 2*GiB, check.Equals, true)
104 func (*NodeSizeSuite) TestChooseWithBlobBuffersOverhead(c *check.C) {
105 menu := map[string]arvados.InstanceType{
106 "nearly": {Price: 2.2, RAM: 4000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "small"},
107 "best": {Price: 3.3, RAM: 8000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best"},
108 "costly": {Price: 4.4, RAM: 12000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly"},
110 best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu, Containers: arvados.ContainersConfig{
111 LocalKeepBlobBuffersPerVCPU: 16, // 1 GiB per vcpu => 2 GiB
112 ReserveExtraRAM: 268435456,
113 }}, &arvados.Container{
114 Mounts: map[string]arvados.Mount{
115 "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
117 RuntimeConstraints: arvados.RuntimeConstraints{
120 KeepCacheRAM: 123456789,
123 c.Check(err, check.IsNil)
124 c.Check(best.Name, check.Equals, "best")
127 func (*NodeSizeSuite) TestChoosePreemptible(c *check.C) {
128 menu := map[string]arvados.InstanceType{
129 "costly": {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Preemptible: true, Name: "costly"},
130 "almost best": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "almost best"},
131 "best": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Preemptible: true, Name: "best"},
132 "small": {Price: 1.1, RAM: 1000000000, VCPUs: 2, Scratch: 2 * GiB, Preemptible: true, Name: "small"},
134 best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu}, &arvados.Container{
135 Mounts: map[string]arvados.Mount{
136 "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
138 RuntimeConstraints: arvados.RuntimeConstraints{
141 KeepCacheRAM: 123456789,
143 SchedulingParameters: arvados.SchedulingParameters{
147 c.Check(err, check.IsNil)
148 c.Check(best.Name, check.Equals, "best")
149 c.Check(best.RAM >= 1234567890, check.Equals, true)
150 c.Check(best.VCPUs >= 2, check.Equals, true)
151 c.Check(best.Scratch >= 2*GiB, check.Equals, true)
152 c.Check(best.Preemptible, check.Equals, true)
155 func (*NodeSizeSuite) TestScratchForDockerImage(c *check.C) {
156 n := EstimateScratchSpace(&arvados.Container{
157 ContainerImage: "d5025c0f29f6eef304a7358afa82a822+342",
159 // Actual image is 371.1 MiB (according to workbench)
160 // Estimated size is 384 MiB (402653184 bytes)
161 // Want to reserve 2x the estimated size, so 805306368 bytes
162 c.Check(n, check.Equals, int64(805306368))
164 n = EstimateScratchSpace(&arvados.Container{
165 ContainerImage: "d5025c0f29f6eef304a7358afa82a822+-342",
167 // Parse error will return 0
168 c.Check(n, check.Equals, int64(0))
170 n = EstimateScratchSpace(&arvados.Container{
171 ContainerImage: "d5025c0f29f6eef304a7358afa82a822+34",
173 // Short manifest will return 0
174 c.Check(n, check.Equals, int64(0))
177 func (*NodeSizeSuite) TestChooseGPU(c *check.C) {
178 menu := map[string]arvados.InstanceType{
179 "costly": {Price: 4.4, RAM: 4000000000, VCPUs: 8, Scratch: 2 * GiB, Name: "costly", CUDA: arvados.CUDAFeatures{DeviceCount: 2, HardwareCapability: "9.0", DriverVersion: "11.0"}},
180 "low_capability": {Price: 2.1, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "low_capability", CUDA: arvados.CUDAFeatures{DeviceCount: 1, HardwareCapability: "8.0", DriverVersion: "11.0"}},
181 "best": {Price: 2.2, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "best", CUDA: arvados.CUDAFeatures{DeviceCount: 1, HardwareCapability: "9.0", DriverVersion: "11.0"}},
182 "low_driver": {Price: 2.1, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "low_driver", CUDA: arvados.CUDAFeatures{DeviceCount: 1, HardwareCapability: "9.0", DriverVersion: "10.0"}},
183 "cheap_gpu": {Price: 2.0, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "cheap_gpu", CUDA: arvados.CUDAFeatures{DeviceCount: 1, HardwareCapability: "8.0", DriverVersion: "10.0"}},
184 "invalid_gpu": {Price: 1.9, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "invalid_gpu", CUDA: arvados.CUDAFeatures{DeviceCount: 1, HardwareCapability: "12.0.12", DriverVersion: "12.0.12"}},
185 "non_gpu": {Price: 1.1, RAM: 2000000000, VCPUs: 4, Scratch: 2 * GiB, Name: "non_gpu"},
188 type GPUTestCase struct {
189 CUDA arvados.CUDARuntimeConstraints
190 SelectedInstance string
192 cases := []GPUTestCase{
194 CUDA: arvados.CUDARuntimeConstraints{
196 HardwareCapability: "9.0",
197 DriverVersion: "11.0",
199 SelectedInstance: "best",
202 CUDA: arvados.CUDARuntimeConstraints{
204 HardwareCapability: "9.0",
205 DriverVersion: "11.0",
207 SelectedInstance: "costly",
210 CUDA: arvados.CUDARuntimeConstraints{
212 HardwareCapability: "8.0",
213 DriverVersion: "11.0",
215 SelectedInstance: "low_capability",
218 CUDA: arvados.CUDARuntimeConstraints{
220 HardwareCapability: "9.0",
221 DriverVersion: "10.0",
223 SelectedInstance: "low_driver",
226 CUDA: arvados.CUDARuntimeConstraints{
228 HardwareCapability: "",
229 DriverVersion: "10.0",
231 SelectedInstance: "",
234 CUDA: arvados.CUDARuntimeConstraints{
236 HardwareCapability: "9.0",
237 DriverVersion: "11.0",
239 SelectedInstance: "non_gpu",
243 for _, tc := range cases {
244 best, err := ChooseInstanceType(&arvados.Cluster{InstanceTypes: menu}, &arvados.Container{
245 Mounts: map[string]arvados.Mount{
246 "/tmp": {Kind: "tmp", Capacity: 2 * int64(GiB)},
248 RuntimeConstraints: arvados.RuntimeConstraints{
251 KeepCacheRAM: 123456789,
256 c.Check(err, check.IsNil)
257 c.Check(best.Name, check.Equals, tc.SelectedInstance)
259 c.Check(err, check.Not(check.IsNil))