Merge remote-tracking branch 'origin/master' into 2882-job-process-stats
authorPeter Amstutz <peter.amstutz@curoverse.com>
Wed, 4 Jun 2014 19:23:31 +0000 (15:23 -0400)
committerPeter Amstutz <peter.amstutz@curoverse.com>
Wed, 4 Jun 2014 19:23:31 +0000 (15:23 -0400)
17 files changed:
.gitignore
doc/api/methods/users.html.textile.liquid
doc/user/topics/tutorial-gatk-variantfiltration.html.textile.liquid
doc/user/topics/tutorial-job1.html.textile.liquid
docker/api/secret_token.rb.in
docker/build_tools/Makefile
docker/config.yml.example
docker/jobs/Dockerfile
sdk/cli/bin/arv
services/api/Gemfile
services/api/Gemfile.lock
services/api/app/controllers/arvados/v1/jobs_controller.rb
services/api/app/controllers/arvados/v1/users_controller.rb
services/api/app/models/job.rb
services/api/config/routes.rb
services/keep/src/keep/keep.go
services/keep/src/keep/volume_unix.go

index 602e1b9e40dbb8eda8a20ca1a607c40fa7e0b525..0cddee596c4cf665b9359f1a6276c7ecdcfd3d49 100644 (file)
@@ -16,3 +16,6 @@ services/keep/pkg
 services/keep/src/github.com
 sdk/java/target
 *.class
+apps/workbench/vendor/bundle
+services/api/vendor/bundle
+sdk/java/log
index 59fa856b493d98d0c6c31fbc08f89b15d80a141b..33f884b6cc7ffd9599343503a34db89ee90ff6ad 100644 (file)
@@ -42,10 +42,6 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the User in question.|path||
 
-h2. event_stream
-
-event_stream users
-
 Arguments:
 
 table(table table-bordered table-condensed).
index 0248325f61034511acefe6733a9e3d5a8b5f3d8c..ea608b4bc1c15a278f919abe8f8677e943f39b8c 100644 (file)
@@ -154,48 +154,8 @@ Now start a job:
   "5ee633fe2569d2a42dd81b07490d5d13+82",
   "c905c8d8443a9c44274d98b7c6cfaa32+94",
   "d237a90bae3870b3b033aea1e99de4a9+10820"
- ],
- "log_stream_href":"https://qr1hi.arvadosapi.com/arvados/v1/jobs/qr1hi-8i9sb-n9k7qyp7bs5b9d4/log_tail_follow"
+ ]
 }
-~$ <span class="userinput">arv job log_tail_follow --uuid qr1hi-8i9sb-n9k7qyp7bs5b9d4</span>
-Tue Dec 17 19:02:16 2013 salloc: Granted job allocation 1251
-Tue Dec 17 19:02:17 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  check slurm allocation
-Tue Dec 17 19:02:17 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  node compute13 - 8 slots
-Tue Dec 17 19:02:17 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  start
-Tue Dec 17 19:02:17 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  Install revision 76588bfc57f33ea1b36b82ca7187f465b73b4ca4
-Tue Dec 17 19:02:18 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  Clean-work-dir exited 0
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  Install exited 0
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  script GATK2-VariantFiltration
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  script_version 76588bfc57f33ea1b36b82ca7187f465b73b4ca4
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  script_parameters {"input":"5ee633fe2569d2a42dd81b07490d5d13+82","gatk_bundle":"d237a90bae3870b3b033aea1e99de4a9+10820","gatk_binary_tarball":"c905c8d8443a9c44274d98b7c6cfaa32+94"}
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  runtime_constraints {"max_tasks_per_node":0}
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  start level 0
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  status: 0 done, 0 running, 1 todo
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 0 job_task qr1hi-ot0gb-d3sjxerucfbvyev
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 0 child 4946 started on compute13.1
-Tue Dec 17 19:02:19 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  status: 0 done, 1 running, 0 todo
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 0 child 4946 on compute13.1 exit 0 signal 0 success=true
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 0 success in 1 seconds
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 0 output
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  wait for last 0 children to finish
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  status: 1 done, 0 running, 1 todo
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  start level 1
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  status: 1 done, 0 running, 1 todo
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 1 job_task qr1hi-ot0gb-w8ujbnulxjaamxf
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 1 child 4984 started on compute13.1
-Tue Dec 17 19:02:20 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  status: 1 done, 1 running, 0 todo
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 1 child 4984 on compute13.1 exit 0 signal 0 success=true
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 1 success in 110 seconds
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867 1 output bedd6ff56b3ae9f90d873b1fcb72f9a3+91
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  wait for last 0 children to finish
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  status: 2 done, 0 running, 0 todo
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  release job allocation
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  Freeze not implemented
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  collate
-Tue Dec 17 19:04:10 2013 salloc: Job allocation 1251 has been revoked.
-Tue Dec 17 19:04:10 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  output bedd6ff56b3ae9f90d873b1fcb72f9a3+91
-Tue Dec 17 19:04:11 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  finish
-Tue Dec 17 19:04:12 2013 qr1hi-8i9sb-n9k7qyp7bs5b9d4 4867  log manifest is 1e77aaceee2df499e14dc5dde5c3d328+91
 </code></pre>
 </notextile>
 
