4253: Users can manage their own repositories.
authorBrett Smith <brett@curoverse.com>
Tue, 31 Mar 2015 13:23:56 +0000 (09:23 -0400)
committerBrett Smith <brett@curoverse.com>
Tue, 31 Mar 2015 13:23:59 +0000 (09:23 -0400)
This commit allows users to create their own repositories, as long as
the repository name starts with their own username.

To support this change, we've modified our Gitolite setup to store
repositories primarily by UUID, with a name alias for easier
checkout.  fetch_url and push_url become generated attributes
accordingly.  This makes it easier to rename the repository later and
allow checkouts to continue to work.

33 files changed:
apps/workbench/app/views/users/_manage_repositories.html.erb
apps/workbench/test/integration/users_test.rb
doc/_includes/_tutorial_submit_job.liquid
doc/api/methods/jobs.html.textile.liquid
doc/api/schema/PipelineTemplate.html.textile.liquid
doc/api/schema/Repository.html.textile.liquid
doc/user/topics/tutorial-parallel.html.textile.liquid
doc/user/tutorials/tutorial-submit-job.html.textile.liquid
services/api/app/controllers/arvados/v1/users_controller.rb
services/api/app/models/commit.rb
services/api/app/models/repository.rb
services/api/app/models/user.rb
services/api/db/migrate/20150324152204_backward_compatibility_for_user_repositories.rb [new file with mode: 0644]
services/api/db/structure.sql
services/api/script/crunch-dispatch.rb
services/api/test/fixtures/api_client_authorizations.yml
services/api/test/fixtures/jobs.yml
services/api/test/fixtures/repositories.yml
services/api/test/fixtures/users.yml
services/api/test/functional/arvados/v1/commits_controller_test.rb
services/api/test/functional/arvados/v1/job_reuse_controller_test.rb
services/api/test/functional/arvados/v1/jobs_controller_test.rb
services/api/test/functional/arvados/v1/repositories_controller_test.rb
services/api/test/functional/arvados/v1/users_controller_test.rb
services/api/test/integration/crunch_dispatch_test.rb
services/api/test/integration/serialized_encoding_test.rb
services/api/test/integration/users_test.rb
services/api/test/test.git.tar
services/api/test/test_helper.rb
services/api/test/unit/job_test.rb
services/api/test/unit/permission_test.rb
services/api/test/unit/repository_test.rb
services/api/test/unit/user_test.rb

index 83ec30a8146f9311a03f817568ccd46f14d05075..e7c6d2422cb6f459e32905be19330dfc547d516c 100644 (file)
@@ -32,7 +32,7 @@
               <%= writable ? 'writable' : 'read-only' %>
             </td>
             <td style="word-break:break-all;">
-              <code><%= writable ? repo[:push_url] : repo[:fetch_url] %></code>
+              <code><%= writable ? repo.push_url : repo.fetch_url %></code>
             </td>
             <td>
               <% if writable == 'can_manage' %>
index 80e6a71932efb5098aac750499c3654fc44c6cf8..a329f5188eada16eac38e79d9288ccb61d7f8c94 100644 (file)
@@ -56,7 +56,7 @@ class UsersTest < ActionDispatch::IntegrationTest
     within '.modal-content' do
       find 'label', text: 'Virtual Machine'
       fill_in "email", :with => "foo@example.com"
-      fill_in "repo_name", :with => "test_repo"
+      fill_in "repo_name", :with => "newtestrepo"
       click_button "Submit"
       wait_for_ajax
     end
@@ -81,7 +81,7 @@ class UsersTest < ActionDispatch::IntegrationTest
 
     click_link 'Advanced'
     click_link 'Metadata'
-    assert page.has_text? 'Repository: test_repo'
+    assert page.has_text? 'Repository: foo/newtestrepo'
     assert !(page.has_text? 'VirtualMachine:')
   end
 
@@ -106,7 +106,7 @@ class UsersTest < ActionDispatch::IntegrationTest
 
     within '.modal-content' do
       find 'label', text: 'Virtual Machine'
-      fill_in "repo_name", :with => "test_repo"
+      fill_in "repo_name", :with => "activetestrepo"
       click_button "Submit"
     end
 
@@ -115,7 +115,7 @@ class UsersTest < ActionDispatch::IntegrationTest
 
     click_link 'Advanced'
     click_link 'Metadata'
-    assert page.has_text? 'Repository: test_repo'
+    assert page.has_text? 'Repository: active/activetestrepo'
     assert !(page.has_text? 'VirtualMachine:')
 
     # Click on Setup button again and this time also choose a VM
@@ -123,7 +123,7 @@ class UsersTest < ActionDispatch::IntegrationTest
     click_link 'Setup Active User'
 
     within '.modal-content' do
-      fill_in "repo_name", :with => "second_test_repo"
+      fill_in "repo_name", :with => "activetestrepo2"
       select("testvm.shell", :from => 'vm_uuid')
       click_button "Submit"
     end
@@ -133,7 +133,7 @@ class UsersTest < ActionDispatch::IntegrationTest
 
     click_link 'Advanced'
     click_link 'Metadata'
-    assert page.has_text? 'Repository: second_test_repo'
+    assert page.has_text? 'Repository: active/activetestrepo2'
     assert page.has_text? 'VirtualMachine: testvm.shell'
   end
 
@@ -181,16 +181,15 @@ class UsersTest < ActionDispatch::IntegrationTest
 
     click_link 'Advanced'
     click_link 'Metadata'
-    assert !(page.has_text? 'Repository: test_repo')
-    assert !(page.has_text? 'Repository: second_test_repo')
-    assert !(page.has_text? 'VirtualMachine: testvm.shell')
+    assert page.has_no_text? 'Repository: active/'
+    assert page.has_no_text? 'VirtualMachine: testvm.shell'
 
     # setup user again and verify links present
     click_link 'Admin'
     click_link 'Setup Active User'
 
     within '.modal-content' do
-      fill_in "repo_name", :with => "second_test_repo"
+      fill_in "repo_name", :with => "activetestrepo"
       select("testvm.shell", :from => 'vm_uuid')
       click_button "Submit"
     end
@@ -200,7 +199,7 @@ class UsersTest < ActionDispatch::IntegrationTest
 
     click_link 'Advanced'
     click_link 'Metadata'
-    assert page.has_text? 'Repository: second_test_repo'
+    assert page.has_text? 'Repository: active/activetestrepo'
     assert page.has_text? 'VirtualMachine: testvm.shell'
   end
 
