3551: Merge branch 'master' into 3551-go-layout
[arvados.git] / services / api / app / controllers / arvados / v1 / jobs_controller.rb
index feeb82d9a16b41acedf0f8256eb894acf19f2490..69778293a4096138cba2607599a2c9d47efafe6b 100644 (file)
@@ -26,32 +26,35 @@ class Arvados::V1::JobsController < ApplicationController
     end
 
     if params[:find_or_create]
-      load_filters_param
+      return if false.equal?(load_filters_param)
       if @filters.empty?  # Translate older creation parameters into filters.
-        @filters = [:repository, :script].map do |attrsym|
-          [attrsym.to_s, "=", resource_attrs[attrsym]]
-        end
-        @filters.append(["script_version", "in",
-                         Commit.find_commit_range(current_user,
-                                                  resource_attrs[:repository],
-                                                  params[:minimum_script_version],
-                                                  resource_attrs[:script_version],
-                                                  params[:exclude_script_versions])])
+        @filters =
+          [["repository", "=", resource_attrs[:repository]],
+           ["script", "=", resource_attrs[:script]],
+           ["script_version", "in git",
+            params[:minimum_script_version] || resource_attrs[:script_version]],
+           ["script_version", "not in git", params[:exclude_script_versions]],
+          ].reject { |filter| filter.last.nil? }
         if image_search = resource_attrs[:runtime_constraints].andand["docker_image"]
-          image_tag = resource_attrs[:runtime_constraints]["docker_image_tag"]
-          image_locator = Collection.
-            uuids_for_docker_image(image_search, image_tag, @read_users).first
-          return super if image_locator.nil?  # We won't find anything to reuse.
-          @filters.append(["docker_image_locator", "=", image_locator])
+          if image_tag = resource_attrs[:runtime_constraints]["docker_image_tag"]
+            image_search += ":#{image_tag}"
+          end
+          @filters.append(["docker_image_locator", "in docker", image_search])
         else
           @filters.append(["docker_image_locator", "=", nil])
         end
-      else  # 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)
-            raise ArgumentError.new("#{req_filter} filter required")
-          end
+        begin
+          load_job_specific_filters
+        rescue ArgumentError => error
+          return send_error(error.message)
+        end
+      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
 
@@ -64,18 +67,20 @@ class Arvados::V1::JobsController < ApplicationController
         if j.nondeterministic != true and
             ((j.success == true and j.output != nil) or j.running == true) and
             j.script_parameters == resource_attrs[:script_parameters]
-          if j.running
+          if j.running && j.owner_uuid == current_user.uuid
             # We'll use this if we don't find a job that has completed
             incomplete_job ||= j
           else
-            # Record the first job in the list
-            if !@object
-              @object = j
-            end
-            # Ensure that all candidate jobs actually did produce the same output
-            if @object.output != j.output
-              @object = nil
-              break
+            if Collection.readable_by(current_user).find_by_uuid(j.output)
+              # Record the first job in the list
+              if !@object
+                @object = j
+              end
+              # Ensure that all candidate jobs actually did produce the same output
+              if @object.output != j.output
+                @object = nil
+                break
+              end
             end
           end
         end
@@ -152,20 +157,37 @@ class Arvados::V1::JobsController < ApplicationController
                     cancelled_at: nil,
                     success: nil
                   })
-    load_filters_param
+    return if false.equal?(load_filters_param)
     find_objects_for_index
     index
   end
 
+  def self._create_requires_parameters
+    (super rescue {}).
+      merge({
+              find_or_create: {
+                type: 'boolean', required: false, default: false
+              },
+              filters: {
+                type: 'array', required: false
+              },
+              minimum_script_version: {
+                type: 'string', required: false
+              },
+              exclude_script_versions: {
+                type: 'array', required: false
+              },
+            })
+  end
+
   def self._queue_requires_parameters
     self._index_requires_parameters
   end
 
   protected
 
-  def load_filters_param
-    # Convert Job-specific git and Docker filters into normal SQL filters.
-    super
+  def load_job_specific_filters
+    # Convert Job-specific @filters entries into general SQL filters.
     script_info = {"repository" => nil, "script" => nil}
     script_range = {"exclude_versions" => []}
     @filters.select! do |filter|
@@ -208,12 +230,28 @@ class Arvados::V1::JobsController < ApplicationController
         end
       end
       last_version = begin resource_attrs[:script_version] rescue "HEAD" end
-      @filters.append(["script_version", "in",
-                       Commit.find_commit_range(current_user,
-                                                script_info["repository"],
-                                                script_range["min_version"],
-                                                last_version,
-                                                script_range["exclude_versions"])])
+      version_range = Commit.find_commit_range(current_user,
+                                               script_info["repository"],
+                                               script_range["min_version"],
+                                               last_version,
+                                               script_range["exclude_versions"])
+      if version_range.nil?
+        raise ArgumentError.
+          new(["error searching #{script_info['repository']} from",
+               "#{script_range['min_version']} to #{last_version},",
+               "excluding #{script_range['exclude_versions']}"].join(" "))
+      end
+      @filters.append(["script_version", "in", version_range])
+    end
+  end
+
+  def load_filters_param
+    begin
+      super
+      load_job_specific_filters
+    rescue ArgumentError => error
+      send_error(error.message)
+      false
     end
   end
 end