index f69381e4127510696c124019856942c8ac9b0c1f..cf09804871a624e383d0bccaeee4d39f43b66825 100644 (file)
@@ -76,8 +76,7 @@ Use @arv job create@ to actually submit the job.  It should print out a JSON obj
  "tasks_summary":{},
  "dependencies":[
   "c1bad4b39ca5a924e481008009d94e32+210"
- ],
- "log_stream_href":"https://qr1hi.arvadosapi.com/arvados/v1/jobs/qr1hi-8i9sb-1pm1t02dezhupss/log_tail_follow"
+ ]
 }
 </code></pre>
 </notextile>
@@ -91,12 +90,6 @@ h2. Monitor job progress
 
 Go to the "Workbench dashboard":https://{{site.arvados_workbench_host}} and visit *Activity* %(rarr)&rarr;% *Recent jobs*.  Your job should be near the top of the table.  This table refreshes automatically.  When the job has completed successfully, it will show <span class="label label-success">finished</span> in the *Status* column.
 
-On the command line, you can access log messages while the job runs using @arv job log_tail_follow@:
-
-notextile. <pre><code>~$ <span class="userinput">arv job log_tail_follow --uuid qr1hi-8i9sb-xxxxxxxxxxxxxxx</span></code></pre>
-
-This will print out the last several lines of the log for that job.
-
 h2. Inspect the job output
 
 On the "Workbench dashboard":https://{{site.arvados_workbench_host}}, look for the *Output* column of the *Recent jobs* table.  Click on the link under *Output* for your job to go to the files page with the job output.  The files page lists all the files that were output by the job.  Click on the link under the *file* column to view a file, or click on the download icon <span class="glyphicon glyphicon-download-alt"></span> to download the output file.
@@ -142,8 +135,7 @@ On the command line, you can use @arv job get@ to access a JSON object describin
  },
  "dependencies":[
   "c1bad4b39ca5a924e481008009d94e32+210"
- ],
- "log_stream_href":null
+ ]
 }
 </code></pre>
 </notextile>
index 201870bdd86932bf8e6720556085552cf9a658bd..30084803f08f7733ab8c127e25b1dfa77b68f946 100644 (file)
@@ -5,3 +5,8 @@
 # Make sure the secret is at least 30 characters and all random,
 # no regular words or you'll be exposed to dictionary attacks.
 Server::Application.config.secret_token = '@@API_SECRET@@'
+
+# The blob_signing_key is a string of alphanumeric characters used
+# to sign permission hints for Keep locators. It must be identical
+# to the permission key given to Keep.
+Server::Application.config.blob_signing_key = '@@KEEP_SIGNING_SECRET@@'
index 69db746326767729c5eacaa728db905400a7929a..298551fa1566b9b62ce3b588a8230e5c42cbc22b 100644 (file)
@@ -89,7 +89,8 @@ $(BUILD):
        rsync -rlp --exclude=docker/ --exclude='**/log/*' --exclude='**/tmp/*' \
                --chmod=Da+rx,Fa+rX ../ build/
        find build/ -name \*.gem -delete
