14715: keepproxy.service checks for cluster config
[arvados.git] / services / api / test / functional / arvados / v1 / job_reuse_controller_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'test_helper'
6 require 'helpers/git_test_helper'
7
8 class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
9   fixtures :repositories, :users, :jobs, :links, :collections
10
11   # See git_setup.rb for the commit log for test.git.tar
12   include GitTestHelper
13
14   setup do
15     @controller = Arvados::V1::JobsController.new
16     authorize_with :active
17   end
18
19   test "reuse job with no_reuse=false" do
20     post :create, params: {
21       job: {
22         no_reuse: false,
23         script: "hash",
24         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
25         repository: "active/foo",
26         script_parameters: {
27           an_integer: '1',
28           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45'
29         }
30       }
31     }
32     assert_response :success
33     assert_not_nil assigns(:object)
34     new_job = JSON.parse(@response.body)
35     assert_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
36     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
37   end
38
39   test "reuse job with find_or_create=true" do
40     post :create, params: {
41       job: {
42         script: "hash",
43         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
44         repository: "active/foo",
45         script_parameters: {
46           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
47           an_integer: '1'
48         }
49       },
50       find_or_create: true
51     }
52     assert_response :success
53     assert_not_nil assigns(:object)
54     new_job = JSON.parse(@response.body)
55     assert_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
56     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
57   end
58
59   test "no reuse job with null log" do
60     post :create, params: {
61       job: {
62         script: "hash",
63         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
64         repository: "active/foo",
65         script_parameters: {
66           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
67           an_integer: '3'
68         }
69       },
70       find_or_create: true
71     }
72     assert_response :success
73     assert_not_nil assigns(:object)
74     new_job = JSON.parse(@response.body)
75     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqq3', new_job['uuid']
76     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
77   end
78
79   test "reuse job with symbolic script_version" do
80     post :create, params: {
81       job: {
82         script: "hash",
83         script_version: "tag1",
84         repository: "active/foo",
85         script_parameters: {
86           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
87           an_integer: '1'
88         }
89       },
90       find_or_create: true
91     }
92     assert_response :success
93     assert_not_nil assigns(:object)
94     new_job = JSON.parse(@response.body)
95     assert_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
96     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
97   end
98
99   test "do not reuse job because no_reuse=true" do
100     post :create, params: {
101       job: {
102         no_reuse: true,
103         script: "hash",
104         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
105         repository: "active/foo",
106         script_parameters: {
107           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
108           an_integer: '1'
109         }
110       }
111     }
112     assert_response :success
113     assert_not_nil assigns(:object)
114     new_job = JSON.parse(@response.body)
115     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
116     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
117   end
118
119   [false, "false"].each do |whichfalse|
120     test "do not reuse job because find_or_create=#{whichfalse.inspect}" do
121       post :create, params: {
122         job: {
123           script: "hash",
124           script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
125           repository: "active/foo",
126           script_parameters: {
127             input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
128             an_integer: '1'
129           }
130         },
131         find_or_create: whichfalse
132       }
133       assert_response :success
134       assert_not_nil assigns(:object)
135       new_job = JSON.parse(@response.body)
136       assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
137       assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
138     end
139   end
140
141   test "do not reuse job because output is not readable by user" do
142     authorize_with :job_reader
143     post :create, params: {
144       job: {
145         script: "hash",
146         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
147         repository: "active/foo",
148         script_parameters: {
149           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
150           an_integer: '1'
151         }
152       },
153       find_or_create: true
154     }
155     assert_response :success
156     assert_not_nil assigns(:object)
157     new_job = JSON.parse(@response.body)
158     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
159     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
160   end
161
162   test "test_cannot_reuse_job_no_output" do
163     post :create, params: {
164       job: {
165         no_reuse: false,
166         script: "hash",
167         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
168         repository: "active/foo",
169         script_parameters: {
170           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
171           an_integer: '2'
172         }
173       }
174     }
175     assert_response :success
176     assert_not_nil assigns(:object)
177     new_job = JSON.parse(@response.body)
178     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykppp', new_job['uuid']
179   end
180
181   test "test_reuse_job_range" do
182     post :create, params: {
183       job: {
184         no_reuse: false,
185         script: "hash",
186         minimum_script_version: "tag1",
187         script_version: "master",
188         repository: "active/foo",
189         script_parameters: {
190           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
191           an_integer: '1'
192         }
193       }
194     }
195     assert_response :success
196     assert_not_nil assigns(:object)
197     new_job = JSON.parse(@response.body)
198     assert_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
199     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
200   end
201
202   test "cannot_reuse_job_no_minimum_given_so_must_use_specified_commit" do
203     post :create, params: {
204       job: {
205         no_reuse: false,
206         script: "hash",
207         script_version: "master",
208         repository: "active/foo",
209         script_parameters: {
210           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
211           an_integer: '1'
212         }
213       }
214     }
215     assert_response :success
216     assert_not_nil assigns(:object)
217     new_job = JSON.parse(@response.body)
218     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
219     assert_equal '077ba2ad3ea24a929091a9e6ce545c93199b8e57', new_job['script_version']
220   end
221
222   test "test_cannot_reuse_job_different_input" do
223     post :create, params: {
224       job: {
225         no_reuse: false,
226         script: "hash",
227         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
228         repository: "active/foo",
229         script_parameters: {
230           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
231           an_integer: '2'
232         }
233       }
234     }
235     assert_response :success
236     assert_not_nil assigns(:object)
237     new_job = JSON.parse(@response.body)
238     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
239     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
240   end
241
242   test "test_cannot_reuse_job_different_version" do
243     post :create, params: {
244       job: {
245         no_reuse: false,
246         script: "hash",
247         script_version: "master",
248         repository: "active/foo",
249         script_parameters: {
250           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
251           an_integer: '2'
252         }
253       }
254     }
255     assert_response :success
256     assert_not_nil assigns(:object)
257     new_job = JSON.parse(@response.body)
258     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
259     assert_equal '077ba2ad3ea24a929091a9e6ce545c93199b8e57', new_job['script_version']
260   end
261
262   test "test_can_reuse_job_submitted_nondeterministic" do
263     post :create, params: {
264       job: {
265         no_reuse: false,
266         script: "hash",
267         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
268         repository: "active/foo",
269         script_parameters: {
270           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
271           an_integer: '1'
272         },
273         nondeterministic: true
274       }
275     }
276     assert_response :success
277     assert_not_nil assigns(:object)
278     new_job = JSON.parse(@response.body)
279     assert_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
280     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
281   end
282
283   test "test_cannot_reuse_job_past_nondeterministic" do
284     post :create, params: {
285       job: {
286         no_reuse: false,
287         script: "hash2",
288         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
289         repository: "active/foo",
290         script_parameters: {
291           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
292           an_integer: '1'
293         }
294       }
295     }
296     assert_response :success
297     assert_not_nil assigns(:object)
298     new_job = JSON.parse(@response.body)
299     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykyyy', new_job['uuid']
300     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
301   end
302
303   test "test_cannot_reuse_job_no_permission" do
304     authorize_with :spectator
305     post :create, params: {
306       job: {
307         no_reuse: false,
308         script: "hash",
309         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
310         repository: "active/foo",
311         script_parameters: {
312           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
313           an_integer: '1'
314         }
315       }
316     }
317     assert_response :success
318     assert_not_nil assigns(:object)
319     new_job = JSON.parse(@response.body)
320     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
321     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
322   end
323
324   test "test_cannot_reuse_job_excluded" do
325     post :create, params: {
326       job: {
327         no_reuse: false,
328         script: "hash",
329         minimum_script_version: "31ce37fe365b3dc204300a3e4c396ad333ed0556",
330         script_version: "master",
331         repository: "active/foo",
332         exclude_script_versions: ["tag1"],
333         script_parameters: {
334           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
335           an_integer: '1'
336         }
337       }
338     }
339     assert_response :success
340     assert_not_nil assigns(:object)
341     new_job = JSON.parse(@response.body)
342     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
343     assert_not_equal('4fe459abe02d9b365932b8f5dc419439ab4e2577',
344                      new_job['script_version'])
345   end
346
347   test "cannot reuse job with find_or_create but excluded version" do
348     post :create, params: {
349       job: {
350         script: "hash",
351         script_version: "master",
352         repository: "active/foo",
353         script_parameters: {
354           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
355           an_integer: '1'
356         }
357       },
358       find_or_create: true,
359       minimum_script_version: "31ce37fe365b3dc204300a3e4c396ad333ed0556",
360       exclude_script_versions: ["tag1"],
361     }
362     assert_response :success
363     assert_not_nil assigns(:object)
364     new_job = JSON.parse(@response.body)
365     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
366     assert_not_equal('4fe459abe02d9b365932b8f5dc419439ab4e2577',
367                      new_job['script_version'])
368   end
369
370   test "cannot reuse job when hash-like branch includes newer commit" do
371     check_new_job_created_from({job: {script_version: "738783"}},
372                                :previous_job_run_superseded_by_hash_branch)
373   end
374
375   BASE_FILTERS = {
376     'repository' => ['=', 'active/foo'],
377     'script' => ['=', 'hash'],
378     'script_version' => ['in git', 'master'],
379     'docker_image_locator' => ['=', nil],
380     'arvados_sdk_version' => ['=', nil],
381   }
382
383   def filters_from_hash(hash)
384     hash.each_pair.map { |name, filter| [name] + filter }
385   end
386
387   test "can reuse a Job based on filters" do
388     filters_hash = BASE_FILTERS.
389       merge('script_version' => ['in git', 'tag1'])
390     post(:create, params: {
391            job: {
392              script: "hash",
393              script_version: "master",
394              repository: "active/foo",
395              script_parameters: {
396                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
397                an_integer: '1'
398              }
399            },
400            filters: filters_from_hash(filters_hash),
401            find_or_create: true,
402          })
403     assert_response :success
404     assert_not_nil assigns(:object)
405     new_job = JSON.parse(@response.body)
406     assert_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
407     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
408   end
409
410   test "can not reuse a Job based on filters" do
411     filters = filters_from_hash(BASE_FILTERS
412                                   .reject { |k| k == 'script_version' })
413     filters += [["script_version", "in git",
414                  "31ce37fe365b3dc204300a3e4c396ad333ed0556"],
415                 ["script_version", "not in git", ["tag1"]]]
416     post(:create, params: {
417            job: {
418              script: "hash",
419              script_version: "master",
420              repository: "active/foo",
421              script_parameters: {
422                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
423                an_integer: '1'
424              }
425            },
426            filters: filters,
427            find_or_create: true,
428          })
429     assert_response :success
430     assert_not_nil assigns(:object)
431     new_job = JSON.parse(@response.body)
432     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
433     assert_equal '077ba2ad3ea24a929091a9e6ce545c93199b8e57', new_job['script_version']
434   end
435
436   test "can not reuse a Job based on arbitrary filters" do
437     filters_hash = BASE_FILTERS.
438       merge("created_at" => ["<", "2010-01-01T00:00:00Z"])
439     post(:create, params: {
440            job: {
441              script: "hash",
442              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
443              repository: "active/foo",
444              script_parameters: {
445                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
446                an_integer: '1'
447              }
448            },
449            filters: filters_from_hash(filters_hash),
450            find_or_create: true,
451          })
452     assert_response :success
453     assert_not_nil assigns(:object)
454     new_job = JSON.parse(@response.body)
455     assert_not_equal 'zzzzz-8i9sb-cjs4pklxxjykqqq', new_job['uuid']
456     assert_equal '4fe459abe02d9b365932b8f5dc419439ab4e2577', new_job['script_version']
457   end
458
459   test "can reuse a Job with a Docker image" do
460     post(:create, params: {
461            job: {
462              script: "hash",
463              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
464              repository: "active/foo",
465              script_parameters: {
466                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
467                an_integer: '1'
468              },
469              runtime_constraints: {
470                docker_image: 'arvados/apitestfixture',
471              }
472            },
473            find_or_create: true,
474          })
475     assert_response :success
476     new_job = assigns(:object)
477     assert_not_nil new_job
478     target_job = jobs(:previous_docker_job_run)
479     [:uuid, :script_version, :docker_image_locator].each do |attr|
480       assert_equal(target_job.send(attr), new_job.send(attr))
481     end
482   end
483
484   test "can reuse a Job with a Docker image hash filter" do
485     filters_hash = BASE_FILTERS.
486       merge("script_version" =>
487               ["=", "4fe459abe02d9b365932b8f5dc419439ab4e2577"],
488             "docker_image_locator" =>
489               ["in docker", links(:docker_image_collection_hash).name])
490     post(:create, params: {
491            job: {
492              script: "hash",
493              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
494              repository: "active/foo",
495              script_parameters: {
496                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
497                an_integer: '1'
498              },
499            },
500            filters: filters_from_hash(filters_hash),
501            find_or_create: true,
502          })
503     assert_response :success
504     new_job = assigns(:object)
505     assert_not_nil new_job
506     target_job = jobs(:previous_docker_job_run)
507     [:uuid, :script_version, :docker_image_locator].each do |attr|
508       assert_equal(target_job.send(attr), new_job.send(attr))
509     end
510   end
511
512   test "reuse Job with Docker image repo+tag" do
513     filters_hash = BASE_FILTERS.
514       merge("script_version" =>
515               ["=", "4fe459abe02d9b365932b8f5dc419439ab4e2577"],
516             "docker_image_locator" =>
517               ["in docker", links(:docker_image_collection_tag2).name])
518     post(:create, params: {
519            job: {
520              script: "hash",
521              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
522              repository: "active/foo",
523              script_parameters: {
524                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
525                an_integer: '1'
526              },
527            },
528            filters: filters_from_hash(filters_hash),
529            find_or_create: true,
530          })
531     assert_response :success
532     new_job = assigns(:object)
533     assert_not_nil new_job
534     target_job = jobs(:previous_docker_job_run)
535     [:uuid, :script_version, :docker_image_locator].each do |attr|
536       assert_equal(target_job.send(attr), new_job.send(attr))
537     end
538   end
539
540   test "new job with unknown Docker image filter" do
541     filters_hash = BASE_FILTERS.
542       merge("docker_image_locator" => ["in docker", "_nonesuchname_"])
543     post(:create, params: {
544            job: {
545              script: "hash",
546              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
547              repository: "active/foo",
548              script_parameters: {
549                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
550                an_integer: '1'
551              },
552            },
553            filters: filters_from_hash(filters_hash),
554            find_or_create: true,
555          })
556     assert_response :success
557     new_job = assigns(:object)
558     assert_not_nil new_job
559     assert_not_equal(jobs(:previous_docker_job_run).uuid, new_job.uuid)
560   end
561
562   test "don't reuse job using older Docker image of same name" do
563     jobspec = {runtime_constraints: {
564         docker_image: "arvados/apitestfixture",
565       }}
566     check_new_job_created_from({job: jobspec},
567                                :previous_ancient_docker_image_job_run)
568   end
569
570   test "reuse job with Docker image that has hash name" do
571     jobspec = {runtime_constraints: {
572         docker_image: "a" * 64,
573       }}
574     check_job_reused_from(jobspec, :previous_docker_job_run)
575   end
576
577   ["repository", "script"].each do |skip_key|
578     test "missing #{skip_key} filter raises an error" do
579       filters = filters_from_hash(BASE_FILTERS.reject { |k| k == skip_key })
580       post(:create, params: {
581              job: {
582                script: "hash",
583                script_version: "master",
584                repository: "active/foo",
585                script_parameters: {
586                  input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
587                  an_integer: '1'
588                }
589              },
590              filters: filters,
591              find_or_create: true,
592            })
593       assert_includes(405..599, @response.code.to_i,
594                       "bad status code with missing #{skip_key} filter")
595     end
596   end
597
598   test "find Job with script version range" do
599     get :index, params: {
600       filters: [["repository", "=", "active/foo"],
601                 ["script", "=", "hash"],
602                 ["script_version", "in git", "tag1"]]
603     }
604     assert_response :success
605     assert_not_nil assigns(:objects)
606     assert_includes(assigns(:objects).map { |job| job.uuid },
607                     jobs(:previous_job_run).uuid)
608   end
609
610   test "find Job with script version range exclusions" do
611     get :index, params: {
612       filters: [["repository", "=", "active/foo"],
613                 ["script", "=", "hash"],
614                 ["script_version", "not in git", "tag1"]]
615     }
616     assert_response :success
617     assert_not_nil assigns(:objects)
618     refute_includes(assigns(:objects).map { |job| job.uuid },
619                     jobs(:previous_job_run).uuid)
620   end
621
622   test "find Job with Docker image range" do
623     get :index, params: {
624       filters: [["docker_image_locator", "in docker",
625                  "arvados/apitestfixture"]]
626     }
627     assert_response :success
628     assert_not_nil assigns(:objects)
629     assert_includes(assigns(:objects).map { |job| job.uuid },
630                     jobs(:previous_docker_job_run).uuid)
631     refute_includes(assigns(:objects).map { |job| job.uuid },
632                     jobs(:previous_job_run).uuid)
633   end
634
635   test "find Job with Docker image using reader tokens" do
636     authorize_with :inactive
637     get(:index, params: {
638           filters: [["docker_image_locator", "in docker",
639                      "arvados/apitestfixture"]],
640           reader_tokens: [api_token(:active)],
641         })
642     assert_response :success
643     assert_not_nil assigns(:objects)
644     assert_includes(assigns(:objects).map { |job| job.uuid },
645                     jobs(:previous_docker_job_run).uuid)
646     refute_includes(assigns(:objects).map { |job| job.uuid },
647                     jobs(:previous_job_run).uuid)
648   end
649
650   test "'in docker' filter accepts arrays" do
651     get :index, params: {
652       filters: [["docker_image_locator", "in docker",
653                 ["_nonesuchname_", "arvados/apitestfixture"]]]
654     }
655     assert_response :success
656     assert_not_nil assigns(:objects)
657     assert_includes(assigns(:objects).map { |job| job.uuid },
658                     jobs(:previous_docker_job_run).uuid)
659     refute_includes(assigns(:objects).map { |job| job.uuid },
660                     jobs(:previous_job_run).uuid)
661   end
662
663   test "'not in docker' filter accepts arrays" do
664     get :index, params: {
665       filters: [["docker_image_locator", "not in docker",
666                 ["_nonesuchname_", "arvados/apitestfixture"]]]
667     }
668     assert_response :success
669     assert_not_nil assigns(:objects)
670     assert_includes(assigns(:objects).map { |job| job.uuid },
671                     jobs(:previous_job_run).uuid)
672     refute_includes(assigns(:objects).map { |job| job.uuid },
673                     jobs(:previous_docker_job_run).uuid)
674   end
675
676   JOB_SUBMIT_KEYS = [:script, :script_parameters, :script_version, :repository]
677   DEFAULT_START_JOB = :previous_job_run
678
679   def create_job_params(params, start_from=DEFAULT_START_JOB)
680     if not params.has_key?(:find_or_create)
681       params[:find_or_create] = true
682     end
683     job_attrs = params.delete(:job) || {}
684     start_job = jobs(start_from)
685     params[:job] = Hash[JOB_SUBMIT_KEYS.map do |key|
686                           [key, start_job.send(key)]
687                         end]
688     params[:job][:runtime_constraints] =
689       job_attrs.delete(:runtime_constraints) || {}
690     { arvados_sdk_version: :arvados_sdk_version,
691       docker_image_locator: :docker_image }.each do |method, constraint_key|
692       if constraint_value = start_job.send(method)
693         params[:job][:runtime_constraints][constraint_key] ||= constraint_value
694       end
695     end
696     params[:job].merge!(job_attrs)
697     params
698   end
699
700   def create_job_from(params, start_from)
701     post(:create, params: create_job_params(params, start_from))
702     assert_response :success
703     new_job = assigns(:object)
704     assert_not_nil new_job
705     new_job
706   end
707
708   def check_new_job_created_from(params, start_from=DEFAULT_START_JOB)
709     start_time = Time.now
710     new_job = create_job_from(params, start_from)
711     assert_operator(start_time, :<=, new_job.created_at)
712     new_job
713   end
714
715   def check_job_reused_from(params, start_from)
716     new_job = create_job_from(params, start_from)
717     assert_equal(jobs(start_from).uuid, new_job.uuid)
718   end
719
720   def check_errors_from(params, start_from=DEFAULT_START_JOB)
721     post(:create, params: create_job_params(params, start_from))
722     assert_includes(405..499, @response.code.to_i)
723     errors = json_response.fetch("errors", [])
724     assert(errors.any?, "no errors assigned from #{params}")
725     refute(errors.any? { |msg| msg =~ /^#<[A-Za-z]+: / },
726            "errors include raw exception: #{errors.inspect}")
727     errors
728   end
729
730   # 1de84a8 is on the b1 branch, after master's tip.
731   test "new job created from unsatisfiable minimum version filter" do
732     filters_hash = BASE_FILTERS.merge("script_version" => ["in git", "1de84a8"])
733     check_new_job_created_from(filters: filters_from_hash(filters_hash))
734   end
735
736   test "new job created from unsatisfiable minimum version parameter" do
737     check_new_job_created_from(minimum_script_version: "1de84a8")
738   end
739
740   test "new job created from unsatisfiable minimum version attribute" do
741     check_new_job_created_from(job: {minimum_script_version: "1de84a8"})
742   end
743
744   test "graceful error from nonexistent minimum version filter" do
745     filters_hash = BASE_FILTERS.merge("script_version" =>
746                                       ["in git", "__nosuchbranch__"])
747     errors = check_errors_from(filters: filters_from_hash(filters_hash))
748     assert(errors.any? { |msg| msg.include? "__nosuchbranch__" },
749            "bad refspec not mentioned in error message")
750   end
751
752   test "graceful error from nonexistent minimum version parameter" do
753     errors = check_errors_from(minimum_script_version: "__nosuchbranch__")
754     assert(errors.any? { |msg| msg.include? "__nosuchbranch__" },
755            "bad refspec not mentioned in error message")
756   end
757
758   test "graceful error from nonexistent minimum version attribute" do
759     errors = check_errors_from(job: {minimum_script_version: "__nosuchbranch__"})
760     assert(errors.any? { |msg| msg.include? "__nosuchbranch__" },
761            "bad refspec not mentioned in error message")
762   end
763
764   test "don't reuse job with older Arvados SDK version specified by branch" do
765     jobspec = {runtime_constraints: {
766         arvados_sdk_version: "master",
767       }}
768     check_new_job_created_from({job: jobspec},
769                                :previous_job_run_with_arvados_sdk_version)
770   end
771
772   test "don't reuse job with older Arvados SDK version specified by commit" do
773     jobspec = {runtime_constraints: {
774         arvados_sdk_version: "ca68b24e51992e790f29df5cc4bc54ce1da4a1c2",
775       }}
776     check_new_job_created_from({job: jobspec},
777                                :previous_job_run_with_arvados_sdk_version)
778   end
779
780   test "don't reuse job with newer Arvados SDK version specified by commit" do
781     jobspec = {runtime_constraints: {
782         arvados_sdk_version: "436637c87a1d2bdbf4b624008304064b6cf0e30c",
783       }}
784     check_new_job_created_from({job: jobspec},
785                                :previous_job_run_with_arvados_sdk_version)
786   end
787
788   test "reuse job from arvados_sdk_version git filters" do
789     prev_job = jobs(:previous_job_run_with_arvados_sdk_version)
790     filters_hash = BASE_FILTERS.
791       merge("arvados_sdk_version" => ["in git", "commit2"],
792             "docker_image_locator" => ["=", prev_job.docker_image_locator])
793     filters_hash.delete("script_version")
794     params = create_job_params(filters: filters_from_hash(filters_hash))
795     post(:create, params: params)
796     assert_response :success
797     assert_equal(prev_job.uuid, assigns(:object).uuid)
798   end
799
800   test "create new job because of arvados_sdk_version 'not in git' filters" do
801     filters_hash = BASE_FILTERS.reject { |k| k == "script_version" }
802     filters = filters_from_hash(filters_hash)
803     # Allow anything from the root commit, but before commit 2.
804     filters += [["arvados_sdk_version", "in git", "436637c8"],
805                 ["arvados_sdk_version", "not in git", "00634b2b"]]
806     check_new_job_created_from(filters: filters)
807   end
808 end