X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/8ed521f7fd1e48e1e415125745ed8c6627a62c91..3a6b1a17f1b073e381b053b52e3cb0bb9c81d249:/services/api/app/models/job.rb diff --git a/services/api/app/models/job.rb b/services/api/app/models/job.rb index d078a6146b..37e5f455df 100644 --- a/services/api/app/models/job.rb +++ b/services/api/app/models/job.rb @@ -1,15 +1,41 @@ +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: AGPL-3.0 +# +# +# Legacy jobs API aka crunch v1 +# +# This is superceded by containers / container_requests (aka crunch v2) +# +# Arvados installations since the end of 2017 should have never +# used jobs, and are unaffected by this change. +# +# So that older Arvados sites don't lose access to legacy records, the +# API has been converted to read-only. Creating and updating jobs +# (and related types job_task, pipeline_template and +# pipeline_instance) is disabled and much of the business logic +# related has been removed, along with the crunch-dispatch.rb and +# various other code specific to the jobs API. +# +# If you need to resurrect any of this code, here is the last commit +# on master before the branch removing jobs API support: +# +# Wed Aug 7 14:49:38 2019 -0400 07d92519438a592d531f2c7558cd51788da262ca + +require 'log_reuse_info' +require 'safe_json' + class Job < ArvadosModel include HasUuid include KindAndEtag include CommonApiTemplate extend CurrentApiClient + extend LogReuseInfo serialize :components, Hash - attr_protected :arvados_sdk_version, :docker_image_locator serialize :script_parameters, Hash serialize :runtime_constraints, Hash serialize :tasks_summary, Hash before_create :ensure_unique_submit_id - after_commit :trigger_crunch_dispatch_if_cancelled, :on => :update before_validation :set_priority before_validation :update_state_from_old_state_attrs before_validation :update_script_parameters_digest @@ -21,11 +47,12 @@ class Job < ArvadosModel validate :ensure_no_collection_uuids_in_script_params before_save :tag_version_in_internal_repository before_save :update_timestamps_when_state_changes + before_create :create_disabled + before_update :update_disabled - has_many :commit_ancestors, :foreign_key => :descendant, :primary_key => :script_version has_many(:nodes, foreign_key: :job_uuid, primary_key: :uuid) - class SubmitIdReused < StandardError + class SubmitIdReused < RequestError end api_accessible :user, extend: :common do |t| @@ -71,6 +98,14 @@ class Job < ArvadosModel @need_crunch_dispatch_trigger = false end + def self.limit_index_columns_read + ["components"] + end + + def self.protected_attributes + [:arvados_sdk_version, :docker_image_locator] + end + def assert_finished update_attributes(finished_at: finished_at || db_current_time, success: success.nil? ? false : success, @@ -147,7 +182,7 @@ class Job < ArvadosModel image_hashes = Array.wrap(operand).flat_map do |search_term| image_search, image_tag = search_term.split(':', 2) Collection. - find_all_for_docker_image(image_search, image_tag, read_users). + find_all_for_docker_image(image_search, image_tag, read_users, filter_compatible_format: false). map(&:portable_data_hash) end filters << [attr, operator.sub(/ docker$/, ""), image_hashes] @@ -176,7 +211,7 @@ class Job < ArvadosModel else raise ArgumentError.new("unknown attribute for git filter: #{attr}") end - revisions = Commit.find_commit_range(filter["repository"], + revisions = CommitsHelper::find_commit_range(filter["repository"], filter["min_version"], filter["max_version"], filter["exclude_versions"]) @@ -192,92 +227,11 @@ class Job < ArvadosModel filters end - def self.find_reusable attrs, params, filters, read_users - if filters.empty? # Translate older creation parameters into filters. - filters = - [["repository", "=", attrs[:repository]], - ["script", "=", attrs[:script]], - ["script_version", "not in git", params[:exclude_script_versions]], - ].reject { |filter| filter.last.nil? or filter.last.empty? } - if !params[:minimum_script_version].blank? - filters << ["script_version", "in git", - params[:minimum_script_version]] - else - filters += default_git_filters("script_version", attrs[:repository], - attrs[:script_version]) - end - if image_search = attrs[:runtime_constraints].andand["docker_image"] - if image_tag = attrs[:runtime_constraints]["docker_image_tag"] - image_search += ":#{image_tag}" - end - image_locator = Collection. - for_latest_docker_image(image_search).andand.portable_data_hash - else - image_locator = nil - end - filters << ["docker_image_locator", "=", - Collection.docker_migration_pdh(read_users, image_locator)] - if sdk_version = attrs[:runtime_constraints].andand["arvados_sdk_version"] - filters += default_git_filters("arvados_sdk_version", "arvados", sdk_version) - end - filters = load_job_specific_filters(attrs, filters, read_users) - end - - # Check specified filters for some reasonableness. - filter_names = filters.map { |f| f.first }.uniq - ["repository", "script"].each do |req_filter| - if not filter_names.include?(req_filter) - return send_error("#{req_filter} filter required") - end - end - - # Search for a reusable Job, and return it if found. - candidates = Job. - readable_by(current_user). - where('state = ? or (owner_uuid = ? and state in (?))', - Job::Complete, current_user.uuid, [Job::Queued, Job::Running]). - where('script_parameters_digest = ?', Job.sorted_hash_digest(attrs[:script_parameters])). - where('nondeterministic is distinct from ?', true). - order('state desc, created_at') # prefer Running jobs over Queued - candidates = apply_filters candidates, filters - chosen = nil - incomplete_job = nil - candidates.each do |j| - if j.state != Job::Complete - # We'll use this if we don't find a job that has completed - incomplete_job ||= j - next - end - - if chosen == false - # We have already decided not to reuse any completed job - next - elsif chosen - if chosen.output != j.output - # If two matching jobs produced different outputs, run a new - # job (or use one that's already running/queued) instead of - # choosing one arbitrarily. - chosen = false - end - # ...and that's the only thing we need to do once we've chosen - # a job to reuse. - elsif !Collection.readable_by(current_user).find_by_portable_data_hash(j.output) - # As soon as the output we will end up returning (if any) is - # decided, check whether it will be visible to the user; if - # not, any further investigation of reusable jobs is futile. - chosen = false - else - chosen = j - end - end - chosen || incomplete_job - end - def self.default_git_filters(attr_name, repo_name, refspec) # Add a filter to @filters for `attr_name` = the latest commit available # in `repo_name` at `refspec`. No filter is added if refspec can't be # resolved. - commits = Commit.find_commit_range(repo_name, nil, refspec, nil) + commits = CommitsHelper::find_commit_range(repo_name, nil, refspec, nil) if commit_hash = commits.first [[attr_name, "=", commit_hash]] else @@ -286,36 +240,7 @@ class Job < ArvadosModel end def cancel(cascade: false, need_transaction: true) - if need_transaction - ActiveRecord::Base.transaction do - cancel(cascade: cascade, need_transaction: false) - end - return - end - - if self.state.in?([Queued, Running]) - self.state = Cancelled - self.save! - elsif self.state != Cancelled - raise InvalidStateTransitionError - end - - return if !cascade - - # cancel all children; they could be jobs or pipeline instances - children = self.components.andand.collect{|_, u| u}.compact - - return if children.empty? - - # cancel any child jobs - Job.where(uuid: children, state: [Queued, Running]).each do |job| - job.cancel(cascade: cascade, need_transaction: false) - end - - # cancel any child pipelines - PipelineInstance.where(uuid: children, state: [PipelineInstance::RunningOnServer, PipelineInstance::RunningOnClient]).each do |pi| - pi.cancel(cascade: cascade, need_transaction: false) - end + raise "No longer supported" end protected @@ -351,7 +276,7 @@ class Job < ArvadosModel return true end if new_record? or repository_changed? or script_version_changed? - sha1 = Commit.find_commit_range(repository, + sha1 = CommitsHelper::find_commit_range(repository, nil, script_version, nil).first if not sha1 errors.add :script_version, "#{script_version} does not resolve to a commit" @@ -376,7 +301,7 @@ class Job < ArvadosModel uuid_was = uuid begin assign_uuid - Commit.tag_in_internal_repository repository, script_version, uuid + CommitsHelper::tag_in_internal_repository repository, script_version, uuid rescue self.uuid = uuid_was raise @@ -411,7 +336,7 @@ class Job < ArvadosModel def find_arvados_sdk_version resolve_runtime_constraint("arvados_sdk_version", :arvados_sdk_version) do |git_search| - commits = Commit.find_commit_range("arvados", + commits = CommitsHelper::find_commit_range("arvados", nil, git_search, nil) if commits.empty? [false, "#{git_search} does not resolve to a commit"] @@ -424,9 +349,9 @@ class Job < ArvadosModel end def find_docker_image_locator - if runtime_constraints.is_a? Hash + if runtime_constraints.is_a? Hash and Rails.configuration.Containers.JobsAPI.DefaultDockerImage != "" runtime_constraints['docker_image'] ||= - Rails.configuration.default_docker_image_for_jobs + Rails.configuration.Containers.JobsAPI.DefaultDockerImage end resolve_runtime_constraint("docker_image", @@ -438,12 +363,6 @@ class Job < ArvadosModel [false, "not found for #{image_search}"] end end - Rails.logger.info("docker_image_locator is #{docker_image_locator}") - if docker_image_locator && docker_image_locator_changed? - self.docker_image_locator = - Collection.docker_migration_pdh([current_user], docker_image_locator) - end - Rails.logger.info("docker_image_locator is #{docker_image_locator}") end def permission_to_update @@ -506,14 +425,6 @@ class Job < ArvadosModel super end - def trigger_crunch_dispatch_if_cancelled - if @need_crunch_dispatch_trigger - File.open(Rails.configuration.crunch_refresh_trigger, 'wb') do - # That's all, just create/touch a file for crunch-job to see. - end - end - end - def update_timestamps_when_state_changes return if not (state_changed? or new_record?) @@ -642,4 +553,12 @@ class Job < ArvadosModel end false end + + def create_disabled + raise "Disabled" + end + + def update_disabled + raise "Disabled" + end end