-       cd build/sdk/python/ && ./build.sh
+       cd build/services/fuse/ && python setup.py build
+       cd build/sdk/python/ && python setup.py build
        cd build/sdk/cli && gem build arvados-cli.gemspec
        cd build/sdk/ruby && gem build arvados.gemspec
        touch build/.buildstamp
index da06fee255f046f701cc1b92585d106f45e16f8e..515fcbefd5121bbddb567c54aaa768b1008cc538 100644 (file)
@@ -45,6 +45,10 @@ ARVADOS_PROD_PW:
 # will be chosen randomly at build time. This is the
 # recommended setting.
 
+# The signing key shared by Keep at the API server to verify
+# blob permission signatures.
+KEEP_SIGNING_SECRET:
+
 # The value for the Rails config.secret_token setting.
 API_SECRET:
 
index 28ef858b8f3cd4bf9cfecf2d8fdc7d637f19927e..29c9d540b5f402828178586471dadd51a707a2cc 100644 (file)
@@ -14,6 +14,8 @@ RUN /usr/bin/apt-get install -q -y python-dev python-llfuse python-pip \
 # Install Arvados packages.
 RUN find /usr/src/arvados/sdk -name '*.gem' -print0 | \
       xargs -0rn 1 gem install && \
+    cd /usr/src/arvados/services/fuse && \
+    python setup.py install && \
     cd /usr/src/arvados/sdk/python && \
     python setup.py install
 
index d047204508fee386ee821bccbbd56fdde0a8dcfe..31cbeec70cb8b9d2b30116617a0e5586bc082591 100755 (executable)
@@ -313,8 +313,6 @@ end
 
 case api_method
 when
-  'arvados.users.event_stream',
-  'arvados.jobs.log_stream',
   'arvados.jobs.log_tail_follow'
 
   # Special case for methods that respond with data streams rather
index b0f85124a07b1010a940f19c9cbcc744fb8de485..105295db46c5df600b8e1eb303c237a0e59ae6e4 100644 (file)
@@ -60,7 +60,6 @@ gem 'omniauth', '1.1.1'
 gem 'omniauth-oauth2', '1.1.1'
 
 gem 'andand'
-gem 'redis'
 
 gem 'test_after_commit', :group => :test
 
index 4a4419f806cd3755b532e354404f2e67d200e570..c3b518062c76c6b2b0be09a120dcc9224c1c8ee9 100644 (file)
@@ -164,7 +164,6 @@ GEM
     rake (10.2.2)
     rdoc (3.12.2)
       json (~> 1.4)
-    redis (3.0.7)
     ref (1.0.5)
     rvm-capistrano (1.5.1)
       capistrano (~> 2.15.4)
@@ -228,7 +227,6 @@ DEPENDENCIES
   pg
   pg_power
   rails (~> 3.2.0)
-  redis
   rvm-capistrano
   sass-rails (>= 3.2.0)
   simplecov (~> 0.7.1)
index ee563010035bd92d7c7dcca9e305cfeb376ab34c..9043ac4b91d64b78d882885e2b2284b5c1a8c9cd 100644 (file)
@@ -117,51 +117,6 @@ 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
 
index 7311deb3860c9337914ea10fe3122dfad62bdad4..a67180912345331ce8872eb7b6fce16119a160b6 100644 (file)
@@ -2,9 +2,9 @@ class Arvados::V1::UsersController < ApplicationController
   accept_attribute_as_json :prefs, Hash
 
   skip_before_filter :find_object_by_uuid, only:
-    [:activate, :event_stream, :current, :system, :setup]
+    [:activate, :current, :system, :setup]
   skip_before_filter :render_404_if_no_object, only:
