X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/bd6b334c80cea328a51a8612d40ef16bdd6ab2e2..0eb72b526bf8bbb011551ecf019f604e17a534f1:/services/api/test/unit/job_test.rb diff --git a/services/api/test/unit/job_test.rb b/services/api/test/unit/job_test.rb index 1f80ea50f2..5f389c2545 100644 --- a/services/api/test/unit/job_test.rb +++ b/services/api/test/unit/job_test.rb @@ -1,7 +1,13 @@ +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: AGPL-3.0 + require 'test_helper' require 'helpers/git_test_helper' +require 'helpers/docker_migration_helper' class JobTest < ActiveSupport::TestCase + include DockerMigrationHelper include GitTestHelper BAD_COLLECTION = "#{'f' * 32}+0" @@ -174,26 +180,33 @@ class JobTest < ActiveSupport::TestCase [ {script_parameters: ""}, {script_parameters: []}, - {script_parameters: {symbols: :are_not_allowed_here}}, + {script_parameters: {["foo"] => ["bar"]}}, {runtime_constraints: ""}, {runtime_constraints: []}, {tasks_summary: ""}, {tasks_summary: []}, - {script_version: "no/branch/could/ever/possibly/have/this/name"}, ].each do |invalid_attrs| test "validation failures set error messages: #{invalid_attrs.to_json}" do # Ensure valid_attrs doesn't produce errors -- otherwise we will # not know whether errors reported below are actually caused by # invalid_attrs. - Job.create! job_attrs + Job.new(job_attrs).save! - job = Job.create job_attrs(invalid_attrs) - assert_raises(ActiveRecord::RecordInvalid, ArgumentError, - "save! did not raise the expected exception") do - job.save! + err = assert_raises(ArgumentError) do + Job.new(job_attrs(invalid_attrs)).save! end - assert_not_empty job.errors, "validation failure did not provide errors" + assert_match /parameters|constraints|summary/, err.message + end + end + + test "invalid script_version" do + invalid = { + script_version: "no/branch/could/ever/possibly/have/this/name", + } + err = assert_raises(ActiveRecord::RecordInvalid) do + Job.new(job_attrs(invalid)).save! end + assert_match /Script version .* does not resolve to a commit/, err.message end [ @@ -307,6 +320,24 @@ class JobTest < ActiveSupport::TestCase assert_equal "Failed", job.state end + test "admin user can cancel a running job despite lock" do + set_user_from_auth :active_trustedclient + job = Job.create! job_attrs + job.lock current_user.uuid + assert_equal Job::Running, job.state + + set_user_from_auth :spectator + assert_raises do + job.update_attributes!(state: Job::Cancelled) + end + + set_user_from_auth :admin + job.reload + assert_equal Job::Running, job.state + job.update_attributes!(state: Job::Cancelled) + assert_equal Job::Cancelled, job.state + end + test "verify job queue position" do job1 = Job.create! job_attrs assert_equal 'Queued', job1.state, "Incorrect job state for newly created job1" @@ -393,6 +424,73 @@ class JobTest < ActiveSupport::TestCase "Job with SDK constraint valid after clearing Docker image") end + test "use migrated docker image if requesting old-format image by tag" do + Rails.configuration.docker_image_formats = ['v2'] + add_docker19_migration_link + job = Job.create!( + job_attrs( + script: 'foo', + runtime_constraints: { + 'docker_image' => links(:docker_image_collection_tag).name})) + assert(job.valid?) + assert_equal(job.docker_image_locator, collections(:docker_image_1_12).portable_data_hash) + end + + test "use migrated docker image if requesting old-format image by pdh" do + Rails.configuration.docker_image_formats = ['v2'] + add_docker19_migration_link + job = Job.create!( + job_attrs( + script: 'foo', + runtime_constraints: { + 'docker_image' => collections(:docker_image).portable_data_hash})) + assert(job.valid?) + assert_equal(job.docker_image_locator, collections(:docker_image_1_12).portable_data_hash) + end + + [[:docker_image, :docker_image, :docker_image_1_12], + [:docker_image_1_12, :docker_image, :docker_image_1_12], + [:docker_image, :docker_image_1_12, :docker_image_1_12], + [:docker_image_1_12, :docker_image_1_12, :docker_image_1_12], + ].each do |existing_image, request_image, expect_image| + test "if a #{existing_image} job exists, #{request_image} yields #{expect_image} after migration" do + Rails.configuration.docker_image_formats = ['v1'] + + if existing_image == :docker_image + oldjob = Job.create!( + job_attrs( + script: 'foobar1', + runtime_constraints: { + 'docker_image' => collections(existing_image).portable_data_hash})) + oldjob.reload + assert_equal(oldjob.docker_image_locator, + collections(existing_image).portable_data_hash) + elsif existing_image == :docker_image_1_12 + assert_raises(ActiveRecord::RecordInvalid, + "Should not resolve v2 image when only v1 is supported") do + oldjob = Job.create!( + job_attrs( + script: 'foobar1', + runtime_constraints: { + 'docker_image' => collections(existing_image).portable_data_hash})) + end + end + + Rails.configuration.docker_image_formats = ['v2'] + add_docker19_migration_link + + # Check that both v1 and v2 images get resolved to v2. + newjob = Job.create!( + job_attrs( + script: 'foobar1', + runtime_constraints: { + 'docker_image' => collections(request_image).portable_data_hash})) + newjob.reload + assert_equal(newjob.docker_image_locator, + collections(expect_image).portable_data_hash) + end + end + test "can't create job with SDK version assigned directly" do check_creation_prohibited(arvados_sdk_version: SDK_MASTER) end @@ -463,7 +561,18 @@ class JobTest < ActiveSupport::TestCase assert_equal Job.deep_sort_hash(a).to_json, Job.deep_sort_hash(b).to_json end - test 'find_reusable' do + test 'find_reusable without logging' do + Rails.logger.expects(:info).never + try_find_reusable + end + + test 'find_reusable with logging' do + Rails.configuration.log_reuse_decisions = true + Rails.logger.expects(:info).at_least(3) + try_find_reusable + end + + def try_find_reusable foobar = jobs(:foobar) example_attrs = { script_version: foobar.script_version, @@ -483,5 +592,57 @@ class JobTest < ActiveSupport::TestCase Job.where(uuid: jobs(:job_with_latest_version).uuid). update_all(output: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+1') assert_nil Job.find_reusable(example_attrs, {}, [], [users(:active)]) + + # ...unless config says to reuse the earlier job in such cases. + Rails.configuration.reuse_job_if_outputs_differ = true + j = Job.find_reusable(example_attrs, {}, [], [users(:active)]) + assert_equal foobar.uuid, j.uuid + end + + [ + true, + false, + ].each do |cascade| + test "cancel job with cascade #{cascade}" do + job = Job.find_by_uuid jobs(:running_job_with_components_at_level_1).uuid + job.cancel cascade: cascade + assert_equal Job::Cancelled, job.state + + descendents = ['zzzzz-8i9sb-jobcomponentsl2', + 'zzzzz-d1hrv-picomponentsl02', + 'zzzzz-8i9sb-job1atlevel3noc', + 'zzzzz-8i9sb-job2atlevel3noc'] + + jobs = Job.where(uuid: descendents) + jobs.each do |j| + assert_equal ('Cancelled' == j.state), cascade + end + + pipelines = PipelineInstance.where(uuid: descendents) + pipelines.each do |pi| + assert_equal ('Paused' == pi.state), cascade + end + end + end + + test 'cancelling a completed job raises error' do + job = Job.find_by_uuid jobs(:job_with_latest_version).uuid + assert job + assert_equal 'Complete', job.state + + assert_raises(ArvadosModel::InvalidStateTransitionError) do + job.cancel + end + end + + test 'cancelling a job with circular relationship with another does not result in an infinite loop' do + job = Job.find_by_uuid jobs(:running_job_2_with_circular_component_relationship).uuid + + job.cancel cascade: true + + assert_equal Job::Cancelled, job.state + + child = Job.find_by_uuid job.components.collect{|_, uuid| uuid}[0] + assert_equal Job::Cancelled, child.state end end