5737: Merge branch 'master' into 5737-ruby231
authorLucas Di Pentima <lucas@curoverse.com>
Tue, 22 Nov 2016 16:40:45 +0000 (13:40 -0300)
committerLucas Di Pentima <lucas@curoverse.com>
Tue, 22 Nov 2016 16:40:45 +0000 (13:40 -0300)
12 files changed:
1  2 
apps/workbench/app/controllers/application_controller.rb
build/run-tests.sh
services/api/Gemfile
services/api/Gemfile.lock
services/api/app/controllers/application_controller.rb
services/api/app/models/arvados_model.rb
services/api/app/models/collection.rb
services/api/app/models/log.rb
services/api/app/models/node.rb
services/api/app/models/user.rb
services/api/test/functional/arvados/v1/collections_controller_test.rb
services/api/test/functional/arvados/v1/jobs_controller_test.rb

index b2f5c70cbb69a2a2f0861f127aa88ef5d3a59d99,c9ce8ce0b748a9473d2cd5f80739d070f1f8aef5..ee3ac4d6810588b7d630705a5efe4cd6e08bd6ae
@@@ -13,6 -13,7 +13,7 @@@ class ApplicationController < ActionCon
    # Methods that don't require login should
    #   skip_around_filter :require_thread_api_token
    around_filter :require_thread_api_token, except: ERROR_ACTIONS
+   before_filter :ensure_arvados_api_exists, only: [:index, :show]
    before_filter :set_cache_buster
    before_filter :accept_uuid_as_id_param, except: ERROR_ACTIONS
    before_filter :check_user_agreements, except: ERROR_ACTIONS
      end
    end
  
+   def ensure_arvados_api_exists
+     if model_class.is_a?(Class) && model_class < ArvadosBase && !model_class.api_exists?(params['action'].to_sym)
+       @errors = ["#{params['action']} method is not supported for #{params['controller']}"]
+       return render_error(status: 404)
+     end
+   end
    def index
      find_objects_for_index if !@objects
      render_index
      begin
        if not model_class
          @object = nil
+       elsif params[:uuid].nil? or params[:uuid].empty?
+         @object = nil
        elsif not params[:uuid].is_a?(String)
          @object = model_class.where(uuid: params[:uuid]).first
-       elsif params[:uuid].empty?
-         @object = nil
        elsif (model_class != Link and
               resource_class_for_uuid(params[:uuid]) == Link)
          @name_link = Link.find(params[:uuid])
          @object = model_class.find(@name_link.head_uuid)
        else
          @object = model_class.find(params[:uuid])