-    [:activate, :event_stream, :current, :system, :setup]
+    [:activate, :current, :system, :setup]
   before_filter :admin_required, only: [:setup, :unsetup]
 
   def current
@@ -16,39 +16,6 @@ class Arvados::V1::UsersController < ApplicationController
     show
   end
 
-  class ChannelStreamer
-    Q_UPDATE_INTERVAL = 12
-    def initialize(opts={})
-      @opts = opts
-    end
-    def each
-      return unless @opts[:channel]
-      @redis = Redis.new(:timeout => 0)
-      @redis.subscribe(@opts[:channel]) do |event|
-        event.message do |channel, msg|
-          yield msg + "\n"
-        end
-      end
-    end
-  end
-
-  def event_stream
-    channel = current_user.andand.uuid
-    if current_user.andand.is_admin
-      channel = params[:uuid] || channel
-    end
-    if client_accepts_plain_text_stream
-      self.response.headers['Last-Modified'] = Time.now.ctime.to_s
-      self.response_body = ChannelStreamer.new(channel: channel)
-    else
-      render json: {
-        href: url_for(uuid: channel),
-        comment: ('To retrieve the event stream as plain text, ' +
-                  'use a request header like "Accept: text/plain"')
-      }
-    end
-  end
-
   def activate
     if current_user.andand.is_admin && params[:uuid]
       @object = User.find params[:uuid]
index dc3539891f30ad325ddb99db524b62f513538eb6..51fb7c27832a21eec4193886e720d1c2c3363e23 100644 (file)
@@ -35,8 +35,6 @@ class Job < ArvadosModel
     t.add :runtime_constraints
     t.add :tasks_summary
     t.add :dependencies
-    t.add :log_stream_href
-    t.add :log_buffer
     t.add :nondeterministic
     t.add :repository
     t.add :supplied_script_version
@@ -48,12 +46,6 @@ class Job < ArvadosModel
                       running: false)
   end
 
-  def log_stream_href
-    unless self.finished_at
-      "#{current_api_base}/#{self.class.to_s.pluralize.underscore}/#{self.uuid}/log_tail_follow"
-    end
-  end
-
   def self.queue
     self.where('started_at is ? and is_locked_by_uuid is ? and cancelled_at is ? and success is ?',
                nil, nil, nil, nil).
@@ -189,15 +181,4 @@ class Job < ArvadosModel
       end
     end
   end
-
-  def log_buffer
-    begin
-      @@redis ||= Redis.new(:timeout => 0)
-      if @@redis.exists uuid
-        @@redis.getrange(uuid, 0 - 2**10, -1)
-      end
-    rescue Redis::CannotConnectError
-      return '(not available)'
-    end
-  end
 end
index 0223c04da4d4fc9783dce3eb61b0e8820a84f49d..e4d2975a571699d85d39887cd30bd24725639db2 100644 (file)
@@ -21,7 +21,6 @@ Server::Application.routes.draw do
       resources :job_tasks
       resources :jobs do
         get 'queue', on: :collection
-        get 'log_tail_follow', on: :member
         post 'cancel', on: :member
       end
       resources :keep_disks do
@@ -49,7 +48,6 @@ Server::Application.routes.draw do
       resources :users do
         get 'current', on: :collection
         get 'system', on: :collection
-        get 'event_stream', on: :member
         post 'activate', on: :member
         post 'setup', on: :collection
         post 'unsetup', on: :member