index 57063b3f3b22e98177510eec269151e3a17f8cdf..3ea7602d3f0a78235ad946d25e4caea9ad14cde7 100644 (file)
@@ -2,7 +2,7 @@
   "name":"My md5 pipeline",
   "components":{
     "do_hash":{
-      "repository":"$USER",
+      "repository":"$USER/$USER",
       "script":"hash.py",
       "script_version":"master",
       "runtime_constraints":{
index ac68129a5f49318a2e94084220703e34b430bf7d..b57858e0ddc279d339de161fc041428e823fb96e 100644 (file)
@@ -76,7 +76,7 @@ Run the script "crunch_scripts/hash.py" in the repository "you" using the "maste
 {
   "job": {
     "script": "hash.py",
-    "repository": "<b>you</b>",
+    "repository": "<b>you</b>/<b>you</b>",
     "script_version": "master",
     "script_parameters": {
       "input": "c1bad4b39ca5a924e481008009d94e32+210"
@@ -92,7 +92,7 @@ Run using exactly the version "d00220fb38d4b85ca8fc28a8151702a2b9d1dec5". Arvado
 {
   "job": {
     "script": "hash.py",
-    "repository": "<b>you</b>",
+    "repository": "<b>you</b>/<b>you</b>",
     "script_version": "d00220fb38d4b85ca8fc28a8151702a2b9d1dec5",
     "script_parameters": {
       "input": "c1bad4b39ca5a924e481008009d94e32+210"
@@ -108,7 +108,7 @@ Arvados should re-use a previous job if the "script_version" of the previous job
 {
   "job": {
     "script": "hash.py",
-    "repository": "<b>you</b>",
+    "repository": "<b>you</b>/<b>you</b>",
     "script_version": "master",
     "script_parameters": {
       "input": "c1bad4b39ca5a924e481008009d94e32+210"
@@ -126,27 +126,27 @@ The same behavior, using filters:
 {
   "job": {
     "script": "hash.py",
-    "repository": "<b>you</b>",
+    "repository": "<b>you</b>/<b>you</b>",
     "script_version": "master",
     "script_parameters": {
       "input": "c1bad4b39ca5a924e481008009d94e32+210"
     }
   },
   "filters": [["script", "=", "hash.py"],
-              ["repository", "=", "<b>you</b>"],
+              ["repository", "=", "<b>you</b>/<b>you</b>"],
               ["script_version", "in git", "earlier_version_tag"],
               ["script_version", "not in git", "blacklisted_version_tag"]],
   "find_or_create": true
 }
 </pre></notextile>
 
-Run the script "crunch_scripts/monte-carlo.py" in the repository "you" using the current "master" commit. Because it is marked as "nondeterministic", this job will not be considered as a suitable candidate for future job submissions that use the "find_or_create" feature.
+Run the script "crunch_scripts/monte-carlo.py" in the repository "you/you" using the current "master" commit. Because it is marked as "nondeterministic", this job will not be considered as a suitable candidate for future job submissions that use the "find_or_create" feature.
 
 <notextile><pre>
 {
   "job": {
     "script": "monte-carlo.py",
-    "repository": "<b>you</b>",
+    "repository": "<b>you</b>/<b>you</b>",
     "script_version": "master",
     "nondeterministic": true,
     "script_parameters": {
index 2b215c2ba37ba52e623dd8003fbc233e7aa53752..444960a6ea46e9f38a389e06814b743cf6a4680d 100644 (file)
@@ -51,7 +51,7 @@ This is a pipeline named "Filter MD5 hash values" with two components, "do_hash"
   "components": {
     "do_hash": {
       "script": "hash.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": {
         "input": {
@@ -64,7 +64,7 @@ This is a pipeline named "Filter MD5 hash values" with two components, "do_hash"
     },
     "filter": {
       "script": "0-filter.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": {
         "input": {
@@ -84,13 +84,13 @@ This pipeline consists of three components.  The components "thing1" and "thing2
   "components": {
     "cat_in_the_hat": {
       "script": "cat.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": { }
     },
     "thing1": {
       "script": "thing1.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": {
         "input": {
@@ -100,7 +100,7 @@ This pipeline consists of three components.  The components "thing1" and "thing2
     },
     "thing2": {
       "script": "thing2.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": {
         "input": {
@@ -120,19 +120,19 @@ This pipeline consists of three components.  The component "cleanup" depends on
   "components": {
     "thing1": {
       "script": "thing1.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": { }
     },
     "thing2": {
       "script": "thing2.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": { }
     },
     "cleanup": {
       "script": "cleanup.py",
-      "repository": "<b>you</b>",
+      "repository": "<b>you</b>/<b>you</b>",
       "script_version": "master",
       "script_parameters": {
         "mess1": {
index 0308f7d65c8ae04a575e6f64529c5202dffd9af6..27cc711985f0a21f59b912bb9aa1cade80307b4b 100644 (file)
@@ -18,6 +18,6 @@ Each Repository has, in addition to the usual "attributes of Arvados resources":
 
 table(table table-bordered table-condensed).
 |_. Attribute|_. Type|_. Description|_. Example|
-|name|string|||
-|fetch_url|string|||
-|push_url|string|||
+|name|string|The name of the repository on disk.  Repository names must begin with a letter and contain only alphanumerics.  Unless the repository is owned by the system user, the name must begin with the owner's username, then be separated from the base repository name with @/@.  You may not create a repository that is owned by a user without a username.|@username/project1@|
+|fetch_url|string|The git remote's fetch URL for the repository.  Read-only.||
+|push_url|string|The git remote's push URL for the repository.  Read-only.||
index 9be610358bb5f8133c6f7269c390799e04de8e5d..7a430a9471ab8a5b09f74630287ee5d415e52343 100644 (file)
@@ -40,7 +40,7 @@ You should now be able to run your new script using Crunch, with "script" referr
 <pre><code>~/$USER/crunch_scripts$ <span class="userinput">cat &gt;~/the_job &lt;&lt;EOF
 {
  "script": "concurrent-hash.py",
- "repository": "$USER",
+ "repository": "$USER/$USER",
  "script_version": "master",
  "script_parameters":
  {
index fc77e5cdc02162f4d42a997cbb644d7323e4adcc..7cc4b885490b0fe54db4ee59d73cf1cc043ba45b 100644 (file)
@@ -20,13 +20,13 @@ All Crunch scripts are managed through the Git revision control system.  Before
 ~$ <span class="userinput">git config --global user.email $USER@example.com</span></code></pre>
 </notextile>
 
-On the Arvados Workbench, navigate to "Code repositories":https://{{site.arvados_workbench_host}}/repositories.  You should see a repository with your user name listed in the *name* column.  Next to *name* is the column *push_url*.  Copy the *push_url* value associated with your repository.  This should look like <notextile><code>git@git.{{ site.arvados_api_host }}:$USER.git</code></notextile>.
+On the Arvados Workbench, navigate to "Code repositories":https://{{site.arvados_workbench_host}}/repositories.  You should see a repository with your user name listed in the *name* column.  Next to *name* is the column *push_url*.  Copy the *push_url* value associated with your repository.  This should look like <notextile><code>git@git.{{ site.arvados_api_host }}:$USER/$USER.git</code></notextile>.
 
 Next, on the Arvados virtual machine, clone your Git repository:
 
 <notextile>
 <pre><code>~$ <span class="userinput">cd $HOME</span> # (or wherever you want to install)
-~$ <span class="userinput">git clone git@git.{{ site.arvados_api_host }}:$USER.git</span>
+~$ <span class="userinput">git clone git@git.{{ site.arvados_api_host }}:$USER/$USER.git</span>
 Cloning into '$USER'...</code></pre>
 </notextile>
 
@@ -84,7 +84,7 @@ Counting objects: 4, done.
 Compressing objects: 100% (2/2), done.
 Writing objects: 100% (4/4), 682 bytes, done.
 Total 4 (delta 0), reused 0 (delta 0)
-To git@git.qr1hi.arvadosapi.com:$USER.git
+To git@git.qr1hi.arvadosapi.com:$USER/$USER.git
  * [new branch]      master -> master</code></pre>
 </notextile>
 
index 131ee5236bc08e26afb096912505594db0755f67..b5ac19586f17bba42621b9a349c359443ffbc9e6 100644 (file)
@@ -96,12 +96,28 @@ class Arvados::V1::UsersController < ApplicationController
       end
     end
 
+    # It's not always possible to know the user's username when submitting
+    # this request.  If it included a plain repository name, expand that to a
+    # user-owned name now.
+    if params[:repo_name].nil?
+      full_repo_name = nil
+    else
+      full_repo_name = "#{@object.username}/#{params[:repo_name]}"
+    end
     if object_found
-      @response = @object.setup_repo_vm_links params[:repo_name],
+      if params[:repo_name].andand.include?("/")
+        repo_name = params[:repo_name]
+      elsif @object.username.nil?
+        raise ArgumentError.
+          new("can't setup a user without a username with a repository")
+      else
+        repo_name = full_repo_name
+      end
+      @response = @object.setup_repo_vm_links repo_name,
                     params[:vm_uuid], params[:openid_prefix]
     else
       @response = User.setup @object, params[:openid_prefix],
-                    params[:repo_name], params[:vm_uuid]
+                    full_repo_name, params[:vm_uuid]
     end
 
     # setup succeeded. send email to user
index 0f62737cea15577879134bff6a128878c956612b..0d47b63c61ea000fd04b50d7bf61b442aa25cb24 100644 (file)
@@ -137,20 +137,16 @@ class Commit < ActiveRecord::Base
 
   protected
 
-  def self.repositories
-    return @repositories if @repositories
-
-    @repositories = {}
-    @gitdirbase = Rails.configuration.git_repositories_dir
-    Dir.foreach @gitdirbase do |repo|
-      next if repo.match /^\./
-      git_dir = File.join(@gitdirbase,
-                          repo.match(/\.git$/) ? repo : File.join(repo, '.git'))
-      next if git_dir == Rails.configuration.git_internal_dir
-      repo_name = repo.sub(/\.git$/, '')
-      @repositories[repo_name] = {git_dir: git_dir}
-    end
-
-    @repositories
-  end
+ def self.repositories
+   return @repositories if @repositories
+
+   @repositories = {}
+   Repository.find_each do |repo|
+     if git_dir = repo.server_path
+       @repositories[repo.name] = {git_dir: git_dir}
+     end
+   end
+
+   @repositories
+ end
 end
index 0189ee7236940934831713ff18b2b92ffb53bd87..5bc467c9204caeb445b55131941f5fccd723fb10 100644 (file)
@@ -3,26 +3,49 @@ class Repository < ArvadosModel
   include KindAndEtag
   include CommonApiTemplate
 
+  # Order is important here.  We must validate the owner before we can
+  # validate the name.
+  validate :valid_owner
+  validate :name_format, :if => Proc.new { |r| r.errors[:owner_uuid].empty? }
+  validates(:name, uniqueness: true, allow_nil: false)
+
   api_accessible :user, extend: :common do |t|
     t.add :name
     t.add :fetch_url
     t.add :push_url
   end
 
+  def self.attributes_required_columns
+    super.merge({"push_url" => ["name"], "fetch_url" => ["name"]})
+  end
+
   def push_url
-    super || self.name && "git@git.#{Rails.configuration.uuid_prefix}.arvadosapi.com:#{self.name}.git"
+    "git@git.%s.arvadosapi.com:%s.git" % [Rails.configuration.uuid_prefix, name]
   end
 
   def fetch_url
-    super || push_url
+    push_url
   end
 
-  protected
-
-  def permission_to_create
-    current_user and current_user.is_admin
+  def server_path
+    # Find where the repository is stored on the API server's filesystem,
+    # and return that path, or nil if not found.
+    # This method is only for the API server's internal use, and should not
+    # be exposed through the public API.  Following our current gitolite
+    # setup, it searches for repositories stored by UUID, then name; and it
+    # prefers bare repositories over checkouts.
+    [["%s.git"], ["%s", ".git"]].each do |repo_base, *join_args|
+      [:uuid, :name].each do |path_attr|
+        git_dir = File.join(Rails.configuration.git_repositories_dir,
+                            repo_base % send(path_attr), *join_args)
+        return git_dir if File.exist?(git_dir)
+      end
+    end
+    nil
   end
 
+  protected
+
   def permission_to_update
     return false if not current_user
     return true if current_user.is_admin
@@ -33,4 +56,30 @@ class Repository < ArvadosModel
     return super if changed_attributes.keys - ['modified_at', 'updated_at'] == []
     false
   end
+
+  def owner
+    User.find_by_uuid(owner_uuid)
+  end
+
+  def valid_owner
+    if owner.nil? or (owner.username.nil? and (owner.uuid != system_user_uuid))
+      errors.add(:owner_uuid, "must refer to a user with a username")
+      false
+    end
+  end
+
+  def name_format
+    if owner.uuid == system_user_uuid
+      prefix_match = ""
+      errmsg_start = "must be"
+    else
+      prefix_match = Regexp.escape(owner.username + "/")
+      errmsg_start = "must be the owner's username, then '/', then"
+    end
+    if not /^#{prefix_match}[A-Za-z][A-Za-z0-9]*$/.match(name)
+      errors.add(:name,
+                 "#{errmsg_start} a letter followed by alphanumerics")
+      false
+    end
+  end
 end
index c395d23bb1c799c4c748927f27b3a7af33d4b963..fe5e07b3b7c736d1c933005cd1dc1bcd6c66f243 100644 (file)
@@ -17,6 +17,9 @@ class User < ArvadosModel
             allow_nil: true)
   before_update :prevent_privilege_escalation
   before_update :prevent_inactive_admin
+  before_update :verify_repositories_empty, :if => Proc.new { |user|
+    user.username.nil? and user.username_changed?
+  }
   before_create :check_auto_admin
   before_create :set_initial_username, :if => Proc.new { |user|
     user.username.nil? and user.email
@@ -29,8 +32,14 @@ class User < ArvadosModel
   }
   after_create :send_admin_notifications
   after_update :send_profile_created_notification
+  after_update :sync_repository_names, :if => Proc.new { |user|
+    (user.uuid != system_user_uuid) and
+    user.username_changed? and
+    (not user.username_was.nil?)
+  }
 
   has_many :authorized_keys, :foreign_key => :authorized_user_uuid, :primary_key => :uuid
+  has_many :repositories, foreign_key: :owner_uuid, primary_key: :uuid
 
   api_accessible :user, extend: :common do |t|
     t.add :email
@@ -388,7 +397,7 @@ class User < ArvadosModel
       return
     end
 
-    repo = Repository.where(name: repo_name).first_or_create!
+    repo = Repository.where(owner_uuid: uuid, name: repo_name).first_or_create!
     logger.info { "repo uuid: " + repo[:uuid] }
     repo_perm = Link.where(tail_uuid: uuid, head_uuid: repo.uuid,
                            link_class: "permission",
@@ -467,9 +476,10 @@ class User < ArvadosModel
     if username
       create_vm_login_permission_link(Rails.configuration.auto_setup_new_users_with_vm_uuid,
                                       username)
+      repo_name = "#{username}/#{username}"
       if Rails.configuration.auto_setup_new_users_with_repository and
-          Repository.where(name: username).first.nil?
-        repo = Repository.create!(name: username)
+          Repository.where(name: repo_name).first.nil?
+        repo = Repository.create!(name: repo_name, owner_uuid: uuid)
         Link.create!(tail_uuid: uuid, head_uuid: repo.uuid,
                      link_class: "permission", name: "can_manage")
       end
@@ -486,4 +496,19 @@ class User < ArvadosModel
     end
   end
 
+  def verify_repositories_empty
+    unless repositories.first.nil?
+      errors.add(:username, "can't be unset when the user owns repositories")
+      false
+    end
+  end
+
+  def sync_repository_names
+    old_name_re = /^#{Regexp.escape(username_was)}\//
+    name_sub = "#{username}/"
+    repositories.find_each do |repo|
+      repo.name = repo.name.sub(old_name_re, name_sub)
+      repo.save!
+    end
+  end
 end
diff --git a/services/api/db/migrate/20150324152204_backward_compatibility_for_user_repositories.rb b/services/api/db/migrate/20150324152204_backward_compatibility_for_user_repositories.rb
new file mode 100644 (file)
index 0000000..12b888d
--- /dev/null
@@ -0,0 +1,89 @@
+require 'has_uuid'
+require 'kind_and_etag'
+
+class BackwardCompatibilityForUserRepositories < ActiveRecord::Migration
+  include CurrentApiClient
+
+  class ArvadosModel < ActiveRecord::Base
+    self.abstract_class = true
+    extend HasUuid::ClassMethods
+    include CurrentApiClient
+    include KindAndEtag
+    before_create do |record|
+      record.uuid ||= record.class.generate_uuid
+      record.owner_uuid ||= system_user_uuid
+    end
+    serialize :properties, Hash
+
+    def self.to_s
+      # Clean up the name of the stub model class so we generate correct UUIDs.
+      super.rpartition("::").last
+    end
+  end
+
+  class Log < ArvadosModel
+    def self.log_for(thing, age="old")
+      { "#{age}_etag" => thing.etag,
+        "#{age}_attributes" => thing.attributes,
+      }
+    end
+
+    def self.log_create(thing)
+      new_log("create", thing, log_for(thing, "new"))
+    end
+
+    def self.log_update(thing, start_state)
+      new_log("update", thing, start_state.merge(log_for(thing, "new")))
+    end
+
+    def self.log_destroy(thing)
+      new_log("destroy", thing, log_for(thing, "old"))
+    end
+
+    private
+
+    def self.new_log(event_type, thing, properties)
+      create!(event_type: event_type,
+              event_at: Time.now,
+              object_uuid: thing.uuid,
+              object_owner_uuid: thing.owner_uuid,
+              properties: properties)
+    end
+  end
+
+  class Link < ArvadosModel
+  end
+
+  class Repository < ArvadosModel
+  end
+
+  def up
+    remove_index :repositories, name: "repositories_search_index"
+    add_index(:repositories, %w(uuid owner_uuid modified_by_client_uuid
+                                modified_by_user_uuid name),
+              name: "repositories_search_index")
+    remove_column :repositories, :fetch_url
+    remove_column :repositories, :push_url
+
+    [Link, Log, Repository].each { |m| m.reset_column_information }
+    Repository.where("owner_uuid != ?", system_user_uuid).find_each do |repo|
+      link_attrs = {
+        tail_uuid: repo.owner_uuid,
+        link_class: "permission", name: "can_manage", head_uuid: repo.uuid,
+      }
+      if Link.where(link_attrs).first.nil?
+        manage_link = Link.create!(link_attrs)
+        Log.log_create(manage_link)
+      end
+      start_log = Log.log_for(repo)
+      repo.owner_uuid = system_user_uuid
+      repo.save!
+      Log.log_update(repo, start_log)
+    end
+  end
+
+  def down
+    raise ActiveRecord::IrreversibleMigration.
+      new("can't restore prior fetch and push URLs")
+  end
+end
index 40861f455716a9d1b6cfb917692f9d11b5d2d0f6..61fc1ae214701752926923ae826d49144eb5e6f6 100644 (file)
@@ -760,8 +760,6 @@ CREATE TABLE repositories (
     modified_by_user_uuid character varying(255),
     modified_at timestamp without time zone,
     name character varying(255),
-    fetch_url character varying(255),
-    push_url character varying(255),
     created_at timestamp without time zone NOT NULL,
     updated_at timestamp without time zone NOT NULL
 );
@@ -2102,7 +2100,7 @@ CREATE INDEX pipeline_templates_search_index ON pipeline_templates USING btree (
 -- Name: repositories_search_index; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX repositories_search_index ON repositories USING btree (uuid, owner_uuid, modified_by_client_uuid, modified_by_user_uuid, name, fetch_url, push_url);
+CREATE INDEX repositories_search_index ON repositories USING btree (uuid, owner_uuid, modified_by_client_uuid, modified_by_user_uuid, name);
 
 
 --
@@ -2374,4 +2372,6 @@ INSERT INTO schema_migrations (version) VALUES ('20150303210106');
 
 INSERT INTO schema_migrations (version) VALUES ('20150312151136');
 
-INSERT INTO schema_migrations (version) VALUES ('20150317132720');
\ No newline at end of file
+INSERT INTO schema_migrations (version) VALUES ('20150317132720');
+
+INSERT INTO schema_migrations (version) VALUES ('20150324152204');
\ No newline at end of file
index ab4f70e60bd1115a87a47eeed64a544859bf49fd..249582ec6a856e6fa8b98d2fce6243d7f2226189 100755 (executable)
@@ -66,6 +66,7 @@ class Dispatcher
     end
 
     @repo_root = Rails.configuration.git_repositories_dir
+    @arvados_repo_path = Repository.where(name: "arvados").first.server_path
     @authorizations = {}
     @did_recently = {}
     @fetched_commits = {}
@@ -276,19 +277,10 @@ class Dispatcher
     @authorizations[job.uuid]
   end
 
-  def get_commit(repo_name, commit_hash)
+  def get_commit(src_repo, commit_hash)
     # @fetched_commits[V]==true if we know commit V exists in the
     # arvados_internal git repository.
     if !@fetched_commits[commit_hash]
-      src_repo = File.join(@repo_root, "#{repo_name}.git")
-      if not File.exists? src_repo
-        src_repo = File.join(@repo_root, repo_name, '.git')
-        if not File.exists? src_repo
-          fail_job job, "No #{repo_name}.git or #{repo_name}/.git at #{@repo_root}"
-          return nil
-        end
-      end
-
       # check if the commit needs to be fetched or not
       commit_rev = stdout_s(git_cmd("rev-list", "-n1", commit_hash),
                             err: "/dev/null")
@@ -383,11 +375,17 @@ class Dispatcher
                          "GEM_PATH=#{ENV['GEM_PATH']}")
       end
 
+      repo = Repository.where(name: job.repository).first
+      if repo.nil? or repo.server_path.nil?
+        fail_job "Repository #{job.repository} not found under #{@repo_root}"
+        next
+      end
+
       ready = (get_authorization(job) and
-               get_commit(job.repository, job.script_version) and
+               get_commit(repo.server_path, job.script_version) and
                tag_commit(job.script_version, job.uuid))
       if ready and job.arvados_sdk_version
-        ready = (get_commit("arvados", job.arvados_sdk_version) and
+        ready = (get_commit(@arvados_repo_path, job.arvados_sdk_version) and
                  tag_commit(job.arvados_sdk_version, "#{job.uuid}-arvados-sdk"))
       end
       next unless ready
index 0b4d8747ea35bdd66b66182215d821058b917dfe..869d9eeeb300e1853fd0f511ab6af01c5fec6d26 100644 (file)
@@ -1,5 +1,11 @@
 # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
 
+system_user:
+  api_client: untrusted
+  user: system_user
+  api_token: systemusertesttoken1234567890aoeuidhtnsqjkxbmwvzpy
+  expires_at: 2038-01-01 00:00:00
+
 admin:
   api_client: untrusted
   user: admin
index 78120042aa625bec49a4c5bdf27f0abeb00f572b..ea6cbb0868049de00550d0b2d05706233d958d6a 100644 (file)
@@ -77,7 +77,7 @@ foobar:
   cancelled_by_user_uuid: ~
   cancelled_by_client_uuid: ~
   script: hash
-  repository: foo
+  repository: active/foo
   script_version: 7def43a4d3f20789dda4700f703b5514cc3ed250
   script_parameters:
     input: 1f4b0bc7583c2a7f9102c395f4ffc5e3+45
@@ -113,7 +113,7 @@ barbaz:
   finished_at: <%= 2.minute.ago.to_s(:db) %>
   running: false
   success: true
-  repository: foo
+  repository: active/foo
   output: ea10d51bcf88862dbcc36eb292017dfd+45
   priority: 0
   log: d41d8cd98f00b204e9800998ecf8427e+0
@@ -141,7 +141,7 @@ runningbarbaz:
   finished_at: <%= 2.minute.ago.to_s(:db) %>
   running: true
   success: ~
-  repository: foo
+  repository: active/foo
   output: ea10d51bcf88862dbcc36eb292017dfd+45
   priority: 0
   log: d41d8cd98f00b204e9800998ecf8427e+0
@@ -158,7 +158,7 @@ previous_job_run:
   uuid: zzzzz-8i9sb-cjs4pklxxjykqqq
   created_at: <%= 14.minute.ago.to_s(:db) %>
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
-  repository: foo
+  repository: active/foo
   script: hash
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   script_parameters:
@@ -172,7 +172,7 @@ previous_docker_job_run:
   uuid: zzzzz-8i9sb-k6emstgk4kw4yhi
   created_at: <%= 14.minute.ago.to_s(:db) %>
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
-  repository: foo
+  repository: active/foo
   script: hash
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   script_parameters:
@@ -189,7 +189,7 @@ previous_job_run_with_arvados_sdk_version:
   uuid: zzzzz-8i9sb-eoo0321or2dw2jg
   created_at: <%= 14.minute.ago.to_s(:db) %>
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
-  repository: foo
+  repository: active/foo
   script: hash
   script_version: 31ce37fe365b3dc204300a3e4c396ad333ed0556
   script_parameters:
@@ -206,7 +206,7 @@ previous_job_run_no_output:
   uuid: zzzzz-8i9sb-cjs4pklxxjykppp
   created_at: <%= 14.minute.ago.to_s(:db) %>
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
-  repository: foo
+  repository: active/foo
   script: hash
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   script_parameters:
@@ -220,7 +220,7 @@ nondeterminisic_job_run:
   uuid: zzzzz-8i9sb-cjs4pklxxjykyyy
   created_at: <%= 14.minute.ago.to_s(:db) %>
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
-  repository: foo
+  repository: active/foo
   script: hash2
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   script_parameters:
@@ -311,7 +311,7 @@ job_in_subproject:
   created_at: 2014-10-15 12:00:00
   owner_uuid: zzzzz-j7d0g-axqo7eu9pwvna1x
   log: ~
-  repository: foo
+  repository: active/foo
   script: hash
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   state: Complete
@@ -343,7 +343,7 @@ running_will_be_completed:
 graph_stage1:
   uuid: zzzzz-8i9sb-graphstage10000
   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
-  repository: foo
+  repository: active/foo
   script: hash
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   state: Complete
@@ -352,7 +352,7 @@ graph_stage1:
 graph_stage2:
   uuid: zzzzz-8i9sb-graphstage20000
   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
-  repository: foo
+  repository: active/foo
   script: hash2
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   state: Complete
@@ -364,7 +364,7 @@ graph_stage2:
 graph_stage3:
   uuid: zzzzz-8i9sb-graphstage30000
   owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
-  repository: foo
+  repository: active/foo
   script: hash2
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   state: Complete
@@ -380,7 +380,7 @@ job_with_latest_version:
   cancelled_by_user_uuid: ~
   cancelled_by_client_uuid: ~
   script: hash
-  repository: foo
+  repository: active/foo
   script_version: 7def43a4d3f20789dda4700f703b5514cc3ed250
   supplied_script_version: master
   script_parameters:
@@ -406,7 +406,7 @@ running_job_in_publicly_accessible_project:
   uuid: zzzzz-8i9sb-n7omg50bvt0m1nf
   owner_uuid: zzzzz-j7d0g-zhxawtyetzwc5f0
   modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
-  repository: foo
+  repository: active/foo
   script: running_job_script
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   state: Running
@@ -418,7 +418,7 @@ completed_job_in_publicly_accessible_project:
   uuid: zzzzz-8i9sb-jyq01m7in1jlofj
   owner_uuid: zzzzz-j7d0g-zhxawtyetzwc5f0
   modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
-  repository: foo
+  repository: active/foo
   script: completed_job_script
   script_version: 4fe459abe02d9b365932b8f5dc419439ab4e2577
   state: Complete
index b80414ddff84ae723cd7ca3ca5a21b24799f73b8..ab2e360218177e42bf39e422872104b43efc0503 100644 (file)
@@ -1,7 +1,7 @@
 crunch_dispatch_test:
   uuid: zzzzz-s0uqq-382brsig8rp3665
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz # active user
-  name: crunch_dispatch_test
+  name: active/crunchdispatchtest
   created_at: 2015-01-01T00:00:00.123456Z
   modified_at: 2015-01-01T00:00:00.123456Z
 
@@ -15,34 +15,27 @@ arvados:
 foo:
   uuid: zzzzz-s0uqq-382brsig8rp3666
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz # active user
-  name: foo
+  name: active/foo
   created_at: 2015-01-01T00:00:00.123456Z
   modified_at: 2015-01-01T00:00:00.123456Z
 
 repository2:
   uuid: zzzzz-s0uqq-382brsig8rp3667
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz # active user
-  name: foo2
+  name: active/foo2
   created_at: 2015-01-01T00:00:00.123456Z
   modified_at: 2015-01-01T00:00:00.123456Z
 
 repository3:
   uuid: zzzzz-s0uqq-38orljkqpyo1j61
   owner_uuid: zzzzz-tpzed-d9tiejq69daie8f # admin user
-  name: foo3
+  name: admin/foo3
   created_at: 2015-01-01T00:00:00.123456Z
   modified_at: 2015-01-01T00:00:00.123456Z
 
 repository4:
   uuid: zzzzz-s0uqq-38oru8hnk57ht34
   owner_uuid: zzzzz-tpzed-d9tiejq69daie8f # admin user
-  name: foo4
-  created_at: 2015-01-01T00:00:00.123456Z
-  modified_at: 2015-01-01T00:00:00.123456Z
-
-auto_setup_repository:
-  uuid: zzzzz-s0uqq-382brabc8rp3667
-  owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz # active user
-  name: auto_setup_repo
+  name: admin/foo4
   created_at: 2015-01-01T00:00:00.123456Z
   modified_at: 2015-01-01T00:00:00.123456Z
index 46fbd0f2896c227d97bc845d9398633f4ed42935..4e17de3bb6982fc24012eba6465e9d329e061bcd 100644 (file)
@@ -157,6 +157,7 @@ inactive:
   identity_url: https://inactive-user.openid.local
   is_active: false
   is_admin: false
+  username: inactiveuser
   prefs: {}
 
 inactive_but_signed_user_agreement:
index ceaebffb2305b2255b556c7c28519606f384910a..2b109b61f795c00fd6b3cd971ddaa94cfbca159e 100644 (file)
@@ -29,17 +29,17 @@ class Arvados::V1::CommitsControllerTest < ActionController::TestCase
     assert_includes(a, '077ba2ad3ea24a929091a9e6ce545c93199b8e57')
 
   #test "test_branch2" do
-    a = Commit.find_commit_range(users(:active), 'foo', nil, 'b1', nil)
+    a = Commit.find_commit_range(users(:active), 'active/foo', nil, 'b1', nil)
     assert_equal ['1de84a854e2b440dc53bf42f8548afa4c17da332'], a
 
   #test "test_branch3" do
-    a = Commit.find_commit_range(users(:active), 'foo', nil, 'HEAD', nil)
+    a = Commit.find_commit_range(users(:active), 'active/foo', nil, 'HEAD', nil)
     assert_equal ['1de84a854e2b440dc53bf42f8548afa4c17da332'], a
 
   #test "test_single_revision_repo" do
-    a = Commit.find_commit_range(users(:active), "foo", nil, '31ce37fe365b3dc204300a3e4c396ad333ed0556', nil)
+    a = Commit.find_commit_range(users(:active), "active/foo", nil, '31ce37fe365b3dc204300a3e4c396ad333ed0556', nil)
     assert_equal ['31ce37fe365b3dc204300a3e4c396ad333ed0556'], a
-    a = Commit.find_commit_range(users(:active), "bar", nil, '31ce37fe365b3dc204300a3e4c396ad333ed0556', nil)
+    a = Commit.find_commit_range(users(:active), "arvados", nil, '31ce37fe365b3dc204300a3e4c396ad333ed0556', nil)
     assert_equal nil, a
 
   #test "test_multi_revision" do
index 9b66851d7e0dc8885876b0b7b179c7e063782ed8..1dd620a68cd975ae3866081424a2fd00c065f5ff 100644 (file)
@@ -17,7 +17,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash",
       script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '1'
@@ -35,7 +35,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       job: {
         script: "hash",
         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-        repository: "foo",
+        repository: "active/foo",
         script_parameters: {
           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
           an_integer: '1'
@@ -55,7 +55,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       job: {
         script: "hash",
         script_version: "tag1",
-        repository: "foo",
+        repository: "active/foo",
         script_parameters: {
           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
           an_integer: '1'
@@ -76,7 +76,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
         no_reuse: true,
         script: "hash",
         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-        repository: "foo",
+        repository: "active/foo",
         script_parameters: {
           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
           an_integer: '1'
@@ -96,7 +96,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
         job: {
           script: "hash",
           script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-          repository: "foo",
+          repository: "active/foo",
           script_parameters: {
             input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
             an_integer: '1'
@@ -118,7 +118,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       job: {
         script: "hash",
         script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-        repository: "foo",
+        repository: "active/foo",
         script_parameters: {
           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
           an_integer: '1'
@@ -138,7 +138,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash",
       script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '2'
@@ -156,7 +156,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       script: "hash",
       minimum_script_version: "tag1",
       script_version: "master",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '1'
@@ -174,7 +174,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash",
       script_version: "master",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '1'
@@ -192,7 +192,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash",
       script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '2'
@@ -210,7 +210,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash",
       script_version: "master",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '2'
@@ -228,7 +228,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash",
       script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '1'
@@ -247,7 +247,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash2",
       script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '1'
@@ -266,7 +266,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       no_reuse: false,
       script: "hash",
       script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '1'
@@ -285,7 +285,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       script: "hash",
       minimum_script_version: "31ce37fe365b3dc204300a3e4c396ad333ed0556",
       script_version: "master",
-      repository: "foo",
+      repository: "active/foo",
       exclude_script_versions: ["tag1"],
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
@@ -305,7 +305,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
       job: {
         script: "hash",
         script_version: "master",
-        repository: "foo",
+        repository: "active/foo",
         script_parameters: {
           input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
           an_integer: '1'
@@ -324,7 +324,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
   end
 
   BASE_FILTERS = {
-    'repository' => ['=', 'foo'],
+    'repository' => ['=', 'active/foo'],
     'script' => ['=', 'hash'],
     'script_version' => ['in git', 'master'],
     'docker_image_locator' => ['=', nil],
@@ -342,7 +342,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
            job: {
              script: "hash",
              script_version: "master",
-             repository: "foo",
+             repository: "active/foo",
              script_parameters: {
                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                an_integer: '1'
@@ -368,7 +368,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
            job: {
              script: "hash",
              script_version: "master",
-             repository: "foo",
+             repository: "active/foo",
              script_parameters: {
                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                an_integer: '1'
@@ -391,7 +391,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
            job: {
              script: "hash",
              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-             repository: "foo",
+             repository: "active/foo",
              script_parameters: {
                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                an_integer: '1'
@@ -412,7 +412,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
            job: {
              script: "hash",
              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-             repository: "foo",
+             repository: "active/foo",
              script_parameters: {
                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                an_integer: '1'
@@ -442,7 +442,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
            job: {
              script: "hash",
              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-             repository: "foo",
+             repository: "active/foo",
              script_parameters: {
                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                an_integer: '1'
@@ -470,7 +470,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
            job: {
              script: "hash",
              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-             repository: "foo",
+             repository: "active/foo",
              script_parameters: {
                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                an_integer: '1'
@@ -495,7 +495,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
            job: {
              script: "hash",
              script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-             repository: "foo",
+             repository: "active/foo",
              script_parameters: {
                input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                an_integer: '1'
@@ -517,7 +517,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
              job: {
                script: "hash",
                script_version: "master",
-               repository: "foo",
+               repository: "active/foo",
                script_parameters: {
                  input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
                  an_integer: '1'
@@ -532,7 +532,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
   end
 
   test "find Job with script version range" do
-    get :index, filters: [["repository", "=", "foo"],
+    get :index, filters: [["repository", "=", "active/foo"],
                           ["script", "=", "hash"],
                           ["script_version", "in git", "tag1"]]
     assert_response :success
@@ -542,7 +542,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
   end
 
   test "find Job with script version range exclusions" do
-    get :index, filters: [["repository", "=", "foo"],
+    get :index, filters: [["repository", "=", "active/foo"],
                           ["script", "=", "hash"],
                           ["script_version", "not in git", "tag1"]]
     assert_response :success
@@ -607,7 +607,7 @@ class Arvados::V1::JobReuseControllerTest < ActionController::TestCase
     params[:job] = {
       script: "hash",
       script_version: "4fe459abe02d9b365932b8f5dc419439ab4e2577",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {
         input: 'fa7aeb5140e2848d39b416daeef4ffc5+45',
         an_integer: '1',
index 07e7f840a1bafedcd456d2d674e1ad243b59ec0a..b8b061f69b774fb7f2b330517d37d00f1da4e64a 100644 (file)
@@ -10,7 +10,7 @@ class Arvados::V1::JobsControllerTest < ActionController::TestCase
     post :create, job: {
       script: "hash",
       script_version: "master",
-      repository: "foo",
+      repository: "active/foo",
       script_parameters: {}
     }
     assert_response :success
@@ -27,7 +27,7 @@ class Arvados::V1::JobsControllerTest < ActionController::TestCase
       script: "hash",
       script_version: "master",
       script_parameters: {},
-      repository: "foo",
+      repository: "active/foo",
       started_at: Time.now,
       finished_at: Time.now,
       running: false,
index 5304bcafc531bc1138c8c17071fd2793d71aff1c..128e4909d6e46e42b6927b69754ae88fae59563e 100644 (file)
@@ -87,4 +87,20 @@ class Arvados::V1::RepositoriesControllerTest < ActionController::TestCase
                    "response public_key does not match fixture #{u}.")
     end
   end
+
+  test "default index includes fetch_url" do
+    authorize_with :active
+    get(:index)
+    assert_response :success
+    assert_includes(json_response["items"].map { |r| r["fetch_url"] },
+                    "git@git.zzzzz.arvadosapi.com:active/foo.git")
+  end
+
+  test "can select push_url in index" do
+    authorize_with :active
+    get(:index, {select: ["uuid", "push_url"]})
+    assert_response :success
+    assert_includes(json_response["items"].map { |r| r["push_url"] },
+                    "git@git.zzzzz.arvadosapi.com:active/foo.git")
+  end
 end
index 2d26370b749f5b07dc866855563c62cd31f9c03a..f776ad2e56289727a577d8895ac730d27cc0f6a7 100644 (file)
@@ -83,7 +83,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
   test "create user with user, vm and repo as input" do
     authorize_with :admin
-    repo_name = 'test_repo'
+    repo_name = 'usertestrepo'
 
     post :setup, {
       repo_name: repo_name,
@@ -113,7 +113,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
         created['uuid'], created['email'], 'arvados#user', false, 'User'
 
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        repo_name, created['uuid'], 'arvados#repository', true, 'Repository'
+        "foo/#{repo_name}", created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
@@ -129,7 +129,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     post :setup, {
       uuid: 'bogus_uuid',
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       vm_uuid: @vm_uuid
     }
     response_body = JSON.parse(@response.body)
@@ -143,7 +143,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     post :setup, {
       user: {uuid: 'bogus_uuid'},
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       vm_uuid: @vm_uuid,
       openid_prefix: 'https://www.google.com/accounts/o8/id'
     }
@@ -158,7 +158,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
     authorize_with :admin
 
     post :setup, {
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       vm_uuid: @vm_uuid,
       openid_prefix: 'https://www.google.com/accounts/o8/id'
     }
@@ -174,7 +174,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     post :setup, {
       user: {},
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       vm_uuid: @vm_uuid,
       openid_prefix: 'https://www.google.com/accounts/o8/id'
     }
@@ -191,7 +191,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     post :setup, {
       uuid: users(:inactive).uuid,
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       vm_uuid: @vm_uuid
     }
 
@@ -207,7 +207,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     # expect repo and vm links
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        'test_repo', resp_obj['uuid'], 'arvados#repository', true, 'Repository'
+        'inactiveuser/usertestrepo', resp_obj['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#virtualMachine', true, 'permission', 'can_login',
         @vm_uuid, resp_obj['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
@@ -257,7 +257,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
     authorize_with :admin
 
     post :setup, {
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       user: {email: 'foo@example.com'},
       openid_prefix: 'https://www.google.com/accounts/o8/id'
     }
@@ -276,7 +276,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
     authorize_with :admin
 
     post :setup, {
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       vm_uuid: 'no_such_vm',
       user: {email: 'foo@example.com'},
       openid_prefix: 'https://www.google.com/accounts/o8/id'
@@ -293,7 +293,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
     authorize_with :admin
 
     post :setup, {
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       openid_prefix: 'https://www.google.com/accounts/o8/id',
       vm_uuid: @vm_uuid,
       user: {email: 'foo@example.com'}
@@ -333,7 +333,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
         'All users', response_object['uuid'], 'arvados#group', true, 'Group'
 
     verify_link response_items, 'arvados#repository', false, 'permission', 'can_manage',
-        'test_repo', response_object['uuid'], 'arvados#repository', true, 'Repository'
+        'foo/usertestrepo', response_object['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
         nil, response_object['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
@@ -344,7 +344,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     post :setup, {
       openid_prefix: 'https://www.google.com/accounts/o8/id',
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       vm_uuid: @vm_uuid,
       user: {
         first_name: 'test_first_name',
@@ -370,7 +370,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     post :setup, {
       openid_prefix: 'https://www.google.com/accounts/o8/id',
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       user: {
         email: inactive_user['email']
       }
@@ -391,7 +391,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
     authorize_with :admin
 
     post :setup, {
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       openid_prefix: 'http://www.example.com/account',
       user: {
         first_name: "in_create_test_first_name",
@@ -418,7 +418,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
         created['uuid'], created['email'], 'arvados#user', false, 'User'
 
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        'test_repo', created['uuid'], 'arvados#repository', true, 'Repository'
+        'foo/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
@@ -431,7 +431,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
     authorize_with :admin
 
     post :setup, {
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       user: {
         first_name: "in_create_test_first_name",
         last_name: "test_last_name",
@@ -456,7 +456,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
         email: "foo@example.com"
       },
       vm_uuid: @vm_uuid,
-      repo_name: 'test_repo',
+      repo_name: 'usertestrepo',
       openid_prefix: 'https://www.google.com/accounts/o8/id'
     }
 
@@ -478,7 +478,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
         created['uuid'], created['email'], 'arvados#user', false, 'User'
 
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        'test_repo', created['uuid'], 'arvados#repository', true, 'Repository'
+        'foo/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
@@ -522,7 +522,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
 
     # invoke setup with a repository
     post :setup, {
-      repo_name: 'new_repo',
+      repo_name: 'usertestrepo',
       uuid: active_user['uuid']
     }
 
@@ -538,7 +538,7 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
         'All users', created['uuid'], 'arvados#group', true, 'Group'
 
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        'new_repo', created['uuid'], 'arvados#repository', true, 'Repository'
+        'active/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
         nil, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
@@ -547,6 +547,11 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
   test "setup active user with vm and no repo" do
     authorize_with :admin
     active_user = users(:active)
+    repos_query = Repository.where(owner_uuid: active_user.uuid)
+    repo_link_query = Link.where(tail_uuid: active_user.uuid,
+                                 link_class: "permission", name: "can_manage")
+    repos_count = repos_query.count
+    repo_link_count = repo_link_query.count
 
     # invoke setup with a repository
     post :setup, {
@@ -566,8 +571,8 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
 
-    verify_link response_items, 'arvados#repository', false, 'permission', 'can_manage',
-        'new_repo', created['uuid'], 'arvados#repository', true, 'Repository'
+    assert_equal(repos_count, repos_query.count)
+    assert_equal(repo_link_count, repo_link_query.count)
 
     verify_link response_items, 'arvados#virtualMachine', true, 'permission', 'can_login',
         @vm_uuid, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
index 81767af905cc609f3cfc18a56b404446cbb10bb1..a6f937bbda66c0efa3a52630e15a0b37278579b2 100644 (file)
@@ -28,7 +28,7 @@ class CrunchDispatchTest < ActionDispatch::IntegrationTest
       format: "json",
       job: {
         script: "log",
-        repository: "crunch_dispatch_test",
+        repository: "active/crunchdispatchtest",
         script_version: "f35f99b7d32bac257f5989df02b9f12ee1a9b0d6",
         script_parameters: "{}"
       }
index 8a1cb10004f2bcfedf379d3d944c4251828fd02f..36c533a9bbe8d1deb1366b971ac325bd08a37978 100644 (file)
@@ -12,7 +12,7 @@ class SerializedEncodingTest < ActionDispatch::IntegrationTest
     human: {properties: {eye_color: 'gray'}},
 
     job: {
-      repository: 'foo',
+      repository: 'active/foo',
       runtime_constraints: {docker_image: 'arvados/apitestfixture'},
       script: 'hash',
       script_version: 'master',
index 0d6c0f360f3156ae15e32f0098c510afb00e796f..38ac12267aaf8ec6b894835ae0f5876aff9e04d2 100644 (file)
@@ -5,7 +5,7 @@ class UsersTest < ActionDispatch::IntegrationTest
   include UsersTestHelper
 
   test "setup user multiple times" do
-    repo_name = 'test_repo'
+    repo_name = 'usertestrepo'
 
     post "/arvados/v1/users/setup", {
       repo_name: repo_name,
@@ -35,7 +35,7 @@ class UsersTest < ActionDispatch::IntegrationTest
         created['uuid'], created['email'], 'arvados#user', false, 'arvados#user'
 
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        repo_name, created['uuid'], 'arvados#repository', true, 'Repository'
+        'foo/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
@@ -71,7 +71,7 @@ class UsersTest < ActionDispatch::IntegrationTest
 
     # arvados#user, repo link and link add user to 'All users' group
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        repo_name, created['uuid'], 'arvados#repository', true, 'Repository'
+        'foo/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
@@ -105,16 +105,13 @@ class UsersTest < ActionDispatch::IntegrationTest
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
 
-    verify_link response_items, 'arvados#repository', false, 'permission', 'can_manage',
-        'test_repo', created['uuid'], 'arvados#repository', true, 'Repository'
-
     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
         nil, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
 
    # invoke setup with a repository
     post "/arvados/v1/users/setup", {
       openid_prefix: 'http://www.example.com/account',
-      repo_name: 'new_repo',
+      repo_name: 'newusertestrepo',
       uuid: created['uuid']
     }, auth(:admin)
 
@@ -130,7 +127,7 @@ class UsersTest < ActionDispatch::IntegrationTest
         'All users', created['uuid'], 'arvados#group', true, 'Group'
 
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        'new_repo', created['uuid'], 'arvados#repository', true, 'Repository'
+        'foo/newusertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
         nil, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
@@ -156,17 +153,13 @@ class UsersTest < ActionDispatch::IntegrationTest
     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
         'All users', created['uuid'], 'arvados#group', true, 'Group'
 
-    # since no repo name in input, we won't get any; even though user has one
-    verify_link response_items, 'arvados#repository', false, 'permission', 'can_manage',
-        'new_repo', created['uuid'], 'arvados#repository', true, 'Repository'
-
     verify_link response_items, 'arvados#virtualMachine', true, 'permission', 'can_login',
         virtual_machines(:testvm).uuid, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
   end
 
   test "setup and unsetup user" do
     post "/arvados/v1/users/setup", {
-      repo_name: 'test_repo',
+      repo_name: 'newusertestrepo',
       vm_uuid: virtual_machines(:testvm).uuid,
       user: {email: 'foo@example.com'},
       openid_prefix: 'https://www.google.com/accounts/o8/id'
@@ -186,7 +179,7 @@ class UsersTest < ActionDispatch::IntegrationTest
         'All users', created['uuid'], 'arvados#group', true, 'Group'
 
     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
-        'test_repo', created['uuid'], 'arvados#repository', true, 'Repository'
+        'foo/newusertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
 
     verify_link response_items, 'arvados#virtualMachine', true, 'permission', 'can_login',
         virtual_machines(:testvm).uuid, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
index ae466016a3ab658bf133bcb3d5f0ce9358900a05..9b8e4d5a1671321630e342c6ced56d2fc31e0d15 100644 (file)
Binary files a/services/api/test/test.git.tar and b/services/api/test/test.git.tar differ
index 5ea6e62bfa73381e0f7e95b79aba31be7143ee08..f155fc070848ccd926c87ca9c057e127d7bb856c 100644 (file)
@@ -52,6 +52,16 @@ class ActiveSupport::TestCase
     restore_configuration
   end
 
+  def assert_not_allowed
+    # Provide a block that calls a Rails boolean "true or false" success value,
+    # like model.save or model.destroy.  This method will test that it either
+    # returns false, or raises a Permission Denied exception.
+    begin
+      refute(yield)
+    rescue ArvadosModel::PermissionDeniedError
+    end
+  end
+
   def restore_configuration
     # Restore configuration settings changed during tests
     $application_config.each do |k,v|
index 24bc2600103b2589c578bad481f05e0b0b309867..1c8573eb190c3d07ef957c8c5c0ff1e37844ae40 100644 (file)
@@ -15,7 +15,7 @@ class JobTest < ActiveSupport::TestCase
     {
       script: "hash",
       script_version: "master",
-      repository: "foo",
+      repository: "active/foo",
     }.merge(merge_me)
   end
 
index 20cffdaaa743cee800ff484a8709ca18f30f097f..4a6ddc69fbcb1703c3234e4e6e1360a779b2f4b3 100644 (file)
@@ -353,18 +353,4 @@ class PermissionTest < ActiveSupport::TestCase
       ob.update_attributes!(owner_uuid: groups(:aproject).uuid)
     end
   end
-
-  test "active user cannot write admin's repo" do
-    set_user_from_auth :active
-    assert_raises ArvadosModel::PermissionDeniedError, "pwned" do
-      repositories(:repository3).update_attributes(name: "kilroy")
-    end
-  end
-
-  test "active user cannot change repo name via can_manage permission" do
-    set_user_from_auth :active
-    assert_raises ArvadosModel::PermissionDeniedError, "pwned" do
-      repositories(:foo).update_attributes(name: "arvados")
-    end
-  end
 end
index 4e160dd7e1c3d53c3ccb1825c227ab72f9563550..ef780f893cad7b8ede7ca62fbd51c8f7108d86bf 100644 (file)
@@ -1,6 +1,233 @@
 require 'test_helper'
+require 'helpers/git_test_helper'
 
 class RepositoryTest < ActiveSupport::TestCase
+  include GitTestHelper
+
+  def new_repo(owner_key, attrs={})
+    set_user_from_auth owner_key
+    owner = users(owner_key)
+    Repository.new({owner_uuid: owner.uuid}.merge(attrs))
+  end
+
+  def changed_repo(repo_key, changes)
+    repo = repositories(repo_key)
+    changes.each_pair { |attr, value| repo.send("#{attr}=".to_sym, value) }
+    repo
+  end
+
+  def default_git_url(repo_name, user_name=nil)
+    if user_name
+      "git@git.%s.arvadosapi.com:%s/%s.git" %
+        [Rails.configuration.uuid_prefix, user_name, repo_name]
+    else
+      "git@git.%s.arvadosapi.com:%s.git" %
+        [Rails.configuration.uuid_prefix, repo_name]
+    end
+  end
+
+  def assert_server_path(path_tail, repo_sym)
+    assert_equal(File.join(Rails.configuration.git_repositories_dir, path_tail),
+                 repositories(repo_sym).server_path)
+  end
+
+  ### name validation
+
+  {active: "active/", admin: "admin/", system_user: ""}.
+      each_pair do |user_sym, name_prefix|
+    %w(a aa a0 aA Aa AA A0).each do |name|
+      test "'#{name_prefix}#{name}' is a valid name for #{user_sym} repo" do
+        repo = new_repo(user_sym, name: name_prefix + name)
+        assert(repo.valid?)
+      end
+    end
+
+    test "name is required for #{user_sym} repo" do
+      refute(new_repo(user_sym).valid?)
+    end
+
+    test "repo name beginning with numeral is invalid for #{user_sym}" do
+      repo = new_repo(user_sym, name: "#{name_prefix}0a")
+      refute(repo.valid?)
+    end
+
+    "\\.-_/!@#$%^&*()[]{}".each_char do |bad_char|
+      test "name containing #{bad_char.inspect} is invalid for #{user_sym}" do
+        repo = new_repo(user_sym, name: "#{name_prefix}bad#{bad_char}reponame")
+        refute(repo.valid?)
+      end
+    end
+  end
+
+  test "admin can create valid repo for other user with correct name prefix" do
+    owner = users(:active)
+    repo = new_repo(:admin, name: "#{owner.username}/validnametest",
+                    owner_uuid: owner.uuid)
+    assert(repo.valid?)
+  end
+
+  test "admin can create valid system repo without name prefix" do
+    repo = new_repo(:admin, name: "validnametest",
+                    owner_uuid: users(:system_user).uuid)
+    assert(repo.valid?)
+  end
+
+  test "repo name prefix must match owner_uuid username" do
+    repo = new_repo(:admin, name: "admin/badusernametest",
+                    owner_uuid: users(:active).uuid)
+    refute(repo.valid?)
+  end
+
+  test "repo name prefix must be empty for system repo" do
+    repo = new_repo(:admin, name: "root/badprefixtest",
+                    owner_uuid: users(:system_user).uuid)
+    refute(repo.valid?)
+  end
+
+  ### owner validation
+
+  test "name must be unique per user" do
+    repo = new_repo(:active, name: repositories(:foo).name)
+    refute(repo.valid?)
+  end
+
+  test "name can be duplicated across users" do
+    repo = new_repo(:active, name: "active/#{repositories(:arvados).name}")
+    assert(repo.valid?)
+  end
+
+  test "repository cannot be owned by a group" do
+    set_user_from_auth :active
+    repo = Repository.new(owner_uuid: groups(:all_users).uuid,
+                          name: "ownedbygroup")
+    refute(repo.valid?)
+    refute_empty(repo.errors[:owner_uuid] || [])
+  end
+
+  ### URL generation
+
+  test "fetch_url" do
+    repo = new_repo(:active, name: "active/fetchtest")
+    assert_equal(default_git_url("fetchtest", "active"), repo.fetch_url)
+  end
+
+  test "fetch_url owned by system user" do
+    set_user_from_auth :admin
+    repo = Repository.new(owner_uuid: users(:system_user).uuid,
+                          name: "fetchtest")
+    assert_equal(default_git_url("fetchtest"), repo.fetch_url)
+  end
+
+  test "push_url" do
+    repo = new_repo(:active, name: "active/pushtest")
+    assert_equal(default_git_url("pushtest", "active"), repo.push_url)
+  end
+
+  test "push_url owned by system user" do
+    set_user_from_auth :admin
+    repo = Repository.new(owner_uuid: users(:system_user).uuid,
+                          name: "pushtest")
+    assert_equal(default_git_url("pushtest"), repo.push_url)
+  end
+
+  ### Path generation
+
+  test "disk path stored by UUID" do
+    assert_server_path("zzzzz-s0uqq-382brsig8rp3666/.git", :foo)
+  end
+
+  test "disk path stored by name" do
+    assert_server_path("arvados/.git", :arvados)
+  end
+
+  test "disk path for repository not on disk" do
+    assert_nil(Repository.new.server_path)
+  end
+
+  ### Repository creation
+
+  test "non-admin can create a repository for themselves" do
+    repo = new_repo(:active, name: "active/newtestrepo")
+    assert(repo.save)
+  end
+
+  test "non-admin can't create a repository for another visible user" do
+    repo = new_repo(:active, name: "repoforanon",
+                    owner_uuid: users(:anonymous).uuid)
+    assert_not_allowed { repo.save }
+  end
+
+  test "admin can create a repository for themselves" do
+    repo = new_repo(:admin, name: "admin/newtestrepo")
+    assert(repo.save)
+  end
+
+  test "admin can create a repository for others" do
+    repo = new_repo(:admin, name: "active/repoforactive",
+                    owner_uuid: users(:active).uuid)
+    assert(repo.save)
+  end
+
+  test "admin can create a system repository" do
+    repo = new_repo(:admin, name: "repoforsystem",
+                    owner_uuid: users(:system_user).uuid)
+    assert(repo.save)
+  end
+
+  ### Repository destruction
+
+  test "non-admin can destroy their own repository" do
+    set_user_from_auth :active
+    assert(repositories(:foo).destroy)
+  end
+
+  test "non-admin can't destroy others' repository" do
+    set_user_from_auth :active
+    assert_not_allowed { repositories(:repository3).destroy }
+  end
+
+  test "non-admin can't destroy system repository" do
+    set_user_from_auth :active
+    assert_not_allowed { repositories(:arvados).destroy }
+  end
+
+  test "admin can destroy their own repository" do
+    set_user_from_auth :admin
+    assert(repositories(:repository3).destroy)
+  end
+
+  test "admin can destroy others' repository" do
+    set_user_from_auth :admin
+    assert(repositories(:foo).destroy)
+  end
+
+  test "admin can destroy system repository" do
+    set_user_from_auth :admin
+    assert(repositories(:arvados).destroy)
+  end
+
+  ### Changing ownership
+
+  test "non-admin can't make their repository a system repository" do
+    set_user_from_auth :active
+    repo = changed_repo(:foo, owner_uuid: users(:system_user).uuid)
+    assert_not_allowed { repo.save }
+  end
+
+  test "admin can give their repository to someone else" do
+    set_user_from_auth :admin
+    repo = changed_repo(:repository3, owner_uuid: users(:active).uuid,
+                        name: "active/foo3")
+    assert(repo.save)
+  end
+
+  test "admin can make their repository a system repository" do
+    set_user_from_auth :admin
+    repo = changed_repo(:repository3, owner_uuid: users(:system_user).uuid,
+                        name: "foo3")
+    assert(repo.save)
+  end
+
   test 'write permission allows changing modified_at' do
     act_as_user users(:active) do
       r = repositories(:foo)
index 6eabdfd359a4ef5601023fe9fb217e239bf50bd3..45dd186dfd1b47f41a98441218976c11bb2dcef7 100644 (file)
@@ -39,14 +39,10 @@ class UserTest < ActiveSupport::TestCase
   end
 
   test "non-admin can't update username" do
-    set_user_from_auth :active_trustedclient
-    user = User.find_by_uuid(users(:active).uuid)
+    set_user_from_auth :rominiadmin
+    user = User.find_by_uuid(users(:rominiadmin).uuid)
     user.username = "selfupdate"
-    begin
-      refute(user.save)
-    rescue ArvadosModel::PermissionDeniedError
-      # That works too.
-    end
+    assert_not_allowed { user.save }
   end
 
   def check_admin_username_change(fixture_name)
@@ -108,6 +104,42 @@ class UserTest < ActiveSupport::TestCase
     check_new_username_setting("_", nil)
   end
 
+  test "updating username updates repository names" do
+    set_user_from_auth :admin
+    user = users(:active)
+    user.username = "newtestname"
+    assert(user.save, "username update failed")
+    {foo: "newtestname/foo", repository2: "newtestname/foo2"}.
+        each_pair do |repo_sym, expect_name|
+      assert_equal(expect_name, repositories(repo_sym).name)
+    end
+  end
+
+  test "admin can clear username when user owns no repositories" do
+    set_user_from_auth :admin
+    user = users(:spectator)
+    user.username = nil
+    assert(user.save)
+    assert_nil(user.username)
+  end
+
+  test "admin can't clear username when user owns repositories" do
+    set_user_from_auth :admin
+    user = users(:active)
+    start_username = user.username
+    user.username = nil
+    assert_not_allowed { user.save }
+    refute_empty(user.errors[:username])
+  end
+
+  test "failed username update doesn't change repository names" do
+    set_user_from_auth :admin
+    user = users(:active)
+    user.username = users(:fuse).username
+    assert_not_allowed { user.save }
+    assert_equal("active/foo", repositories(:foo).name)
+  end
+
   [[false, 'foo@example.com', true, nil],
    [false, 'bar@example.com', nil, true],
    [true, 'foo@example.com', true, nil],
@@ -391,14 +423,7 @@ class UserTest < ActiveSupport::TestCase
 
   test "create new user as non-admin user" do
     set_user_from_auth :active
-
-    begin
-      user = User.new
-      user.save
-    rescue ArvadosModel::PermissionDeniedError => e
-    end
-    assert (e.message.include? 'PermissionDeniedError'),
-        'Expected PermissionDeniedError'
+    assert_not_allowed { User.new.save }
   end
 
   test "setup new user" do
@@ -411,7 +436,7 @@ class UserTest < ActiveSupport::TestCase
 
     vm = VirtualMachine.create
 
-    response = User.setup user, openid_prefix, 'test_repo', vm.uuid
+    response = User.setup user, openid_prefix, 'foo/testrepo', vm.uuid
 
     resp_user = find_obj_in_resp response, 'User'
     verify_user resp_user, email
@@ -453,7 +478,7 @@ class UserTest < ActiveSupport::TestCase
 
     verify_link resp_link, 'permission', 'can_login', email, bad_uuid
 
-    response = User.setup user, openid_prefix, 'test_repo', vm.uuid
+    response = User.setup user, openid_prefix, 'foo/testrepo', vm.uuid
 
     resp_user = find_obj_in_resp response, 'User'
     verify_user resp_user, email
@@ -499,7 +524,7 @@ class UserTest < ActiveSupport::TestCase
     verify_link group_perm, 'permission', 'can_read', resp_user[:uuid], nil
 
     # invoke setup again with repo_name
-    response = User.setup user, openid_prefix, 'test_repo'
+    response = User.setup user, openid_prefix, 'foo/testrepo'
     resp_user = find_obj_in_resp response, 'User', nil
     verify_user resp_user, email
     assert_equal user.uuid, resp_user[:uuid], 'expected uuid not found'
@@ -513,7 +538,7 @@ class UserTest < ActiveSupport::TestCase
     # invoke setup again with a vm_uuid
     vm = VirtualMachine.create
 
-    response = User.setup user, openid_prefix, 'test_repo', vm.uuid
+    response = User.setup user, openid_prefix, 'foo/testrepo', vm.uuid
 
     resp_user = find_obj_in_resp response, 'User', nil
     verify_user resp_user, email
@@ -582,7 +607,8 @@ class UserTest < ActiveSupport::TestCase
 
     can_setup = (Rails.configuration.auto_setup_new_users and
                  (not expect_username.nil?))
-    prior_repo = Repository.where(name: expect_username).first
+    expect_repo_name = "#{expect_username}/#{expect_username}"
+    prior_repo = Repository.where(name: expect_repo_name).first
 
     user = User.new
     user.first_name = "first_name_for_newly_created_user"
@@ -600,7 +626,7 @@ class UserTest < ActiveSupport::TestCase
                        user.uuid, user.email, "permission", "can_login")
     # Check for repository.
     if named_repo = (prior_repo or
-                     Repository.where(name: expect_username).first)
+                     Repository.where(name: expect_repo_name).first)
       verify_link_exists((can_setup and prior_repo.nil? and
                           Rails.configuration.auto_setup_new_users_with_repository),
                          named_repo.uuid, user.uuid, "permission", "can_manage")