1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
6 require 'helpers/container_test_helper'
8 class ContainerTest < ActiveSupport::TestCase
10 include ContainerTestHelper
13 command: ['echo', 'foo'],
14 container_image: 'fa3c1a9cb6783f85f2ecda037e07b8c3+167',
17 runtime_constraints: {"vcpus" => 1, "ram" => 1, "cuda" => {"device_count":0, "driver_version": "", "hardware_capability": ""}},
20 REUSABLE_COMMON_ATTRS = {
21 container_image: "9ae44d5792468c58bcf85ce7353c7027+124",
23 command: ["echo", "hello"],
26 runtime_constraints: {
28 "keep_cache_disk" => 0,
29 "keep_cache_ram" => 0,
34 "test" => {"kind" => "json"},
40 runtime_user_uuid: "zzzzz-tpzed-xurymjxw79nv3jz",
41 runtime_auth_scopes: ["all"],
42 scheduling_parameters: {},
45 REUSABLE_ATTRS_SLIM = {
46 command: ["echo", "slim"],
47 container_image: "9ae44d5792468c58bcf85ce7353c7027+124",
53 runtime_auth_scopes: ["all"],
54 runtime_constraints: {
56 "keep_cache_disk" => 0,
57 "keep_cache_ram" => 0,
61 runtime_user_uuid: "zzzzz-tpzed-xurymjxw79nv3jz",
63 scheduling_parameters: {},
66 def request_only attrs
67 attrs.reject {|k| [:runtime_user_uuid, :runtime_auth_scopes].include? k}
70 def minimal_new attrs={}
71 cr = ContainerRequest.new request_only(DEFAULT_ATTRS.merge(attrs))
72 cr.state = ContainerRequest::Committed
74 c = Container.find_by_uuid cr.container_uuid
79 def check_illegal_updates c, bad_updates
80 bad_updates.each do |u|
81 refute c.update(u), u.inspect
82 refute c.valid?, u.inspect
87 def check_illegal_modify c
88 check_illegal_updates c, [{command: ["echo", "bar"]},
89 {container_image: "arvados/apitestfixture:june10"},
91 {environment: {"FOO" => "BAR"}},
92 {mounts: {"FOO" => "BAR"}},
93 {output_path: "/tmp3"},
94 {locked_by_uuid: api_client_authorizations(:admin).uuid},
95 {auth_uuid: api_client_authorizations(:system_user).uuid},
96 {runtime_constraints: {"FOO" => "BAR"}}]
99 def check_bogus_states c
100 check_illegal_updates c, [{state: nil},
104 def check_no_change_from_cancelled c
105 check_illegal_modify c
107 check_illegal_updates c, [{ priority: 3 },
108 { state: Container::Queued },
109 { state: Container::Locked },
110 { state: Container::Running },
111 { state: Container::Complete }]
114 test "Container create" do
115 act_as_system_user do
116 c, _ = minimal_new(environment: {},
117 mounts: {"BAR" => {"kind" => "FOO"}},
120 runtime_constraints: {"vcpus" => 1, "ram" => 1})
122 check_illegal_modify c
131 test "Container valid priority" do
132 act_as_system_user do
133 c, _ = minimal_new(environment: {},
134 mounts: {"BAR" => {"kind" => "FOO"}},
137 runtime_constraints: {"vcpus" => 1, "ram" => 1})
139 assert_raises(ActiveRecord::RecordInvalid) do
159 c.priority = 1000 << 50
164 test "Container runtime_status data types" do
165 set_user_from_auth :active
168 mounts: {"BAR" => {"kind" => "FOO"}},
171 runtime_constraints: {"vcpus" => 1, "ram" => 1}
173 c, _ = minimal_new(attrs)
174 assert_equal c.runtime_status, {}
175 assert_equal Container::Queued, c.state
177 set_user_from_auth :system_user
178 c.update! state: Container::Locked
179 c.update! state: Container::Running
182 'error', 'errorDetail', 'warning', 'warningDetail', 'activity'
184 # String type is allowed
185 string_val = 'A string is accepted'
186 c.update! runtime_status: {k => string_val}
187 assert_equal string_val, c.runtime_status[k]
189 # Other types aren't allowed
191 42, false, [], {}, nil
192 ].each do |unallowed_val|
193 assert_raises ActiveRecord::RecordInvalid do
194 c.update! runtime_status: {k => unallowed_val}
200 test "Container runtime_status updates" do
201 set_user_from_auth :active
204 mounts: {"BAR" => {"kind" => "FOO"}},
207 runtime_constraints: {"vcpus" => 1, "ram" => 1}
209 c1, _ = minimal_new(attrs)
210 assert_equal c1.runtime_status, {}
212 assert_equal Container::Queued, c1.state
213 assert_raises ArvadosModel::PermissionDeniedError do
214 c1.update! runtime_status: {'error' => 'Oops!'}
217 set_user_from_auth :system_user
219 # Allow updates when state = Locked
220 c1.update! state: Container::Locked
221 c1.update! runtime_status: {'error' => 'Oops!'}
222 assert c1.runtime_status.key? 'error'
224 # Reset when transitioning from Locked to Queued
225 c1.update! state: Container::Queued
226 assert_equal c1.runtime_status, {}
228 # Allow updates when state = Running
229 c1.update! state: Container::Locked
230 c1.update! state: Container::Running
231 c1.update! runtime_status: {'error' => 'Oops!'}
232 assert c1.runtime_status.key? 'error'
234 # Don't allow updates on other states
235 c1.update! state: Container::Complete
236 assert_raises ActiveRecord::RecordInvalid do
237 c1.update! runtime_status: {'error' => 'Some other error'}
240 set_user_from_auth :active
241 c2, _ = minimal_new(attrs)
242 assert_equal c2.runtime_status, {}
243 set_user_from_auth :system_user
244 c2.update! state: Container::Locked
245 c2.update! state: Container::Running
246 c2.update! state: Container::Cancelled
247 assert_raises ActiveRecord::RecordInvalid do
248 c2.update! runtime_status: {'error' => 'Oops!'}
252 test "Container serialized hash attributes sorted before save" do
253 set_user_from_auth :active
254 env = {"C" => "3", "B" => "2", "A" => "1"}
255 m = {"F" => {"kind" => "3"}, "E" => {"kind" => "2"}, "D" => {"kind" => "1"}}
256 rc = {"vcpus" => 1, "ram" => 1, "keep_cache_ram" => 1, "keep_cache_disk" => 0, "API" => true, "cuda" => {"device_count":0, "driver_version": "", "hardware_capability": ""}}
257 c, _ = minimal_new(environment: env, mounts: m, runtime_constraints: rc)
259 assert_equal Container.deep_sort_hash(env).to_json, c.environment.to_json
260 assert_equal Container.deep_sort_hash(m).to_json, c.mounts.to_json
261 assert_equal Container.deep_sort_hash(rc).to_json, c.runtime_constraints.to_json
264 test 'deep_sort_hash on array of hashes' do
265 a = {'z' => [[{'a' => 'a', 'b' => 'b'}]]}
266 b = {'z' => [[{'b' => 'b', 'a' => 'a'}]]}
267 assert_equal Container.deep_sort_hash(a).to_json, Container.deep_sort_hash(b).to_json
270 test "find_reusable method should select higher priority queued container" do
271 Rails.configuration.Containers.LogReuseDecisions = true
272 set_user_from_auth :active
273 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment:{"var" => "queued"}})
274 c_low_priority, _ = minimal_new(common_attrs.merge({use_existing:false, priority:1}))
275 c_high_priority, _ = minimal_new(common_attrs.merge({use_existing:false, priority:2}))
276 assert_not_equal c_low_priority.uuid, c_high_priority.uuid
277 assert_equal Container::Queued, c_low_priority.state
278 assert_equal Container::Queued, c_high_priority.state
279 reused = Container.find_reusable(common_attrs)
280 assert_not_nil reused
281 assert_equal reused.uuid, c_high_priority.uuid
284 test "find_reusable method should select latest completed container" do
285 set_user_from_auth :active
286 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "complete"}})
288 state: Container::Complete,
290 log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
291 output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
294 c_older, _ = minimal_new(common_attrs.merge({use_existing: false}))
295 c_recent, _ = minimal_new(common_attrs.merge({use_existing: false}))
296 assert_not_equal c_older.uuid, c_recent.uuid
298 set_user_from_auth :system_user
299 c_older.update!({state: Container::Locked})
300 c_older.update!({state: Container::Running})
301 c_older.update!(completed_attrs)
303 c_recent.update!({state: Container::Locked})
304 c_recent.update!({state: Container::Running})
305 c_recent.update!(completed_attrs)
307 reused = Container.find_reusable(common_attrs)
308 assert_not_nil reused
309 assert_equal reused.uuid, c_older.uuid
312 test "find_reusable method should select oldest completed container when inconsistent outputs exist" do
313 set_user_from_auth :active
314 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "complete"}, priority: 1})
316 state: Container::Complete,
318 log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
321 cr = ContainerRequest.new request_only(common_attrs)
322 cr.use_existing = false
323 cr.state = ContainerRequest::Committed
325 c_output1 = Container.where(uuid: cr.container_uuid).first
327 cr = ContainerRequest.new request_only(common_attrs)
328 cr.use_existing = false
329 cr.state = ContainerRequest::Committed
331 c_output2 = Container.where(uuid: cr.container_uuid).first
333 assert_not_equal c_output1.uuid, c_output2.uuid
335 set_user_from_auth :system_user
337 out1 = '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
338 log1 = collections(:log_collection).portable_data_hash
339 c_output1.update!({state: Container::Locked})
340 c_output1.update!({state: Container::Running})
341 c_output1.update!(completed_attrs.merge({log: log1, output: out1}))
343 out2 = 'fa7aeb5140e2848d39b416daeef4ffc5+45'
344 c_output2.update!({state: Container::Locked})
345 c_output2.update!({state: Container::Running})
346 c_output2.update!(completed_attrs.merge({log: log1, output: out2}))
348 set_user_from_auth :active
349 reused = Container.resolve(ContainerRequest.new(request_only(common_attrs)))
350 assert_equal c_output1.uuid, reused.uuid
353 test "find_reusable method should select running container by start date" do
354 set_user_from_auth :active
355 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running"}})
356 c_slower, _ = minimal_new(common_attrs.merge({use_existing: false}))
357 c_faster_started_first, _ = minimal_new(common_attrs.merge({use_existing: false}))
358 c_faster_started_second, _ = minimal_new(common_attrs.merge({use_existing: false}))
359 # Confirm the 3 container UUIDs are different.
360 assert_equal 3, [c_slower.uuid, c_faster_started_first.uuid, c_faster_started_second.uuid].uniq.length
361 set_user_from_auth :system_user
362 c_slower.update!({state: Container::Locked})
363 c_slower.update!({state: Container::Running,
365 c_faster_started_first.update!({state: Container::Locked})
366 c_faster_started_first.update!({state: Container::Running,
368 c_faster_started_second.update!({state: Container::Locked})
369 c_faster_started_second.update!({state: Container::Running,
371 reused = Container.find_reusable(common_attrs)
372 assert_not_nil reused
373 # Selected container is the one that started first
374 assert_equal reused.uuid, c_faster_started_first.uuid
377 test "find_reusable method should select running container by progress" do
378 set_user_from_auth :active
379 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running2"}})
380 c_slower, _ = minimal_new(common_attrs.merge({use_existing: false}))
381 c_faster_started_first, _ = minimal_new(common_attrs.merge({use_existing: false}))
382 c_faster_started_second, _ = minimal_new(common_attrs.merge({use_existing: false}))
383 # Confirm the 3 container UUIDs are different.
384 assert_equal 3, [c_slower.uuid, c_faster_started_first.uuid, c_faster_started_second.uuid].uniq.length
385 set_user_from_auth :system_user
386 c_slower.update!({state: Container::Locked})
387 c_slower.update!({state: Container::Running,
389 c_faster_started_first.update!({state: Container::Locked})
390 c_faster_started_first.update!({state: Container::Running,
392 c_faster_started_second.update!({state: Container::Locked})
393 c_faster_started_second.update!({state: Container::Running,
395 reused = Container.find_reusable(common_attrs)
396 assert_not_nil reused
397 # Selected container is the one with most progress done
398 assert_equal reused.uuid, c_faster_started_second.uuid
401 test "find_reusable method should select non-failing running container" do
402 set_user_from_auth :active
403 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running2"}})
404 c_slower, _ = minimal_new(common_attrs.merge({use_existing: false}))
405 c_faster_started_first, _ = minimal_new(common_attrs.merge({use_existing: false}))
406 c_faster_started_second, _ = minimal_new(common_attrs.merge({use_existing: false}))
407 # Confirm the 3 container UUIDs are different.
408 assert_equal 3, [c_slower.uuid, c_faster_started_first.uuid, c_faster_started_second.uuid].uniq.length
409 set_user_from_auth :system_user
410 c_slower.update!({state: Container::Locked})
411 c_slower.update!({state: Container::Running,
413 c_faster_started_first.update!({state: Container::Locked})
414 c_faster_started_first.update!({state: Container::Running,
415 runtime_status: {'warning' => 'This is not an error'},
417 c_faster_started_second.update!({state: Container::Locked})
418 assert_equal 0, Container.where("runtime_status->'error' is not null").count
419 c_faster_started_second.update!({state: Container::Running,
420 runtime_status: {'error' => 'Something bad happened'},
422 assert_equal 1, Container.where("runtime_status->'error' is not null").count
423 reused = Container.find_reusable(common_attrs)
424 assert_not_nil reused
425 # Selected the non-failing container even if it's the one with less progress done
426 assert_equal reused.uuid, c_faster_started_first.uuid
429 test "find_reusable method should select locked container most likely to start sooner" do
430 set_user_from_auth :active
431 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "locked"}})
432 c_low_priority, _ = minimal_new(common_attrs.merge({use_existing: false}))
433 c_high_priority_older, _ = minimal_new(common_attrs.merge({use_existing: false}))
434 c_high_priority_newer, _ = minimal_new(common_attrs.merge({use_existing: false}))
435 # Confirm the 3 container UUIDs are different.
436 assert_equal 3, [c_low_priority.uuid, c_high_priority_older.uuid, c_high_priority_newer.uuid].uniq.length
437 set_user_from_auth :system_user
438 c_low_priority.update!({state: Container::Locked,
440 c_high_priority_older.update!({state: Container::Locked,
442 c_high_priority_newer.update!({state: Container::Locked,
444 reused = Container.find_reusable(common_attrs)
445 assert_not_nil reused
446 assert_equal reused.uuid, c_high_priority_older.uuid
449 test "find_reusable method should select running over failed container" do
450 set_user_from_auth :active
451 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "failed_vs_running"}})
452 c_failed, _ = minimal_new(common_attrs.merge({use_existing: false}))
453 c_running, _ = minimal_new(common_attrs.merge({use_existing: false}))
454 assert_not_equal c_failed.uuid, c_running.uuid
455 set_user_from_auth :system_user
456 c_failed.update!({state: Container::Locked})
457 c_failed.update!({state: Container::Running})
458 c_failed.update!({state: Container::Complete,
460 log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
461 output: 'ea10d51bcf88862dbcc36eb292017dfd+45'})
462 c_running.update!({state: Container::Locked})
463 c_running.update!({state: Container::Running,
465 reused = Container.find_reusable(common_attrs)
466 assert_not_nil reused
467 assert_equal reused.uuid, c_running.uuid
470 test "find_reusable method should select complete over running container" do
471 set_user_from_auth :active
472 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "completed_vs_running"}})
473 c_completed, _ = minimal_new(common_attrs.merge({use_existing: false}))
474 c_running, _ = minimal_new(common_attrs.merge({use_existing: false}))
475 assert_not_equal c_completed.uuid, c_running.uuid
476 set_user_from_auth :system_user
477 c_completed.update!({state: Container::Locked})
478 c_completed.update!({state: Container::Running})
479 c_completed.update!({state: Container::Complete,
481 log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
482 output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'})
483 c_running.update!({state: Container::Locked})
484 c_running.update!({state: Container::Running,
486 reused = Container.find_reusable(common_attrs)
487 assert_not_nil reused
488 assert_equal c_completed.uuid, reused.uuid
491 test "find_reusable method should select running over locked container" do
492 set_user_from_auth :active
493 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running_vs_locked"}})
494 c_locked, _ = minimal_new(common_attrs.merge({use_existing: false}))
495 c_running, _ = minimal_new(common_attrs.merge({use_existing: false}))
496 assert_not_equal c_running.uuid, c_locked.uuid
497 set_user_from_auth :system_user
498 c_locked.update!({state: Container::Locked})
499 c_running.update!({state: Container::Locked})
500 c_running.update!({state: Container::Running,
502 reused = Container.find_reusable(common_attrs)
503 assert_not_nil reused
504 assert_equal reused.uuid, c_running.uuid
507 test "find_reusable method should select locked over queued container" do
508 set_user_from_auth :active
509 common_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "running_vs_locked"}})
510 c_locked, _ = minimal_new(common_attrs.merge({use_existing: false}))
511 c_queued, _ = minimal_new(common_attrs.merge({use_existing: false}))
512 assert_not_equal c_queued.uuid, c_locked.uuid
513 set_user_from_auth :system_user
514 c_locked.update!({state: Container::Locked})
515 reused = Container.find_reusable(common_attrs)
516 assert_not_nil reused
517 assert_equal reused.uuid, c_locked.uuid
520 test "find_reusable method should not select failed container" do
521 set_user_from_auth :active
522 attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"var" => "failed"}})
523 c, _ = minimal_new(attrs)
524 set_user_from_auth :system_user
525 c.update!({state: Container::Locked})
526 c.update!({state: Container::Running})
527 c.update!({state: Container::Complete,
529 reused = Container.find_reusable(attrs)
533 [[false, false, true],
535 [true, false, false],
537 ].each do |c1_preemptible, c2_preemptible, should_reuse|
538 [[Container::Queued, 1],
539 [Container::Locked, 1],
540 [Container::Running, 0], # not cancelled yet, but obviously will be soon
541 ].each do |c1_state, c1_priority|
542 test "find_reusable for #{c2_preemptible ? '' : 'non-'}preemptible req should #{should_reuse ? '' : 'not'} reuse a #{c1_state} #{c1_preemptible ? '' : 'non-'}preemptible container with priority #{c1_priority}" do
543 configure_preemptible_instance_type
544 set_user_from_auth :active
545 c1_attrs = REUSABLE_COMMON_ATTRS.merge({environment: {"test" => name, "state" => c1_state}, scheduling_parameters: {"preemptible" => c1_preemptible}})
546 c1, _ = minimal_new(c1_attrs)
547 set_user_from_auth :system_user
548 c1.update!({state: Container::Locked}) if c1_state != Container::Queued
549 c1.update!({state: Container::Running, priority: c1_priority}) if c1_state == Container::Running
550 c2_attrs = c1_attrs.merge({scheduling_parameters: {"preemptible" => c2_preemptible}})
551 reused = Container.find_reusable(c2_attrs)
552 if should_reuse && c1_priority > 0
553 assert_not_nil reused
561 test "find_reusable with logging disabled" do
562 set_user_from_auth :active
563 Rails.logger.expects(:info).never
564 Container.find_reusable(REUSABLE_COMMON_ATTRS)
567 test "find_reusable with logging enabled" do
568 set_user_from_auth :active
569 Rails.configuration.Containers.LogReuseDecisions = true
570 Rails.logger.expects(:info).at_least(3)
571 Container.find_reusable(REUSABLE_COMMON_ATTRS)
574 def runtime_token_attr tok
575 auth = api_client_authorizations(tok)
576 {runtime_user_uuid: User.find_by_id(auth.user_id).uuid,
577 runtime_auth_scopes: auth.scopes,
578 runtime_token: auth.token}
581 test "find_reusable method with same runtime_token" do
582 set_user_from_auth :active
583 common_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"}})
584 c1, _ = minimal_new(common_attrs.merge({runtime_token: api_client_authorizations(:container_runtime_token).token}))
585 assert_equal Container::Queued, c1.state
586 reused = Container.find_reusable(common_attrs.merge(runtime_token_attr(:container_runtime_token)))
587 assert_not_nil reused
588 assert_equal reused.uuid, c1.uuid
591 test "find_reusable method with different runtime_token, same user" do
592 set_user_from_auth :active
593 common_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"}})
594 c1, _ = minimal_new(common_attrs.merge({runtime_token: api_client_authorizations(:crt_user).token}))
595 assert_equal Container::Queued, c1.state
596 reused = Container.find_reusable(common_attrs.merge(runtime_token_attr(:container_runtime_token)))
597 assert_not_nil reused
598 assert_equal reused.uuid, c1.uuid
601 test "find_reusable method with nil runtime_token, then runtime_token with same user" do
602 set_user_from_auth :crt_user
603 common_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"}})
604 c1, _ = minimal_new(common_attrs)
605 assert_equal Container::Queued, c1.state
606 assert_equal users(:container_runtime_token_user).uuid, c1.runtime_user_uuid
607 reused = Container.find_reusable(common_attrs.merge(runtime_token_attr(:container_runtime_token)))
608 assert_not_nil reused
609 assert_equal reused.uuid, c1.uuid
612 test "find_reusable method with different runtime_token, different user" do
613 set_user_from_auth :crt_user
614 common_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"}})
615 c1, _ = minimal_new(common_attrs.merge({runtime_token: api_client_authorizations(:active).token}))
616 assert_equal Container::Queued, c1.state
617 reused = Container.find_reusable(common_attrs.merge(runtime_token_attr(:container_runtime_token)))
619 assert_not_nil reused
620 assert_equal c1.uuid, reused.uuid
623 test "find_reusable method with nil runtime_token, then runtime_token with different user" do
624 set_user_from_auth :active
625 common_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"}})
626 c1, _ = minimal_new(common_attrs.merge({runtime_token: nil}))
627 assert_equal Container::Queued, c1.state
628 reused = Container.find_reusable(common_attrs.merge(runtime_token_attr(:container_runtime_token)))
630 assert_not_nil reused
631 assert_equal c1.uuid, reused.uuid
634 test "find_reusable method with different runtime_token, different scope, same user" do
635 set_user_from_auth :active
636 common_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"}})
637 c1, _ = minimal_new(common_attrs.merge({runtime_token: api_client_authorizations(:runtime_token_limited_scope).token}))
638 assert_equal Container::Queued, c1.state
639 reused = Container.find_reusable(common_attrs.merge(runtime_token_attr(:container_runtime_token)))
641 assert_not_nil reused
642 assert_equal c1.uuid, reused.uuid
645 test "find_reusable method with cuda" do
646 set_user_from_auth :active
648 no_cuda_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"},
649 runtime_constraints: {"vcpus" => 1, "ram" => 1, "keep_cache_disk"=>0, "keep_cache_ram"=>268435456, "API" => false,
650 "cuda" => {"device_count":0, "driver_version": "", "hardware_capability": ""}},})
651 c1, _ = minimal_new(no_cuda_attrs)
652 assert_equal Container::Queued, c1.state
655 cuda_attrs = REUSABLE_COMMON_ATTRS.merge({use_existing:false, priority:1, environment:{"var" => "queued"},
656 runtime_constraints: {"vcpus" => 1, "ram" => 1, "keep_cache_disk"=>0, "keep_cache_ram"=>268435456, "API" => false,
657 "cuda" => {"device_count":1, "driver_version": "11.0", "hardware_capability": "9.0"}},})
658 c2, _ = minimal_new(cuda_attrs)
659 assert_equal Container::Queued, c2.state
661 # should find the no cuda one
662 reused = Container.find_reusable(no_cuda_attrs)
663 assert_not_nil reused
664 assert_equal reused.uuid, c1.uuid
666 # should find the cuda one
667 reused = Container.find_reusable(cuda_attrs)
668 assert_not_nil reused
669 assert_equal reused.uuid, c2.uuid
672 test "Container running" do
673 set_user_from_auth :active
674 c, _ = minimal_new priority: 1
676 set_user_from_auth :system_user
677 check_illegal_updates c, [{state: Container::Running},
678 {state: Container::Complete}]
681 c.update! state: Container::Running
683 check_illegal_modify c
686 check_illegal_updates c, [{state: Container::Queued}]
689 c.update! priority: 3
692 test "Lock and unlock" do
693 set_user_from_auth :active
694 c, cr = minimal_new priority: 0
696 set_user_from_auth :system_user
697 assert_equal Container::Queued, c.state
699 assert_raise(ArvadosModel::LockFailedError) do
704 assert cr.update priority: 1
706 refute c.update(state: Container::Running), "not locked"
708 refute c.update(state: Container::Complete), "not locked"
711 assert c.lock, show_errors(c)
712 assert c.locked_by_uuid
715 assert_raise(ArvadosModel::LockFailedError) {c.lock}
718 assert c.unlock, show_errors(c)
719 refute c.locked_by_uuid
722 refute c.update(state: Container::Running), "not locked"
724 refute c.locked_by_uuid
727 assert c.lock, show_errors(c)
728 assert c.update(state: Container::Running), show_errors(c)
729 assert c.locked_by_uuid
732 auth_uuid_was = c.auth_uuid
734 assert_raise(ArvadosModel::LockFailedError) do
735 # Running to Locked is not allowed
739 assert_raise(ArvadosModel::InvalidStateTransitionError) do
740 # Running to Queued is not allowed
745 assert c.update(state: Container::Complete), show_errors(c)
746 refute c.locked_by_uuid
749 auth_exp = ApiClientAuthorization.find_by_uuid(auth_uuid_was).expires_at
750 assert_operator auth_exp, :<, db_current_time
752 assert_nil ApiClientAuthorization.validate(token: ApiClientAuthorization.find_by_uuid(auth_uuid_was).token)
755 test "Exceed maximum lock-unlock cycles" do
756 Rails.configuration.Containers.MaxDispatchAttempts = 3
758 set_user_from_auth :active
761 set_user_from_auth :system_user
762 assert_equal Container::Queued, c.state
763 assert_equal 0, c.lock_count
767 assert_equal 1, c.lock_count
768 assert_equal Container::Locked, c.state
772 assert_equal 1, c.lock_count
773 assert_equal Container::Queued, c.state
777 assert_equal 2, c.lock_count
778 assert_equal Container::Locked, c.state
782 assert_equal 2, c.lock_count
783 assert_equal Container::Queued, c.state
787 assert_equal 3, c.lock_count
788 assert_equal Container::Locked, c.state
792 assert_equal 3, c.lock_count
793 assert_equal Container::Cancelled, c.state
795 assert_raise(ArvadosModel::LockFailedError) do
796 # Cancelled to Locked is not allowed
801 test "Container queued cancel" do
802 set_user_from_auth :active
803 c, cr = minimal_new({container_count_max: 1})
804 set_user_from_auth :system_user
805 assert c.update(state: Container::Cancelled), show_errors(c)
806 check_no_change_from_cancelled c
808 assert_equal ContainerRequest::Final, cr.state
811 test "Container queued count" do
812 assert_equal 1, Container.readable_by(users(:active)).where(state: "Queued").count
815 test "Containers with no matching request are readable by admin" do
816 uuids = Container.includes('container_requests').where(container_requests: {uuid: nil}).collect(&:uuid)
817 assert_not_empty uuids
818 assert_empty Container.readable_by(users(:active)).where(uuid: uuids)
819 assert_not_empty Container.readable_by(users(:admin)).where(uuid: uuids)
820 assert_equal uuids.count, Container.readable_by(users(:admin)).where(uuid: uuids).count
823 test "Container locked cancel" do
824 set_user_from_auth :active
826 set_user_from_auth :system_user
827 assert c.lock, show_errors(c)
828 assert c.update(state: Container::Cancelled), show_errors(c)
829 check_no_change_from_cancelled c
832 test "Container locked with non-expiring token" do
833 Rails.configuration.API.TokenMaxLifetime = 1.hour
834 set_user_from_auth :active
836 set_user_from_auth :system_user
837 assert c.lock, show_errors(c)
839 assert c.auth.expires_at.nil?
840 assert c.auth.user_id == User.find_by_uuid(users(:active).uuid).id
843 test "Container locked cancel with log" do
844 set_user_from_auth :active
846 set_user_from_auth :system_user
847 assert c.lock, show_errors(c)
849 state: Container::Cancelled,
850 log: collections(:log_collection).portable_data_hash,
852 check_no_change_from_cancelled c
855 test "Container running cancel" do
856 set_user_from_auth :active
858 set_user_from_auth :system_user
860 c.update! state: Container::Running
861 c.update! state: Container::Cancelled
862 check_no_change_from_cancelled c
865 test "Container create forbidden for non-admin" do
866 set_user_from_auth :active_trustedclient
867 c = Container.new DEFAULT_ATTRS
869 c.mounts = {"BAR" => "FOO"}
870 c.output_path = "/tmp"
872 c.runtime_constraints = {}
873 assert_raises(ArvadosModel::PermissionDeniedError) do
879 [Container::Queued, {state: Container::Locked}],
880 [Container::Queued, {state: Container::Running}],
881 [Container::Queued, {state: Container::Complete}],
882 [Container::Queued, {state: Container::Cancelled}],
883 [Container::Queued, {priority: 123456789}],
884 [Container::Queued, {runtime_status: {'error' => 'oops'}}],
885 [Container::Queued, {cwd: '/'}],
886 [Container::Locked, {state: Container::Running}],
887 [Container::Locked, {state: Container::Queued}],
888 [Container::Locked, {priority: 123456789}],
889 [Container::Locked, {runtime_status: {'error' => 'oops'}}],
890 [Container::Locked, {cwd: '/'}],
891 [Container::Running, {state: Container::Complete}],
892 [Container::Running, {state: Container::Cancelled}],
893 [Container::Running, {priority: 123456789}],
894 [Container::Running, {runtime_status: {'error' => 'oops'}}],
895 [Container::Running, {cwd: '/'}],
896 [Container::Running, {gateway_address: "172.16.0.1:12345"}],
897 [Container::Running, {interactive_session_started: true}],
898 [Container::Complete, {state: Container::Cancelled}],
899 [Container::Complete, {priority: 123456789}],
900 [Container::Complete, {runtime_status: {'error' => 'oops'}}],
901 [Container::Complete, {cwd: '/'}],
902 [Container::Cancelled, {cwd: '/'}],
903 ].each do |start_state, updates|
904 test "Container update #{updates.inspect} when #{start_state} forbidden for non-admin" do
905 set_user_from_auth :active
907 if start_state != Container::Queued
908 set_user_from_auth :system_user
910 if start_state != Container::Locked
911 c.update! state: Container::Running
912 if start_state != Container::Running
913 c.update! state: start_state
917 assert_equal c.state, start_state
918 set_user_from_auth :active
919 assert_raises(ArvadosModel::PermissionDeniedError) do
925 test "can only change exit code while running and at completion" do
926 set_user_from_auth :active
928 set_user_from_auth :system_user
930 check_illegal_updates c, [{exit_code: 1}]
931 c.update! state: Container::Running
932 assert c.update(exit_code: 1)
933 assert c.update(exit_code: 1, state: Container::Complete)
936 test "locked_by_uuid can update log when locked/running, and output when running" do
937 set_user_from_auth :active
938 logcoll = collections(:container_log_collection)
940 cr2 = ContainerRequest.new(DEFAULT_ATTRS)
941 cr2.state = ContainerRequest::Committed
942 act_as_user users(:active) do
945 assert_equal cr1.container_uuid, cr2.container_uuid
947 logpdh_time1 = logcoll.portable_data_hash
949 set_user_from_auth :system_user
951 assert_equal c.locked_by_uuid, Thread.current[:api_client_authorization].uuid
952 c.update!(log: logpdh_time1)
953 c.update!(state: Container::Running)
956 cr1log_uuid = cr1.log_uuid
957 cr2log_uuid = cr2.log_uuid
958 assert_not_nil cr1log_uuid
959 assert_not_nil cr2log_uuid
960 assert_not_equal logcoll.uuid, cr1log_uuid
961 assert_not_equal logcoll.uuid, cr2log_uuid
962 assert_not_equal cr1log_uuid, cr2log_uuid
964 logcoll.update!(manifest_text: logcoll.manifest_text + ". acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:foo.txt\n")
965 logpdh_time2 = logcoll.portable_data_hash
967 assert c.update(output: collections(:collection_owned_by_active).portable_data_hash)
968 assert c.update(log: logpdh_time2)
969 assert c.update(state: Container::Complete, log: logcoll.portable_data_hash)
971 assert_equal collections(:collection_owned_by_active).portable_data_hash, c.output
972 assert_equal logpdh_time2, c.log
973 refute c.update(output: nil)
974 refute c.update(log: nil)
977 assert_equal cr1log_uuid, cr1.log_uuid
978 assert_equal cr2log_uuid, cr2.log_uuid
979 assert_equal 1, Collection.where(uuid: [cr1log_uuid, cr2log_uuid]).to_a.collect(&:portable_data_hash).uniq.length
980 assert_equal ". 8c12f5f5297b7337598170c6f531fcee+7882 acbd18db4cc2f85cedef654fccc4a4d8+3 0:0:arv-mount.txt 0:1910:container.json 1910:1264:crunch-run.txt 3174:1005:crunchstat.txt 7882:3:foo.txt 4179:659:hoststat.txt 4838:2811:node-info.txt 7649:233:node.json 0:0:stderr.txt
981 ./log\\040for\\040container\\040#{cr1.container_uuid} 8c12f5f5297b7337598170c6f531fcee+7882 acbd18db4cc2f85cedef654fccc4a4d8+3 0:0:arv-mount.txt 0:1910:container.json 1910:1264:crunch-run.txt 3174:1005:crunchstat.txt 7882:3:foo.txt 4179:659:hoststat.txt 4838:2811:node-info.txt 7649:233:node.json 0:0:stderr.txt
982 ", Collection.find_by_uuid(cr1log_uuid).manifest_text
985 ["auth_uuid", "runtime_token"].each do |tok|
986 test "#{tok} can set output, progress, runtime_status, state, exit_code on running container -- but not log" do
987 if tok == "runtime_token"
988 set_user_from_auth :spectator
989 c, _ = minimal_new(container_image: "9ae44d5792468c58bcf85ce7353c7027+124",
990 runtime_token: api_client_authorizations(:active).token)
992 set_user_from_auth :active
995 set_user_from_auth :system_user
997 c.update! state: Container::Running
999 if tok == "runtime_token"
1000 auth = ApiClientAuthorization.validate(token: c.runtime_token)
1001 Thread.current[:api_client_authorization] = auth
1002 Thread.current[:token] = auth.token
1003 Thread.current[:user] = auth.user
1005 auth = ApiClientAuthorization.find_by_uuid(c.auth_uuid)
1006 Thread.current[:api_client_authorization] = auth
1007 Thread.current[:token] = auth.token
1008 Thread.current[:user] = auth.user
1011 assert c.update(gateway_address: "127.0.0.1:9")
1012 assert c.update(output: collections(:collection_owned_by_active).portable_data_hash)
1013 assert c.update(runtime_status: {'warning' => 'something happened'})
1014 assert c.update(progress: 0.5)
1015 assert c.update(exit_code: 0)
1016 refute c.update(log: collections(:log_collection).portable_data_hash)
1018 assert c.update(state: Container::Complete, exit_code: 0)
1022 test "not allowed to set output that is not readable by current user" do
1023 set_user_from_auth :active
1025 set_user_from_auth :system_user
1027 c.update! state: Container::Running
1029 Thread.current[:api_client_authorization] = ApiClientAuthorization.find_by_uuid(c.auth_uuid)
1030 Thread.current[:user] = User.find_by_id(Thread.current[:api_client_authorization].user_id)
1032 assert_raises ActiveRecord::RecordInvalid do
1033 c.update! output: collections(:collection_not_readable_by_active).portable_data_hash
1037 test "other token cannot set output on running container" do
1038 set_user_from_auth :active
1040 set_user_from_auth :system_user
1042 c.update! state: Container::Running
1044 set_user_from_auth :running_to_be_deleted_container_auth
1045 assert_raises(ArvadosModel::PermissionDeniedError) do
1046 c.update(output: collections(:foo_file).portable_data_hash)
1050 test "can set trashed output on running container" do
1051 set_user_from_auth :active
1053 set_user_from_auth :system_user
1055 c.update! state: Container::Running
1057 output = Collection.find_by_uuid('zzzzz-4zz18-mto52zx1s7sn3jk')
1059 assert output.is_trashed
1060 assert c.update output: output.portable_data_hash
1061 assert c.update! state: Container::Complete
1064 test "not allowed to set trashed output that is not readable by current user" do
1065 set_user_from_auth :active
1067 set_user_from_auth :system_user
1069 c.update! state: Container::Running
1071 output = Collection.find_by_uuid('zzzzz-4zz18-mto52zx1s7sn3jr')
1073 Thread.current[:api_client_authorization] = ApiClientAuthorization.find_by_uuid(c.auth_uuid)
1074 Thread.current[:user] = User.find_by_id(Thread.current[:api_client_authorization].user_id)
1076 assert_raises ActiveRecord::RecordInvalid do
1077 c.update! output: output.portable_data_hash
1081 test "user cannot delete" do
1082 set_user_from_auth :active
1084 assert_raises ArvadosModel::PermissionDeniedError do
1087 assert Container.find_by_uuid(c.uuid)
1091 {state: Container::Complete, exit_code: 0, output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'},
1092 {state: Container::Cancelled},
1093 ].each do |final_attrs|
1094 test "secret_mounts and runtime_token are null after container is #{final_attrs[:state]}" do
1095 set_user_from_auth :active
1096 c, cr = minimal_new(secret_mounts: {'/secret' => {'kind' => 'text', 'content' => 'foo'}},
1097 container_count_max: 1, runtime_token: api_client_authorizations(:active).token)
1098 set_user_from_auth :system_user
1100 c.update!(state: Container::Running)
1102 assert c.secret_mounts.has_key?('/secret')
1103 assert_equal api_client_authorizations(:active).token, c.runtime_token
1105 c.update!(final_attrs)
1107 assert_equal({}, c.secret_mounts)
1108 assert_nil c.runtime_token
1110 assert_equal({}, cr.secret_mounts)
1111 assert_nil cr.runtime_token
1112 assert_no_secrets_logged
1116 def configure_preemptible_instance_type
1117 Rails.configuration.InstanceTypes = ConfigLoader.to_OrderedOptions({
1119 "Preemptible" => true,
1121 "ProviderType" => "a1.small",
1123 "RAM" => 1000000000,
1128 def vary_parameters(**kwargs)
1129 # kwargs is a hash that maps parameters to an array of values.
1130 # This function enumerates every possible hash where each key has one of
1131 # the values from its array.
1132 # The output keys are strings since that's what container hash attributes
1134 # A nil value yields a hash without that key.
1135 [[:_, nil]].product(
1136 *kwargs.map { |(key, values)| [key.to_s].product(values) },
1137 ).map { |param_pairs| Hash[param_pairs].compact }
1140 def retry_with_scheduling_parameters(param_hashes)
1141 set_user_from_auth :admin
1144 param_hashes.each do |scheduling_parameters|
1145 container, request = minimal_new(scheduling_parameters: scheduling_parameters)
1146 containers[container.uuid] = container
1149 refute(containers.empty?, "buggy test: no scheduling parameters enumerated")
1150 assert_equal(1, containers.length)
1151 _, container1 = containers.shift
1153 container1.update!(state: Container::Cancelled)
1155 request1 = requests.shift
1157 assert_not_equal(container1.uuid, request1.container_uuid)
1158 requests.each do |request|
1160 assert_equal(request1.container_uuid, request.container_uuid)
1162 container2 = Container.find_by_uuid(request1.container_uuid)
1163 assert_not_nil(container2)
1167 preemptible_values = [true, false, nil]
1168 preemptible_values.permutation(1).chain(
1169 preemptible_values.product(preemptible_values),
1170 preemptible_values.product(preemptible_values, preemptible_values),
1171 ).each do |preemptible_a|
1172 # If the first req has preemptible=true but a subsequent req
1173 # doesn't, we want to avoid reusing the first container, so this
1174 # test isn't appropriate.
1175 next if preemptible_a[0] &&
1176 ((preemptible_a.length > 1 && !preemptible_a[1]) ||
1177 (preemptible_a.length > 2 && !preemptible_a[2]))
1178 test "retry requests scheduled with preemptible=#{preemptible_a}" do
1179 configure_preemptible_instance_type
1180 param_hashes = vary_parameters(preemptible: preemptible_a)
1181 container = retry_with_scheduling_parameters(param_hashes)
1182 assert_equal(preemptible_a.all?,
1183 container.scheduling_parameters["preemptible"] || false)
1187 partition_values = [nil, [], ["alpha"], ["alpha", "bravo"], ["bravo", "charlie"]]
1188 partition_values.permutation(1).chain(
1189 partition_values.permutation(2),
1190 ).each do |partitions_a|
1191 test "retry requests scheduled with partitions=#{partitions_a}" do
1192 param_hashes = vary_parameters(partitions: partitions_a)
1193 container = retry_with_scheduling_parameters(param_hashes)
1194 expected = if partitions_a.any? { |value| value.nil? or value.empty? }
1197 partitions_a.flatten.uniq
1199 actual = container.scheduling_parameters["partitions"] || []
1200 assert_equal(expected.sort, actual.sort)
1204 runtime_values = [nil, 0, 1, 2, 3]
1205 runtime_values.permutation(1).chain(
1206 runtime_values.permutation(2),
1207 runtime_values.permutation(3),
1208 ).each do |max_run_time_a|
1209 test "retry requests scheduled with max_run_time=#{max_run_time_a}" do
1210 param_hashes = vary_parameters(max_run_time: max_run_time_a)
1211 container = retry_with_scheduling_parameters(param_hashes)
1212 expected = if max_run_time_a.any? { |value| value.nil? or value == 0 }
1217 actual = container.scheduling_parameters["max_run_time"] || 0
1218 assert_equal(expected, actual)
1222 test "retry requests with multi-varied scheduling parameters" do
1223 configure_preemptible_instance_type
1225 "partitions": ["alpha", "bravo"],
1226 "preemptible": false,
1229 "partitions": ["alpha", "charlie"],
1232 "partitions": ["bravo", "charlie"],
1233 "preemptible": true,
1236 container = retry_with_scheduling_parameters(param_hashes)
1237 actual = container.scheduling_parameters
1238 assert_equal(["alpha", "bravo", "charlie"], actual["partitions"]&.sort)
1239 assert_equal(false, actual["preemptible"] || false)
1240 assert_equal(30, actual["max_run_time"])
1243 test "retry requests with unset scheduling parameters" do
1244 configure_preemptible_instance_type
1245 param_hashes = vary_parameters(
1246 preemptible: [nil, true],
1247 partitions: [nil, ["alpha"]],
1248 max_run_time: [nil, 5],
1250 container = retry_with_scheduling_parameters(param_hashes)
1251 actual = container.scheduling_parameters
1252 assert_equal([], actual["partitions"] || [])
1253 assert_equal(false, actual["preemptible"] || false)
1254 assert_equal(0, actual["max_run_time"] || 0)
1257 test "retry requests with default scheduling parameters" do
1258 configure_preemptible_instance_type
1259 param_hashes = vary_parameters(
1260 preemptible: [false, true],
1261 partitions: [[], ["bravo"]],
1262 max_run_time: [0, 1],
1264 container = retry_with_scheduling_parameters(param_hashes)
1265 actual = container.scheduling_parameters
1266 assert_equal([], actual["partitions"] || [])
1267 assert_equal(false, actual["preemptible"] || false)
1268 assert_equal(0, actual["max_run_time"] || 0)
1271 def run_container(request_params, final_attrs)
1272 final_attrs[:state] ||= Container::Complete
1273 if final_attrs[:state] == Container::Complete
1274 final_attrs[:exit_code] ||= 0
1275 final_attrs[:log] ||= collections(:log_collection).portable_data_hash
1276 final_attrs[:output] ||= collections(:multilevel_collection_1).portable_data_hash
1278 container, request = minimal_new(request_params)
1280 container.update!(state: Container::Running)
1281 container.update!(final_attrs)
1282 return container, request
1285 def check_reuse_with_variations(default_keep_cache_ram, vary_attr, start_value, variations)
1286 container_params = REUSABLE_ATTRS_SLIM.merge(vary_attr => start_value)
1287 orig_default = Rails.configuration.Containers.DefaultKeepCacheRAM
1289 Rails.configuration.Containers.DefaultKeepCacheRAM = default_keep_cache_ram
1290 set_user_from_auth :admin
1291 expected, _ = run_container(container_params, {})
1292 variations.each do |variation|
1293 full_variation = REUSABLE_ATTRS_SLIM[vary_attr].merge(variation)
1294 parameters = REUSABLE_ATTRS_SLIM.merge(vary_attr => full_variation)
1295 actual = Container.find_reusable(parameters)
1296 assert_equal(expected.uuid, actual&.uuid,
1297 "request with #{vary_attr}=#{variation} did not reuse container")
1300 Rails.configuration.Containers.DefaultKeepCacheRAM = orig_default
1304 # Test that we can reuse a container with a known keep_cache_ram constraint,
1305 # no matter what keep_cache_* constraints the new request uses.
1306 [0, 2 << 30, 4 << 30].product(
1309 ).each do |(default_keep_cache_ram, multiplier, keep_disk_constraint)|
1310 test "reuse request with DefaultKeepCacheRAM=#{default_keep_cache_ram}, keep_cache_ram*=#{multiplier}, keep_cache_disk=#{keep_disk_constraint}" do
1311 runtime_constraints = REUSABLE_ATTRS_SLIM[:runtime_constraints].merge(
1312 "keep_cache_ram" => default_keep_cache_ram * multiplier,
1314 if not keep_disk_constraint
1315 # Simulate a container that predates keep_cache_disk by deleting
1316 # the constraint entirely.
1317 runtime_constraints.delete("keep_cache_disk")
1319 # Important values are:
1321 # * 2GiB, the minimum default keep_cache_disk
1322 # * 8GiB, the default keep_cache_disk based on container ram
1323 # * 32GiB, the maximum default keep_cache_disk
1324 # Check these values and values in between.
1325 vary_values = [0, 1, 2, 6, 8, 10, 32, 33].map { |v| v << 30 }.to_a
1326 variations = vary_parameters(keep_cache_ram: vary_values)
1327 .chain(vary_parameters(keep_cache_disk: vary_values))
1328 check_reuse_with_variations(
1329 default_keep_cache_ram,
1330 :runtime_constraints,
1331 runtime_constraints,
1337 # Test that we can reuse a container with a known keep_cache_disk constraint,
1338 # no matter what keep_cache_* constraints the new request uses.
1339 # keep_cache_disk values are the important values discussed in the test above.
1340 [0, 2 << 30, 4 << 30]
1341 .product([0, 2 << 30, 8 << 30, 32 << 30])
1342 .each do |(default_keep_cache_ram, keep_cache_disk)|
1343 test "reuse request with DefaultKeepCacheRAM=#{default_keep_cache_ram} and keep_cache_disk=#{keep_cache_disk}" do
1344 runtime_constraints = REUSABLE_ATTRS_SLIM[:runtime_constraints].merge(
1345 "keep_cache_disk" => keep_cache_disk,
1347 vary_values = [0, 1, 2, 6, 8, 10, 32, 33].map { |v| v << 30 }.to_a
1348 variations = vary_parameters(keep_cache_ram: vary_values)
1349 .chain(vary_parameters(keep_cache_disk: vary_values))
1350 check_reuse_with_variations(
1351 default_keep_cache_ram,
1352 :runtime_constraints,
1353 runtime_constraints,
1359 # Test that a container request can reuse a container with an exactly
1360 # matching keep_cache_* constraint, no matter what the defaults.
1361 [0, 2 << 30, 4 << 30].product(
1362 ["keep_cache_disk", "keep_cache_ram"],
1363 [135790, 13 << 30, 135 << 30],
1364 ).each do |(default_keep_cache_ram, constraint_key, constraint_value)|
1365 test "reuse request with #{constraint_key}=#{constraint_value} and DefaultKeepCacheRAM=#{default_keep_cache_ram}" do
1366 runtime_constraints = REUSABLE_ATTRS_SLIM[:runtime_constraints].merge(
1367 constraint_key => constraint_value,
1369 check_reuse_with_variations(
1370 default_keep_cache_ram,
1371 :runtime_constraints,
1372 runtime_constraints,
1373 [runtime_constraints],