index fa7340d26f5f14c2e6d6242e9ea747a58dac7421..429a7e01b29b517e8757427412654e0d18dada67 100644 (file)
@@ -5,7 +5,6 @@ import (
        "bytes"
        "crypto/md5"
        "encoding/json"
-       "errors"
        "flag"
        "fmt"
        "github.com/gorilla/mux"
@@ -87,10 +86,6 @@ func (e *KeepError) Error() string {
        return e.ErrMsg
 }
 
-// This error is returned by ReadAtMost if the available
-// data exceeds BLOCKSIZE bytes.
-var ReadErrorTooLong = errors.New("Too long")
-
 // TODO(twp): continue moving as much code as possible out of main
 // so it can be effectively tested. Esp. handling and postprocessing
 // of command line flags (identifying Keep volumes and initializing
@@ -444,14 +439,18 @@ func PutBlockHandler(resp http.ResponseWriter, req *http.Request) {
        // Read the block data to be stored.
        // If the request exceeds BLOCKSIZE bytes, issue a HTTP 500 error.
        //
-       // Note: because req.Body is a buffered Reader, each Read() call will
-       // collect only the data in the network buffer (typically 16384 bytes),
-       // even if it is passed a much larger slice.
-       //
-       // Instead, call ReadAtMost to read data from the socket
-       // repeatedly until either EOF or BLOCKSIZE bytes have been read.
-       //
-       if buf, err := ReadAtMost(req.Body, BLOCKSIZE); err == nil {
+       if req.ContentLength > BLOCKSIZE {
+               http.Error(resp, TooLongError.Error(), TooLongError.HTTPCode)
+               return
+       }
+
+       buf := make([]byte, req.ContentLength)
+       nread, err := io.ReadFull(req.Body, buf)
+       if err != nil {
+               http.Error(resp, err.Error(), 500)
+       } else if int64(nread) < req.ContentLength {
+               http.Error(resp, "request truncated", 500)
+       } else {
                if err := PutBlock(buf, hash); err == nil {
                        // Success; add a size hint, sign the locator if
                        // possible, and return it to the client.
@@ -466,16 +465,8 @@ func PutBlockHandler(resp http.ResponseWriter, req *http.Request) {
                        ke := err.(*KeepError)
                        http.Error(resp, ke.Error(), ke.HTTPCode)
                }
-       } else {
-               log.Println("error reading request: ", err)
-               errmsg := err.Error()
-               if err == ReadErrorTooLong {
-                       // Use a more descriptive error message that includes
-                       // the maximum request size.
-                       errmsg = fmt.Sprintf("Max request size %d bytes", BLOCKSIZE)
-               }
-               http.Error(resp, errmsg, 500)
        }
+       return
 }
 
 // IndexHandler
@@ -691,24 +682,6 @@ func PutBlock(block []byte, hash string) error {
        }
 }
 
-// ReadAtMost
-//     Reads bytes repeatedly from an io.Reader until either
-//     encountering EOF, or the maxbytes byte limit has been reached.
-//     Returns a byte slice of the bytes that were read.
-//
-//     If the reader contains more than maxbytes, returns a nil slice
-//     and an error.
-//
-func ReadAtMost(r io.Reader, maxbytes int) ([]byte, error) {
-       // Attempt to read one more byte than maxbytes.
-       lr := io.LimitReader(r, int64(maxbytes+1))
-       buf, err := ioutil.ReadAll(lr)
-       if len(buf) > maxbytes {
-               return nil, ReadErrorTooLong
-       }
-       return buf, err
-}
-
 // IsValidLocator
 //     Return true if the specified string is a valid Keep locator.
 //     When Keep is extended to support hash types other than MD5,
index 88410def8e6d55b5af68226a134708362e8d973f..7b711d2eac1e7c6f5024cc49f723dc31c6c86952 100644 (file)
@@ -109,23 +109,13 @@ func (v *UnixVolume) Put(loc string, block []byte) error {
 // corrupted data block.
 //
 func (v *UnixVolume) Read(loc string) ([]byte, error) {
-       var f *os.File
-       var err error
-       var buf []byte
-
        blockFilename := filepath.Join(v.root, loc[0:3], loc)
-
-       f, err = os.Open(blockFilename)
+       buf, err := ioutil.ReadFile(blockFilename)
        if err != nil {
-               return nil, err
-       }
-
-       if buf, err = ioutil.ReadAll(f); err != nil {
                log.Printf("%s: reading %s: %s\n", v, blockFilename, err)
-               return buf, err
+               return nil, err
        }
 
-       // Success!
        return buf, nil
 }