+         load_preloaded_objects [@object]
        end
      rescue ArvadosApiClient::NotFoundException, ArvadosApiClient::NotLoggedInException, RuntimeError => error
        if error.is_a?(RuntimeError) and (error.message !~ /^argument to find\(/)
    }
  
    @@notification_tests.push lambda { |controller, current_user|
-     PipelineInstance.limit(1).where(created_by: current_user.uuid).each do
+     if PipelineInstance.api_exists?(:index)
+       PipelineInstance.limit(1).where(created_by: current_user.uuid).each do
+         return nil
+       end
+     else
        return nil
      end
      return lambda { |view|
    def recent_processes lim
      lim = 12 if lim.nil?
  
-     cols = %w(uuid owner_uuid created_at modified_at pipeline_template_uuid name state started_at finished_at)
-     pipelines = PipelineInstance.select(cols).limit(lim).order(["created_at desc"])
+     procs = {}
+     if PipelineInstance.api_exists?(:index)
+       cols = %w(uuid owner_uuid created_at modified_at pipeline_template_uuid name state started_at finished_at)
+       pipelines = PipelineInstance.select(cols).limit(lim).order(["created_at desc"])
+       pipelines.results.each { |pi| procs[pi] = pi.created_at }
+     end
  
      crs = ContainerRequest.limit(lim).order(["created_at desc"]).filter([["requesting_container_uuid", "=", nil]])
-     procs = {}
-     pipelines.results.each { |pi| procs[pi] = pi.created_at }
      crs.results.each { |c| procs[c] = c.created_at }
  
      Hash[procs.sort_by {|key, value| value}].keys.reverse.first(lim)
    # from the top three levels.
    # That is: get toplevel projects under home, get subprojects of
    # these projects, and so on until we hit the limit.
 -  def my_wanted_projects user, page_size=100
 +  def my_wanted_projects(user, page_size=100)
      return @my_wanted_projects if @my_wanted_projects
  
      from_top = []
        break if current_level.results.size == 0
        @too_many_projects = true if current_level.items_available > current_level.results.size
        from_top.concat current_level.results
 -      uuids = current_level.results.collect { |x| x.uuid }
 +      uuids = current_level.results.collect(&:uuid)
        depth += 1
        if depth >= 3
          @reached_level_limit = true
    end
  
    helper_method :my_wanted_projects_tree
 -  def my_wanted_projects_tree user, page_size=100
 -    build_my_wanted_projects_tree user, page_size
 +  def my_wanted_projects_tree(user, page_size=100)
 +    build_my_wanted_projects_tree(user, page_size)
      [@my_wanted_projects_tree, @too_many_projects, @reached_level_limit]
    end
  
 -  def build_my_wanted_projects_tree user, page_size=100
 +  def build_my_wanted_projects_tree(user, page_size=100)
      return @my_wanted_projects_tree if @my_wanted_projects_tree
  
      parent_of = {user.uuid => 'me'}
  
    # helper method to get object of a given dataclass and uuid
    helper_method :object_for_dataclass
-   def object_for_dataclass dataclass, uuid
+   def object_for_dataclass dataclass, uuid, by_attr=nil
      raise ArgumentError, 'No input argument dataclass' unless (dataclass && uuid)
-     preload_objects_for_dataclass(dataclass, [uuid])
+     preload_objects_for_dataclass(dataclass, [uuid], by_attr)
      @objects_for[uuid]
    end
  
    # helper method to preload objects for given dataclass and uuids
    helper_method :preload_objects_for_dataclass
-   def preload_objects_for_dataclass dataclass, uuids
+   def preload_objects_for_dataclass dataclass, uuids, by_attr=nil
      @objects_for ||= {}
  
      raise ArgumentError, 'Argument is not a data class' unless dataclass.is_a? Class
      uuids.each do |x|
        @objects_for[x] = nil
      end
-     dataclass.where(uuid: uuids).each do |obj|
-       @objects_for[obj.uuid] = obj
+     if by_attr and ![:uuid, :name].include?(by_attr)
+       raise ArgumentError, "Preloading only using lookups by uuid or name are supported: #{by_attr}"
+     elsif by_attr and by_attr == :name
+       dataclass.where(name: uuids).each do |obj|
+         @objects_for[obj.name] = obj
+       end
+     else
+       dataclass.where(uuid: uuids).each do |obj|
+         @objects_for[obj.uuid] = obj
+       end
      end
      @objects_for
    end
  
+   # helper method to load objects that are already preloaded
+   helper_method :load_preloaded_objects
+   def load_preloaded_objects objs
+     @objects_for ||= {}
+     objs.each do |obj|
+       @objects_for[obj.uuid] = obj
+     end
+   end
    def wiselinks_layout
      'body'
    end
diff --combined build/run-tests.sh
index ef84a2f41cab1d63781db43389a6d03cc8a3b944,8959cfbe09c3ea7ac6ded2142b626259787d2121..d08568cde58b97bdede5b430eda4f0b788f4e3ce
@@@ -93,6 -93,7 +93,7 @@@ sdk/go/streame
  sdk/go/crunchrunner
  sdk/cwl
  tools/crunchstat-summary
+ tools/keep-exercise
  tools/keep-rsync
  tools/keep-block-check
  
@@@ -155,17 -156,11 +156,17 @@@ sanity_checks() 
      echo -n 'virtualenv: '
      virtualenv --version \
          || fatal "No virtualenv. Try: apt-get install virtualenv (on ubuntu: python-virtualenv)"
 +    echo -n 'ruby: '
 +    ruby -v \
 +        || fatal "No ruby. Install >=2.1.9 (using rbenv, rvm, or source)"
 +    echo -n 'bundler: '
 +    bundle version \
 +        || fatal "No bundler. Try: gem install bundler"
      echo -n 'go: '
      go version \
          || fatal "No go binary. See http://golang.org/doc/install"
-     [[ $(go version) =~ go1.([0-9]+) ]] && [[ ${BASH_REMATCH[1]} -ge 6 ]] \
-         || fatal "Go >= 1.6 required. See http://golang.org/doc/install"
+     [[ $(go version) =~ go1.([0-9]+) ]] && [[ ${BASH_REMATCH[1]} -ge 7 ]] \
+         || fatal "Go >= 1.7 required. See http://golang.org/doc/install"
      echo -n 'gcc: '
      gcc --version | egrep ^gcc \
          || fatal "No gcc. Try: apt-get install build-essential"
@@@ -770,8 -765,9 +771,9 @@@ gostuff=
      services/crunch-dispatch-local
      services/crunch-dispatch-slurm
      services/crunch-run
-     tools/keep-rsync
      tools/keep-block-check
+     tools/keep-exercise
+     tools/keep-rsync
      )
  for g in "${gostuff[@]}"
  do
diff --combined services/api/Gemfile
index dfaa07049bd31e818f5ca5231e455545c5488145,5d9b031e0295a62b86a6f0b4d6e9c13cc784da70..7be039e8a304aea0d6248d5151d969562dab1471
@@@ -1,6 -1,6 +1,6 @@@
  source 'https://rubygems.org'
  
 -gem 'rails', '~> 3.2.0'
 +gem 'rails', '~> 3.2'
  
  # Bundle edge Rails instead:
  # gem 'rails',     :git => 'git://github.com/rails/rails.git'
@@@ -12,15 -12,14 +12,15 @@@ group :test, :development d
    # Note: "require: false" here tells bunder not to automatically
    # 'require' the packages during application startup. Installation is
    # still mandatory.
 +  gem 'test-unit', '~> 3.0', require: false
 +  gem 'minitest-rails', require: false
 +  gem 'minitest', '~> 4.0', require: false
    gem 'simplecov', '~> 0.7.1', require: false
    gem 'simplecov-rcov', require: false
    gem 'mocha', require: false
  end
  
 -# This might not be needed in :test and :development, but we load it
 -# anyway to make sure it always gets in Gemfile.lock and to help
 -# reveal install problems sooner rather than later.
 +# pg is the only supported database driver.
  gem 'pg'
  
  # Start using multi_json once we are on Rails 3.2;
@@@ -32,13 -31,13 +32,13 @@@ gem 'oj
  # Gems used only for assets and not required
  # in production environments by default.
  group :assets do
 -  gem 'sass-rails',   '>= 3.2.0'
 -  gem 'coffee-rails', '~> 3.2.0'
 +  gem 'sass-rails',   '~> 3.2'
 +  gem 'coffee-rails', '~> 3.2'
  
    # See https://github.com/sstephenson/execjs#readme for more supported runtimes
    gem 'therubyracer'
  
 -  gem 'uglifier', '>= 1.0.3'
 +  gem 'uglifier', '~> 2.0'
  end
  
  gem 'jquery-rails'
@@@ -61,8 -60,8 +61,8 @@@ gem 'acts_as_api
  
  gem 'passenger'
  
 -gem 'omniauth', '1.1.1'
 -gem 'omniauth-oauth2', '1.1.1'
 +gem 'omniauth', '~> 1.1'
 +gem 'omniauth-oauth2', '~> 1.1'
  
  gem 'andand'
  
@@@ -74,12 -73,12 +74,12 @@@ gem 'faye-websocket
  gem 'themes_for_rails'
  
  gem 'arvados', '>= 0.1.20150615153458'
- gem 'arvados-cli', '>= 0.1.20151207150126'
+ gem 'arvados-cli', '>= 0.1.20161017193526'
  
  # pg_power lets us use partial indexes in schema.rb in Rails 3
  gem 'pg_power'
  
 -gem 'puma'
 +gem 'puma', '~> 2.0'
  gem 'sshkey'
  gem 'safe_yaml'
  gem 'lograge'
index d9edcc64fa4ee48e455e4ad17dff6ac3821b5172,6f7875163b63fa6af8462f2999e42bad4902f37d..3e551a0c3fab6d425de31b8f3354ba7f1474edfe
@@@ -1,12 -1,12 +1,12 @@@
  GEM
    remote: https://rubygems.org/
    specs:
 -    actionmailer (3.2.17)
 -      actionpack (= 3.2.17)
 +    actionmailer (3.2.22.5)
 +      actionpack (= 3.2.22.5)
        mail (~> 2.5.4)
 -    actionpack (3.2.17)
 -      activemodel (= 3.2.17)
 -      activesupport (= 3.2.17)
 +    actionpack (3.2.22.5)
 +      activemodel (= 3.2.22.5)
 +      activesupport (= 3.2.22.5)
        builder (~> 3.0.0)
        erubis (~> 2.7.0)
        journey (~> 1.0.4)
        rack-cache (~> 1.2)
        rack-test (~> 0.6.1)
        sprockets (~> 2.2.1)
 -    activemodel (3.2.17)
 -      activesupport (= 3.2.17)
 +    activemodel (3.2.22.5)
 +      activesupport (= 3.2.22.5)
        builder (~> 3.0.0)
 -    activerecord (3.2.17)
 -      activemodel (= 3.2.17)
 -      activesupport (= 3.2.17)
 +    activerecord (3.2.22.5)
 +      activemodel (= 3.2.22.5)
 +      activesupport (= 3.2.22.5)
        arel (~> 3.0.2)
        tzinfo (~> 0.3.29)
 -    activeresource (3.2.17)
 -      activemodel (= 3.2.17)
 -      activesupport (= 3.2.17)
 -    activesupport (3.2.17)
 +    activeresource (3.2.22.5)
 +      activemodel (= 3.2.22.5)
 +      activesupport (= 3.2.22.5)
 +    activesupport (3.2.22.5)
        i18n (~> 0.6, >= 0.6.4)
        multi_json (~> 1.0)
 -    acts_as_api (0.4.2)
 +    acts_as_api (0.4.3)
        activemodel (>= 3.0.0)
        activesupport (>= 3.0.0)
        rack (>= 1.1.0)
      addressable (2.4.0)
      andand (1.3.3)
      arel (3.0.3)
 -    arvados (0.1.20160420143004)
 +    arvados (0.1.20160513152536)
        activesupport (>= 3, < 4.2.6)
        andand (~> 1.3, >= 1.3.3)
 -      google-api-client (>= 0.7, < 0.9)
 +      google-api-client (>= 0.7, < 0.8.9)
        i18n (~> 0)
        json (~> 1.7, >= 1.7.7)
        jwt (>= 0.1.5, < 2)
-     arvados-cli (0.1.20160913140509)
+     arvados-cli (0.1.20161017193526)
        activesupport (~> 3.2, >= 3.2.13)
        andand (~> 1.3, >= 1.3.3)
        arvados (~> 0.1, >= 0.1.20150128223554)
        extlib (>= 0.9.15)
        multi_json (>= 1.0.0)
      builder (3.0.4)
 -    capistrano (2.15.5)
 +    capistrano (2.15.9)
        highline
        net-scp (>= 1.0.0)
        net-sftp (>= 2.0.0)
        net-ssh (>= 2.0.14)
        net-ssh-gateway (>= 1.1.0)
 -    coffee-rails (3.2.1)
 +    coffee-rails (3.2.2)
        coffee-script (>= 2.2.0)
 -      railties (~> 3.2.0.beta)
 -    coffee-script (2.2.0)
 +      railties (~> 3.2.0)
 +    coffee-script (2.4.1)
        coffee-script-source
        execjs
 -    coffee-script-source (1.7.0)
 +    coffee-script-source (1.10.0)
      curb (0.9.3)
 -    daemon_controller (1.2.0)
 -    database_cleaner (1.2.0)
 +    database_cleaner (1.5.3)
      erubis (2.7.0)
 -    eventmachine (1.0.3)
 -    execjs (2.0.2)
 +    eventmachine (1.2.0.1)
 +    execjs (2.7.0)
      extlib (0.9.16)
 -    factory_girl (4.4.0)
 +    factory_girl (4.7.0)
        activesupport (>= 3.0.0)
 -    factory_girl_rails (4.4.1)
 -      factory_girl (~> 4.4.0)
 +    factory_girl_rails (4.7.0)
 +      factory_girl (~> 4.7.0)
        railties (>= 3.0.0)
      faraday (0.9.2)
        multipart-post (>= 1.2, < 3)
 -    faye-websocket (0.7.2)
 +    faye-websocket (0.10.4)
        eventmachine (>= 0.12.0)
 -      websocket-driver (>= 0.3.1)
 -    google-api-client (0.7.1)
 -      addressable (>= 2.3.2)
 -      autoparse (>= 0.3.3)
 -      extlib (>= 0.9.15)
 -      faraday (>= 0.9.0)
 -      jwt (>= 0.1.5)
 -      launchy (>= 2.1.1)
 -      multi_json (>= 1.0.0)
 -      retriable (>= 1.4)
 -      signet (>= 0.5.0)
 -      uuidtools (>= 2.1.0)
 -    hashie (1.2.0)
 -    highline (1.6.21)
 +      websocket-driver (>= 0.5.1)
 +    google-api-client (0.8.7)
 +      activesupport (>= 3.2, < 5.0)
 +      addressable (~> 2.3)
 +      autoparse (~> 0.3)
 +      extlib (~> 0.9)
 +      faraday (~> 0.9)
 +      googleauth (~> 0.3)
 +      launchy (~> 2.4)
 +      multi_json (~> 1.10)
 +      retriable (~> 1.4)
 +      signet (~> 0.6)
 +    googleauth (0.5.1)
 +      faraday (~> 0.9)
 +      jwt (~> 1.4)
 +      logging (~> 2.0)
 +      memoist (~> 0.12)
 +      multi_json (~> 1.11)
 +      os (~> 0.9)
 +      signet (~> 0.7)
 +    hashie (3.4.6)
 +    highline (1.7.8)
      hike (1.2.3)
 -    httpauth (0.2.1)
      i18n (0.7.0)
      journey (1.0.4)
 -    jquery-rails (3.1.0)
 +    jquery-rails (3.1.4)
        railties (>= 3.0, < 5.0)
        thor (>= 0.14, < 2.0)
      json (1.8.3)
 -    jwt (0.1.13)
 -      multi_json (>= 1.5)
 +    jwt (1.5.6)
      launchy (2.4.3)
        addressable (~> 2.3)
 -    libv8 (3.16.14.3)
 +    libv8 (3.16.14.15)
 +    little-plugger (1.1.4)
 +    logging (2.1.0)
 +      little-plugger (~> 1.1)
 +      multi_json (~> 1.10)
      lograge (0.3.6)
        actionpack (>= 3)
        activesupport (>= 3)
      mail (2.5.4)
        mime-types (~> 1.16)
        treetop (~> 1.4.8)
 +    memoist (0.15.0)
      metaclass (0.0.4)
      mime-types (1.25.1)
 -    mocha (1.1.0)
 +    minitest (4.7.5)
 +    minitest-rails (1.0.1)
 +      minitest (~> 4.7)
 +      minitest-test (~> 1.0)
 +      railties (>= 3.0, < 4.1)
 +    minitest-test (1.1.0)
 +      minitest (~> 4.0)
 +    mocha (1.2.0)
        metaclass (~> 0.0.1)
 -    multi_json (1.12.0)
 +    multi_json (1.12.1)
 +    multi_xml (0.5.5)
      multipart-post (2.0.0)
 -    net-scp (1.2.0)
 +    net-scp (1.2.1)
        net-ssh (>= 2.6.5)
      net-sftp (2.1.2)
        net-ssh (>= 2.6.5)
 -    net-ssh (2.8.0)
 +    net-ssh (3.2.0)
      net-ssh-gateway (1.2.0)
        net-ssh (>= 2.6.5)
 -    oauth2 (0.8.1)
 -      faraday (~> 0.8)
 -      httpauth (~> 0.1)
 -      jwt (~> 0.1.4)
 -      multi_json (~> 1.0)
 -      rack (~> 1.2)
 +    oauth2 (1.2.0)
 +      faraday (>= 0.8, < 0.10)
 +      jwt (~> 1.0)
 +      multi_json (~> 1.3)
 +      multi_xml (~> 0.5)
 +      rack (>= 1.2, < 3)
      oj (2.15.0)
 -    omniauth (1.1.1)
 -      hashie (~> 1.2)
 -      rack
 -    omniauth-oauth2 (1.1.1)
 -      oauth2 (~> 0.8.0)
 -      omniauth (~> 1.0)
 -    passenger (4.0.41)
 -      daemon_controller (>= 1.2.0)
 +    omniauth (1.3.1)
 +      hashie (>= 1.2, < 4)
 +      rack (>= 1.0, < 3)
 +    omniauth-oauth2 (1.4.0)
 +      oauth2 (~> 1.0)
 +      omniauth (~> 1.2)
 +    os (0.9.6)
 +    passenger (5.0.30)
        rack
        rake (>= 0.8.1)
 -    pg (0.17.1)
 +    pg (0.19.0)
      pg_power (1.6.4)
        pg
        rails (~> 3.1)
 -    polyglot (0.3.4)
 -    puma (2.8.2)
 -      rack (>= 1.1, < 2.0)
 -    rack (1.4.5)
 -    rack-cache (1.2)
 +    polyglot (0.3.5)
 +    power_assert (0.3.1)
 +    puma (2.16.0)
 +    rack (1.4.7)
 +    rack-cache (1.6.1)
        rack (>= 0.4)
      rack-ssl (1.3.4)
        rack
 -    rack-test (0.6.2)
 +    rack-test (0.6.3)
        rack (>= 1.0)
 -    rails (3.2.17)
 -      actionmailer (= 3.2.17)
 -      actionpack (= 3.2.17)
 -      activerecord (= 3.2.17)
 -      activeresource (= 3.2.17)
 -      activesupport (= 3.2.17)
 +    rails (3.2.22.5)
 +      actionmailer (= 3.2.22.5)
 +      actionpack (= 3.2.22.5)
 +      activerecord (= 3.2.22.5)
 +      activeresource (= 3.2.22.5)
 +      activesupport (= 3.2.22.5)
        bundler (~> 1.0)
 -      railties (= 3.2.17)
 -    railties (3.2.17)
 -      actionpack (= 3.2.17)
 -      activesupport (= 3.2.17)
 +      railties (= 3.2.22.5)
 +    railties (3.2.22.5)
 +      actionpack (= 3.2.22.5)
 +      activesupport (= 3.2.22.5)
        rack-ssl (~> 1.3.2)
        rake (>= 0.8.7)
        rdoc (~> 3.4)
        thor (>= 0.14.6, < 2.0)
 -    rake (10.2.2)
 +    rake (11.3.0)
      rdoc (3.12.2)
        json (~> 1.4)
 -    ref (1.0.5)
 -    retriable (2.1.0)
 -    ruby-prof (0.15.2)
 -    rvm-capistrano (1.5.1)
 +    ref (2.0.0)
 +    retriable (1.4.1)
 +    ruby-prof (0.16.2)
 +    rvm-capistrano (1.5.6)
        capistrano (~> 2.15.4)
      safe_yaml (1.0.4)
 -    sass (3.3.4)
 +    sass (3.4.22)
      sass-rails (3.2.6)
        railties (~> 3.2.0)
        sass (>= 3.1.10)
        tilt (~> 1.3)
 -    signet (0.5.1)
 -      addressable (>= 2.2.3)
 -      faraday (>= 0.9.0.rc5)
 -      jwt (>= 0.1.5)
 -      multi_json (>= 1.0.0)
 +    signet (0.7.3)
 +      addressable (~> 2.3)
 +      faraday (~> 0.9)
 +      jwt (~> 1.5)
 +      multi_json (~> 1.10)
      simplecov (0.7.1)
        multi_json (~> 1.0)
        simplecov-html (~> 0.7.1)
      simplecov-html (0.7.1)
      simplecov-rcov (0.2.3)
        simplecov (>= 0.4.1)
 -    sprockets (2.2.2)
 +    sprockets (2.2.3)
        hike (~> 1.2)
        multi_json (~> 1.0)
        rack (~> 1.0)
        tilt (~> 1.1, != 1.3.0)
 -    sshkey (1.6.1)
 -    test_after_commit (0.2.3)
 +    sshkey (1.8.0)
 +    test-unit (3.2.1)
 +      power_assert
 +    test_after_commit (1.1.0)
 +      activerecord (>= 3.2)
      themes_for_rails (0.5.1)
        rails (>= 3.0.0)
 -    therubyracer (0.12.1)
 +    therubyracer (0.12.2)
        libv8 (~> 3.16.14.0)
        ref
      thor (0.19.1)
        polyglot
        polyglot (>= 0.3.1)
      trollop (2.1.2)
 -    tzinfo (0.3.39)
 -    uglifier (2.5.0)
 +    tzinfo (0.3.51)
 +    uglifier (2.7.2)
        execjs (>= 0.3.0)
        json (>= 1.8.0)
 -    uuidtools (2.1.5)
 -    websocket-driver (0.3.2)
 +    websocket-driver (0.6.4)
 +      websocket-extensions (>= 0.1.0)
 +    websocket-extensions (0.1.2)
  
  PLATFORMS
    ruby
@@@ -258,39 -236,33 +258,39 @@@ DEPENDENCIE
    acts_as_api
    andand
    arvados (>= 0.1.20150615153458)
-   arvados-cli (>= 0.1.20151207150126)
-   coffee-rails (~> 3.2)
+   arvados-cli (>= 0.1.20161017193526)
+   coffee-rails (~> 3.2.0)
    database_cleaner
    factory_girl_rails
    faye-websocket
    jquery-rails
    lograge
    logstash-event
 +  minitest (~> 4.0)
 +  minitest-rails
    mocha
    multi_json
    oj
 -  omniauth (= 1.1.1)
 -  omniauth-oauth2 (= 1.1.1)
 +  omniauth (~> 1.1)
 +  omniauth-oauth2 (~> 1.1)
    passenger
    pg
    pg_power
 -  puma
 -  rails (~> 3.2.0)
 +  puma (~> 2.0)
 +  rails (~> 3.2)
    ruby-prof
    rvm-capistrano
    safe_yaml
 -  sass-rails (>= 3.2.0)
 +  sass-rails (~> 3.2)
    simplecov (~> 0.7.1)
    simplecov-rcov
    sshkey
 +  test-unit (~> 3.0)
    test_after_commit
    themes_for_rails
    therubyracer
    trollop
 -  uglifier (>= 1.0.3)
 +  uglifier (~> 2.0)
 +
 +BUNDLED WITH
 +   1.13.2
index 6229b4971888168a1308feb18a00529d50795bd7,776f7e190e06ad0a486dad78c04affe84493175a..4e3b5b8fd860926969db22fb2b946119e65a8f89
@@@ -25,6 -25,7 +25,7 @@@ class ApplicationController < ActionCon
  
    ERROR_ACTIONS = [:render_error, :render_not_found]
  
+   before_filter :disable_api_methods
    before_filter :set_cors_headers
    before_filter :respond_with_json_by_default
    before_filter :remote_ip
                  :with => :render_not_found)
    end
  
 +  def initialize *args
 +    super
 +    @object = nil
 +    @objects = nil
 +    @offset = nil
 +    @limit = nil
 +    @select = nil
 +    @distinct = nil
 +    @response_resource_name = nil
 +    @attrs = nil
 +  end
 +
    def default_url_options
      if Rails.configuration.host
        {:host => Rails.configuration.host}
      end
    end
  
