Merge branch '11017-docker-migration'
[arvados.git] / services / api / app / models / job.rb
index e7d1b39ce9972f3fd5be785f56f4f7b0bcc57685..d078a6146b75a7353be2dfdab90938b81440a2f8 100644 (file)
@@ -67,6 +67,10 @@ class Job < ArvadosModel
             (Complete = 'Complete'),
            ]
 
+  after_initialize do
+    @need_crunch_dispatch_trigger = false
+  end
+
   def assert_finished
     update_attributes(finished_at: finished_at || db_current_time,
                       success: success.nil? ? false : success,
@@ -115,6 +119,10 @@ class Job < ArvadosModel
     super - ["script_parameters_digest"]
   end
 
+  def self.full_text_searchable_columns
+    super - ["script_parameters_digest"]
+  end
+
   def self.load_job_specific_filters attrs, orig_filters, read_users
     # Convert Job-specific @filters entries into general SQL filters.
     script_info = {"repository" => nil, "script" => nil}
@@ -207,7 +215,8 @@ class Job < ArvadosModel
       else
         image_locator = nil
       end
-      filters << ["docker_image_locator", "=", image_locator]
+      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
@@ -276,24 +285,45 @@ class Job < ArvadosModel
     end
   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
+  end
+
   protected
 
   def self.sorted_hash_digest h
     Digest::MD5.hexdigest(Oj.dump(deep_sort_hash(h)))
   end
 
-  def self.deep_sort_hash x
-    if x.is_a? Hash
-      x.sort.collect do |k, v|
-        [k, deep_sort_hash(v)]
-      end.to_h
-    elsif x.is_a? Array
-      x.collect { |v| deep_sort_hash(v) }
-    else
-      x
-    end
-  end
-
   def foreign_key_attributes
     super + %w(output log)
   end
@@ -348,7 +378,7 @@ class Job < ArvadosModel
         assign_uuid
         Commit.tag_in_internal_repository repository, script_version, uuid
       rescue
-        uuid = uuid_was
+        self.uuid = uuid_was
         raise
       end
     end
@@ -394,10 +424,11 @@ class Job < ArvadosModel
   end
 
   def find_docker_image_locator
-    runtime_constraints['docker_image'] =
-        Rails.configuration.default_docker_image_for_jobs if ((runtime_constraints.is_a? Hash) and
-                                                              (runtime_constraints['docker_image']).nil? and
-                                                              Rails.configuration.default_docker_image_for_jobs)
+    if runtime_constraints.is_a? Hash
+      runtime_constraints['docker_image'] ||=
+        Rails.configuration.default_docker_image_for_jobs
+    end
+
     resolve_runtime_constraint("docker_image",
                                :docker_image_locator) do |image_search|
       image_tag = runtime_constraints['docker_image_tag']
@@ -407,6 +438,12 @@ 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
@@ -427,7 +464,7 @@ class Job < ArvadosModel
           output_changed? or
           log_changed? or
           tasks_summary_changed? or
-          state_changed? or
+          (state_changed? && state != Cancelled) or
           components_changed?
         logger.warn "User #{current_user.uuid if current_user} tried to change protected job attributes on locked #{self.class.to_s} #{uuid_was}"
         return false
@@ -577,24 +614,6 @@ class Job < ArvadosModel
   end
 
   def ensure_no_collection_uuids_in_script_params
-    # recursive_hash_search searches recursively through hashes and
-    # arrays in 'thing' for string fields matching regular expression
-    # 'pattern'.  Returns true if pattern is found, false otherwise.
-    def recursive_hash_search thing, pattern
-      if thing.is_a? Hash
-        thing.each do |k, v|
-          return true if recursive_hash_search v, pattern
-        end
-      elsif thing.is_a? Array
-        thing.each do |k|
-          return true if recursive_hash_search k, pattern
-        end
-      elsif thing.is_a? String
-        return true if thing.match pattern
-      end
-      false
-    end
-
     # Fail validation if any script_parameters field includes a string containing a
     # collection uuid pattern.
     if self.script_parameters_changed?
@@ -605,4 +624,22 @@ class Job < ArvadosModel
     end
     true
   end
+
+  # recursive_hash_search searches recursively through hashes and
+  # arrays in 'thing' for string fields matching regular expression
+  # 'pattern'.  Returns true if pattern is found, false otherwise.
+  def recursive_hash_search thing, pattern
+    if thing.is_a? Hash
+      thing.each do |k, v|
+        return true if recursive_hash_search v, pattern
+      end
+    elsif thing.is_a? Array
+      thing.each do |k|
+        return true if recursive_hash_search k, pattern
+      end
+    elsif thing.is_a? String
+      return true if thing.match pattern
+    end
+    false
+  end
 end