2879: API server can find_or_create Jobs based on filters.
[arvados.git] / services / api / app / controllers / arvados / v1 / jobs_controller.rb
index 8ef6acdc38cf21d9e025e5a9e31478019357698f..6fddba7a56c39a80dd94c6e46250f4bc39ae9361 100644 (file)
@@ -9,10 +9,11 @@ class Arvados::V1::JobsController < ApplicationController
     [:repository, :script, :script_version, :script_parameters].each do |r|
       if !resource_attrs[r]
         return render json: {
-          :error => "#{r} attribute must be specified"
+          :errors => ["#{r} attribute must be specified"]
         }, status: :unprocessable_entity
       end
     end
+    load_filters_param
 
     # We used to ask for the minimum_, exclude_, and no_reuse params
     # in the job resource. Now we advertise them as flags that alter
@@ -27,18 +28,48 @@ class Arvados::V1::JobsController < ApplicationController
     end
 
     if params[:find_or_create]
-      r = Commit.find_commit_range(current_user,
-                                   resource_attrs[:repository],
-                                   params[:minimum_script_version],
-                                   resource_attrs[:script_version],
-                                   params[:exclude_script_versions])
-      # Search for jobs whose script_version is in the list of commits
-      # returned by find_commit_range
+      # Convert old special-purpose creation parameters to the new
+      # filters-based method.
+      minimum_script_version = params[:minimum_script_version]
+      exclude_script_versions = params.fetch(:exclude_script_versions, [])
+      @filters.select do |(col_name, operand, operator)|
+        case col_name
+        when "script_version"
+          case operand
+          when "in range"
+            minimum_script_version = operator
+            false
+          when "not in", "not in range"
+            begin
+              exclude_script_versions += operator
+            rescue TypeError
+              exclude_script_versions << operator
+            end
+            false
+          else
+            true
+          end
+        else
+          true
+        end
+      end
+      @filters.append(["script_version", "in",
+                       Commit.find_commit_range(current_user,
+                                                resource_attrs[:repository],
+                                                minimum_script_version,
+                                                resource_attrs[:script_version],
+                                                exclude_script_versions)])
+
+      # Set up default filters for specific parameters.
+      if @filters.select { |f| f.first == "script" }.empty?
+        @filters.append(["script", "=", resource_attrs[:script]])
+      end
+
+      @objects = Job.readable_by(current_user)
+      apply_filters
       @object = nil
       incomplete_job = nil
-      Job.readable_by(current_user).where(script: resource_attrs[:script],
-                                          script_version: r).
-        each do |j|
+      @objects.each do |j|
         if j.nondeterministic != true and
             ((j.success == true and j.output != nil) or j.running == true) and
             j.script_parameters == resource_attrs[:script_parameters]
@@ -117,55 +148,12 @@ class Arvados::V1::JobsController < ApplicationController
           @job.reload
         end
       end
-      @redis = Redis.new(:timeout => 0)
-      if @redis.exists @job.uuid
-        # A log buffer exists. Start by showing the last few KB.
-        @redis.
-          getrange(@job.uuid, 0 - [@opts[:buffer_size], 1].max, -1).
-          sub(/^[^\n]*\n?/, '').
-          split("\n").
-          each do |line|
-          yield "#{line}\n"
-        end
-      end
-      # TODO: avoid missing log entries between getrange() above and
-      # subscribe() below.
-      @redis.subscribe(@job.uuid) do |event|
-        event.message do |channel, msg|
-          if msg == "end"
-            @redis.unsubscribe @job.uuid
-          else
-            yield "#{msg}\n"
-          end
-        end
-      end
-    end
-  end
-
-  def self._log_tail_follow_requires_parameters
-    {
-      buffer_size: {type: 'integer', required: false, default: 2**13}
-    }
-  end
-  def log_tail_follow
-    if !@object.andand.uuid
-      return render_not_found
-    end
-    if client_accepts_plain_text_stream
-      self.response.headers['Last-Modified'] = Time.now.ctime.to_s
-      self.response_body = LogStreamer.new @object, {
-        buffer_size: (params[:buffer_size].to_i rescue 2**13)
-      }
-    else
-      render json: {
-        href: url_for(uuid: @object.uuid),
-        comment: ('To retrieve the log stream as plain text, ' +
-                  'use a request header like "Accept: text/plain"')
-      }
     end
   end
 
   def queue
+    params[:order] ||= ['priority desc', 'created_at']
+    load_limit_offset_order_params
     load_where_param
     @where.merge!({
                     started_at: nil,
@@ -173,7 +161,7 @@ class Arvados::V1::JobsController < ApplicationController
                     cancelled_at: nil,
                     success: nil
                   })
-    params[:order] ||= 'priority desc, created_at'
+    load_filters_param
     find_objects_for_index
     index
   end