+   def disable_api_methods
+     if Rails.configuration.disable_api_methods.
+         include?(controller_name + "." + action_name)
+       send_error("Disabled", status: 404)
+     end
+   end
    def set_cors_headers
      response.headers['Access-Control-Allow-Origin'] = '*'
      response.headers['Access-Control-Allow-Methods'] = 'GET, HEAD, PUT, POST, DELETE'
index fe6833c431b7f7613515cdc1c0b254c1358872ef,18d5647cc929e760a72ed48ed709a9d18b8da8a3..aed0309591e4ecbfa4c309747daee695417b16c8
@@@ -239,7 -239,7 +239,7 @@@ class ArvadosModel < ActiveRecord::Bas
    end
  
    def logged_attributes
 -    attributes.except *Rails.configuration.unlogged_attributes
 +    attributes.except(*Rails.configuration.unlogged_attributes)
    end
  
    def self.full_text_searchable_columns
    end
  
    def foreign_key_attributes
 -    attributes.keys.select { |a| a.match /_uuid$/ }
 +    attributes.keys.select { |a| a.match(/_uuid$/) }
    end
  
    def skip_uuid_read_permission_check
      foreign_key_attributes.each do |attr|
        attr_value = send attr
        if attr_value.is_a? String and
 -          attr_value.match /^[0-9a-f]{32,}(\+[@\w]+)*$/
 +          attr_value.match(/^[0-9a-f]{32,}(\+[@\w]+)*$/)
          begin
            send "#{attr}=", Collection.normalize_uuid(attr_value)
          rescue
      unless uuid.is_a? String
        return nil
      end
 -    resource_class = nil
  
      uuid.match HasUuid::UUID_REGEX do |re|
        return uuid_prefixes[re[1]] if uuid_prefixes[re[1]]
      end
  
 -    if uuid.match /.+@.+/
 +    if uuid.match(/.+@.+/)
        return Email
      end
  
    end
  
    def log_destroy
-     log_change('destroy') do |log|
+     log_change('delete') do |log|
        log.fill_properties('old', etag(@old_attributes), @old_logged_attributes)
        log.update_to nil
      end
index 1c3a29a7c30756e162e201d9237e976a5ea0c133,8579509de70e9eff1c46d25563ca239fcf9dff8d..901084c7636a61496944b4c18616e2d79c3508e5
@@@ -32,11 -32,6 +32,11 @@@ class Collection < ArvadosMode
      t.add :expires_at
    end
  
 +  after_initialize do
 +    @signatures_checked = false
 +    @computed_pdh_for_manifest_text = false
 +  end
 +
    def self.attributes_required_columns
      super.merge(
                  # If we don't list manifest_text explicitly, the
@@@ -66,9 -61,7 +66,9 @@@
      # subsequent passes without checking any signatures. This is
      # important because the signatures have probably been stripped off
      # by the time we get to a second validation pass!
 -    return true if @signatures_checked and @signatures_checked == computed_pdh
 +    if @signatures_checked && @signatures_checked == computed_pdh
 +      return true
 +    end
  
      if self.manifest_text_changed?
        # Check permissions on the collection manifest.
          utf8 = manifest_text
          utf8.force_encoding Encoding::UTF_8
          if utf8.valid_encoding? and utf8 == manifest_text.encode(Encoding::UTF_8)
 -          manifest_text = utf8
 +          self.manifest_text = utf8
            return true
          end
        rescue
      hash_part = nil
      size_part = nil
      uuid.split('+').each do |token|
 -      if token.match /^[0-9a-f]{32,}$/
 +      if token.match(/^[0-9a-f]{32,}$/)
          raise "uuid #{uuid} has multiple hash parts" if hash_part
          hash_part = token
 -      elsif token.match /^\d+$/
 +      elsif token.match(/^\d+$/)
          raise "uuid #{uuid} has multiple size parts" if size_part
          size_part = token
        end
          # looks like a saved Docker image.
          manifest = Keep::Manifest.new(coll_match.manifest_text)
          if manifest.exact_file_count?(1) and
-             (manifest.files[0][1] =~ /^[0-9A-Fa-f]{64}\.tar$/)
+             (manifest.files[0][1] =~ /^(sha256:)?[0-9A-Fa-f]{64}\.tar$/)
            return [coll_match]
          end
        end
index df80ea564d7a7d3a660a58ff84634c735c940d8f,7eab402609b482a238f8a40313bf622ece86c3c0..3207d1f288f2f264c671d6709063d93140ce3fec
@@@ -4,6 -4,7 +4,6 @@@ class Log < ArvadosMode
    include CommonApiTemplate
    serialize :properties, Hash
    before_validation :set_default_event_at
 -  attr_accessor :object, :object_kind
    after_save :send_notify
  
    api_accessible :user, extend: :common do |t|
@@@ -46,7 -47,7 +46,7 @@@
        self.event_at = thing.created_at
      when "update"
        self.event_at = thing.modified_at
-     when "destroy"
+     when "delete"
        self.event_at = db_current_time
      end
      self
index c16e59a73c1a273dffeac557f6f78d4fbbea7caf,e470e4c2bd9c47a45b395a4c90f4814edf89a417..18550204669c7cc6353d87cfc863bcbf3c4d876a
@@@ -13,6 -13,8 +13,8 @@@ class Node < ArvadosMode
    belongs_to(:job, foreign_key: :job_uuid, primary_key: :uuid)
    attr_accessor :job_readable
  
+   UNUSED_NODE_IP = '127.40.4.0'
    api_accessible :user, :extend => :common do |t|
      t.add :hostname
      t.add :domain
      t.add lambda { |x| Rails.configuration.compute_node_nameservers }, :as => :nameservers
    end
  
 +  after_initialize do
 +    @bypass_arvados_authorization = false
 +  end
 +
    def domain
      super || Rails.configuration.compute_node_domain
    end
    end
  
    def dns_server_update
-     if self.hostname_changed? or self.ip_address_changed?
-       if not self.ip_address.nil?
-         stale_conflicting_nodes = Node.where('id != ? and ip_address = ? and last_ping_at < ?',self.id,self.ip_address,10.minutes.ago)
-         if not stale_conflicting_nodes.empty?
-           # One or more stale compute node records have the same IP address as the new node.
-           # Clear the ip_address field on the stale nodes.
-           stale_conflicting_nodes.each do |stale_node|
-             stale_node.ip_address = nil
-             stale_node.save!
-           end
+     if hostname_changed? && hostname_was
+       self.class.dns_server_update(hostname_was, UNUSED_NODE_IP)
+     end
+     if hostname_changed? or ip_address_changed?
+       if ip_address
+         Node.where('id != ? and ip_address = ? and last_ping_at < ?',
+                    id, ip_address, 10.minutes.ago).each do |stale_node|
+           # One or more stale compute node records have the same IP
+           # address as the new node.  Clear the ip_address field on
+           # the stale nodes.
+           stale_node.ip_address = nil
+           stale_node.save!
          end
        end
-       if self.hostname and self.ip_address
-         self.class.dns_server_update(self.hostname, self.ip_address)
+       if hostname
+         self.class.dns_server_update(hostname, ip_address || UNUSED_NODE_IP)
        end
      end
    end
      (0..Rails.configuration.max_compute_nodes-1).each do |slot_number|
        hostname = hostname_for_slot(slot_number)
        hostfile = File.join Rails.configuration.dns_server_conf_dir, "#{hostname}.conf"
 -      if !File.exists? hostfile
 +      if !File.exist? hostfile
          n = Node.where(:slot_number => slot_number).first
          if n.nil? or n.ip_address.nil?
-           dns_server_update(hostname, '127.40.4.0')
+           dns_server_update(hostname, UNUSED_NODE_IP)
          else
            dns_server_update(hostname, n.ip_address)
          end
index 28b9a26f7f359e051011a1c78062728a62c28afa,9363cc4f02aa04d08552b9e343bbda9f8dcda5c1..964de38d0bfbf56a195a2d622d71c763db508753
@@@ -64,7 -64,7 +64,7 @@@ class User < ArvadosMode
    def is_invited
      !!(self.is_active ||
         Rails.configuration.new_users_are_active ||
 -       self.groups_i_can(:read).select { |x| x.match /-f+$/ }.first)
 +       self.groups_i_can(:read).select { |x| x.match(/-f+$/) }.first)
    end
  
    def groups_i_can(verb)
  
      # delete "All users" group read permissions for this user
      group = Group.where(name: 'All users').select do |g|
 -      g[:uuid].match /-f+$/
 +      g[:uuid].match(/-f+$/)
      end.first
      Link.destroy_all(tail_uuid: self.uuid,
                       head_uuid: group[:uuid],
      self.save!
    end
  
+   def set_initial_username(requested: false)
+     if !requested.is_a?(String) || requested.empty?
+       email_parts = email.partition("@")
+       local_parts = email_parts.first.partition("+")
+       if email_parts.any?(&:empty?)
+         return
+       elsif not local_parts.first.empty?
+         requested = local_parts.first
+       else
+         requested = email_parts.first
+       end
+     end
+     requested.sub!(/^[^A-Za-z]+/, "")
+     requested.gsub!(/[^A-Za-z0-9]/, "")
+     unless requested.empty?
+       self.username = find_usable_username_from(requested)
+     end
+   end
    protected
  
    def ensure_ownership_path_leads_to_user
      nil
    end
  
-   def set_initial_username
-     email_parts = email.partition("@")
-     local_parts = email_parts.first.partition("+")
-     if email_parts.any?(&:empty?)
-       return
-     elsif not local_parts.first.empty?
-       base_username = local_parts.first
-     else
-       base_username = email_parts.first
-     end
-     base_username.sub!(/^[^A-Za-z]+/, "")
-     base_username.gsub!(/[^A-Za-z0-9]/, "")
-     unless base_username.empty?
-       self.username = find_usable_username_from(base_username)
-     end
-   end
    def prevent_privilege_escalation
      if current_user.andand.is_admin
        return true
index 634fce79706254069196160410d132e2ed277ae1,c85cc1979f99482ff36ba6dc38ba5790ec7bf591..b96e22ed6583befa82153c623da9bac73014745e
@@@ -46,6 -46,49 +46,49 @@@ class Arvados::V1::CollectionsControlle
      end
    end
  
+   test 'index without select returns everything except manifest' do
+     authorize_with :active
+     get :index
+     assert_response :success
+     assert json_response['items'].any?
+     json_response['items'].each do |coll|
+       assert_includes(coll.keys, 'uuid')
+       assert_includes(coll.keys, 'name')
+       assert_includes(coll.keys, 'created_at')
+       refute_includes(coll.keys, 'manifest_text')
+     end
+   end
+   ['', nil, false, 'null'].each do |select|
+     test "index with select=#{select.inspect} returns everything except manifest" do
+       authorize_with :active
+       get :index, select: select
+       assert_response :success
+       assert json_response['items'].any?
+       json_response['items'].each do |coll|
+         assert_includes(coll.keys, 'uuid')
+         assert_includes(coll.keys, 'name')
+         assert_includes(coll.keys, 'created_at')
+         refute_includes(coll.keys, 'manifest_text')
+       end
+     end
+   end
+   [["uuid"],
+    ["uuid", "manifest_text"],
+    '["uuid"]',
+    '["uuid", "manifest_text"]'].each do |select|
+     test "index with select=#{select.inspect} returns no name" do
+       authorize_with :active
+       get :index, select: select
+       assert_response :success
+       assert json_response['items'].any?
+       json_response['items'].each do |coll|
+         refute_includes(coll.keys, 'name')
+       end
+     end
+   end
    [0,1,2].each do |limit|
      test "get index with limit=#{limit}" do
        authorize_with :active
@@@ -751,11 -794,11 +794,11 @@@ EO
      [2**8, :success],
      [2**18, 422],
    ].each do |description_size, expected_response|
 -    test "create collection with description size #{description_size}
 +    # Descriptions are not part of search indexes. Skip until
 +    # full-text search is implemented, at which point replace with a
 +    # search in description.
 +    skip "create collection with description size #{description_size}
            and expect response #{expected_response}" do
 -      skip "(Descriptions are not part of search indexes. Skip until full-text search
 -            is implemented, at which point replace with a search in description.)"
 -
        authorize_with :active
  
        description = 'here is a collection with a very large description'
index 9a0aa504439d13700ce7e3a9d654681221f12155,3c11b3e00940fefb644b2ea4b3e64f81ffdfed2b..8808a82c45c92d398df02e497438f137a7bf1e1a
@@@ -97,7 -97,7 +97,7 @@@ class Arvados::V1::JobsControllerTest 
                   'server should correct bogus cancelled_at ' +
                   job['cancelled_at'])
      assert_equal(true,
 -                 File.exists?(Rails.configuration.crunch_refresh_trigger),
 +                 File.exist?(Rails.configuration.crunch_refresh_trigger),
                   'trigger file should be created when job is cancelled')
    end
  
      assert_not_nil json_response["components"]
      assert_equal [], json_response["components"].keys
    end
+   test 'jobs.create disabled in config' do
+     Rails.configuration.disable_api_methods = ["jobs.create",
+                                                "pipeline_instances.create"]
+     authorize_with :active
+     post :create, job: {
+       script: "hash",
+       script_version: "master",
+       repository: "active/foo",
+       script_parameters: {}
+     }
+     assert_response 404
+   end
  end