[ARVADOS] created: 755faba1d06331d50dc14d5982cb0ec53a3fa0a6

git at public.curoverse.com git at public.curoverse.com
Fri Feb 6 22:41:21 EST 2015


        at  755faba1d06331d50dc14d5982cb0ec53a3fa0a6 (commit)


commit 755faba1d06331d50dc14d5982cb0ec53a3fa0a6
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri Feb 6 21:36:39 2015 -0500

    3410: Fix munge_manifest_locators!: don't skip locators that have no +hints.
    
    Also, fix portable_manifest_text: do not add a trailing + to a locator
    that has no size hint.
    
    Portable data hash of ". d41d8cd98f00b204e9800998ecf8427e+Foo 0:0:x\n"
    is md5(". d41d8cd98f00b204e9800998ecf8427e 0:0:x\n")
    not md5(". d41d8cd98f00b204e9800998ecf8427e+ 0:0:x\n")

diff --git a/services/api/app/models/collection.rb b/services/api/app/models/collection.rb
index 1e4d4d7..d1acc4e 100644
--- a/services/api/app/models/collection.rb
+++ b/services/api/app/models/collection.rb
@@ -209,7 +209,7 @@ class Collection < ArvadosModel
   def self.munge_manifest_locators! manifest
     # Given a manifest text and a block, yield each locator,
     # and replace it with whatever the block returns.
-    manifest.andand.gsub!(/ [[:xdigit:]]{32}(\+[[:digit:]]+)?(\+\S+)/) do |word|
+    manifest.andand.gsub!(/ [[:xdigit:]]{32}(\+\S+)?/) do |word|
       if loc = Keep::Locator.parse(word.strip)
         " " + yield(loc)
       else
@@ -308,7 +308,11 @@ class Collection < ArvadosModel
   def portable_manifest_text
     portable_manifest = self[:manifest_text].dup
     self.class.munge_manifest_locators!(portable_manifest) do |loc|
-      loc.hash + '+' + loc.size.to_s
+      if loc.size
+        loc.hash + '+' + loc.size.to_s
+      else
+        loc.hash
+      end
     end
     portable_manifest
   end
diff --git a/services/api/test/unit/collection_test.rb b/services/api/test/unit/collection_test.rb
index b4db695..36caaff 100644
--- a/services/api/test/unit/collection_test.rb
+++ b/services/api/test/unit/collection_test.rb
@@ -82,6 +82,21 @@ class CollectionTest < ActiveSupport::TestCase
     end
   end
 
+  test 'portable data hash with missing size hints' do
+    [[". d41d8cd98f00b204e9800998ecf8427e+0+Bar 0:0:x",
+      ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:x"],
+     [". d41d8cd98f00b204e9800998ecf8427e+Foo 0:0:x",
+      ". d41d8cd98f00b204e9800998ecf8427e 0:0:x"],
+     [". d41d8cd98f00b204e9800998ecf8427e 0:0:x",
+      ". d41d8cd98f00b204e9800998ecf8427e 0:0:x"],
+    ].each do |unportable, portable|
+      c = Collection.new(manifest_text: unportable)
+      assert c.valid?
+      assert_equal(Digest::MD5.hexdigest(portable)+"+#{portable.length}",
+                   c.portable_data_hash)
+    end
+  end
+
   [0, 2, 4, nil].each do |ask|
     test "replication_desired reports #{ask or 'default'} if redundancy is #{ask}" do
       Rails.configuration.default_collection_replication = 2

commit 3de9172e76e7b8c39c5ab37adb145fdc94795f37
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri Feb 6 19:07:43 2015 -0500

    3410: Add tests for replication attributes.

diff --git a/services/api/app/models/collection.rb b/services/api/app/models/collection.rb
index 166292a..1e4d4d7 100644
--- a/services/api/app/models/collection.rb
+++ b/services/api/app/models/collection.rb
@@ -220,8 +220,8 @@ class Collection < ArvadosModel
 
   def self.each_manifest_locator manifest
     # Given a manifest text and a block, yield each locator.
-    manifest.andand.scan(/ ([[:xdigit:]]{32}(\+[[:digit:]]+)?(\+\S+))/) do |word|
-      if loc = Keep::Locator.parse(word[1])
+    manifest.andand.scan(/ ([[:xdigit:]]{32}(\+\S+)?)/) do |word, _|
+      if loc = Keep::Locator.parse(word)
         yield loc
       end
     end
@@ -331,8 +331,8 @@ class Collection < ArvadosModel
       end
       self.class.each_manifest_locator(manifest_text) do |loc|
         if not in_old_manifest[loc.hash]
-          replication_confirmed_at = nil
-          replication_confirmed = nil
+          self.replication_confirmed_at = nil
+          self.replication_confirmed = nil
           break
         end
       end
diff --git a/services/api/test/fixtures/collections.yml b/services/api/test/fixtures/collections.yml
index f28606a..fea457f 100644
--- a/services/api/test/fixtures/collections.yml
+++ b/services/api/test/fixtures/collections.yml
@@ -366,6 +366,48 @@ upload_sandbox:
   manifest_text: ''
   name: upload sandbox
 
+replication_undesired_unconfirmed:
+  owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  created_at: 2015-02-07 00:19:28.596506247 Z
+  modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  modified_at: 2015-02-07 00:19:28.596338465 Z
+  portable_data_hash: fa7aeb5140e2848d39b416daeef4ffc5+45
+  replication_desired: ~
+  replication_confirmed_at: ~
+  replication_confirmed: ~
+  updated_at: 2015-02-07 00:19:28.596236608 Z
+  uuid: zzzzz-4zz18-wjxq7uzx2m9jj4a
+  manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
+  name: replication wantnull havenull
+
+replication_desired_2_unconfirmed:
+  owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  created_at: 2015-02-07 00:21:35.050333515 Z
+  modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  modified_at: 2015-02-07 00:21:35.050189104 Z
+  portable_data_hash: fa7aeb5140e2848d39b416daeef4ffc5+45
+  replication_desired: 2
+  replication_confirmed_at: ~
+  replication_confirmed: ~
+  updated_at: 2015-02-07 00:21:35.050126576 Z
+  uuid: zzzzz-4zz18-3t236wrz4769h7x
+  manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
+  name: replication want2 havenull
+
+replication_desired_2_confirmed_2:
+  owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  created_at: 2015-02-07 00:19:28.596506247 Z
+  modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  modified_at: 2015-02-07 00:19:28.596338465 Z
+  portable_data_hash: fa7aeb5140e2848d39b416daeef4ffc5+45
+  replication_desired: 2
+  replication_confirmed_at: 2015-02-07 00:24:52.983381227 Z
+  replication_confirmed: 2
+  updated_at: 2015-02-07 00:24:52.983381227 Z
+  uuid: zzzzz-4zz18-434zv1tnnf2rygp
+  manifest_text: ". acbd18db4cc2f85cedef654fccc4a4d8+3 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 3:6:bar\n"
+  name: replication want2 have2
+
 # Test Helper trims the rest of the file
 
 # Do not add your fixtures below this line as the rest of this file will be trimmed by test_helper
diff --git a/services/api/test/functional/arvados/v1/collections_controller_test.rb b/services/api/test/functional/arvados/v1/collections_controller_test.rb
index c3b5303..e01ab9e 100644
--- a/services/api/test/functional/arvados/v1/collections_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/collections_controller_test.rb
@@ -697,17 +697,13 @@ EOS
   end
 
   [1, 5, nil].each do |ask|
-    test "Set replication_desired=#{ask} using redundancy attr" do
-      # The Python SDK checks the Collection schema in the discovery
-      # doc, then asks for 'redundancy' or 'replication_desired'
-      # accordingly, so it isn't necessary to maintain backward
-      # compatibility here when the attribute changes to
-      # replication_desired.
+    test "Set replication_desired=#{ask}" do
+      Rails.configuration.default_collection_replication = 2
       authorize_with :active
       put :update, {
-        id: collections(:collection_owned_by_active).uuid,
+        id: collections(:replication_undesired_unconfirmed).uuid,
         collection: {
-          redundancy: ask,
+          replication_desired: ask,
         },
       }
       assert_response :success
diff --git a/services/api/test/unit/collection_test.rb b/services/api/test/unit/collection_test.rb
index 1386a25..b4db695 100644
--- a/services/api/test/unit/collection_test.rb
+++ b/services/api/test/unit/collection_test.rb
@@ -83,12 +83,91 @@ class CollectionTest < ActiveSupport::TestCase
   end
 
   [0, 2, 4, nil].each do |ask|
-    test "replication_desired reports #{ask or 2} if redundancy is #{ask}" do
+    test "replication_desired reports #{ask or 'default'} if redundancy is #{ask}" do
+      Rails.configuration.default_collection_replication = 2
       act_as_user users(:active) do
-        c = collections(:collection_owned_by_active)
-        c.update_attributes redundancy: ask
+        c = collections(:replication_undesired_unconfirmed)
+        c.update_attributes replication_desired: ask
         assert_equal (ask or 2), c.replication_desired
       end
     end
   end
+
+  test "replication_confirmed* can be set by admin user" do
+    c = collections(:replication_desired_2_unconfirmed)
+    act_as_user users(:admin) do
+      assert c.update_attributes(replication_confirmed: 2,
+                                 replication_confirmed_at: Time.now)
+    end
+  end
+
+  test "replication_confirmed* cannot be set by non-admin user" do
+    act_as_user users(:active) do
+      c = collections(:replication_desired_2_unconfirmed)
+      # Cannot set just one at a time.
+      assert_raise ArvadosModel::PermissionDeniedError do
+        c.update_attributes replication_confirmed: 1
+      end
+      assert_raise ArvadosModel::PermissionDeniedError do
+        c.update_attributes replication_confirmed_at: Time.now
+      end
+      # Cannot set both at once.
+      assert_raise ArvadosModel::PermissionDeniedError do
+        c.update_attributes(replication_confirmed: 1,
+                            replication_confirmed_at: Time.now)
+      end
+    end
+  end
+
+  test "replication_confirmed* can be cleared (but only together) by non-admin user" do
+    act_as_user users(:active) do
+      c = collections(:replication_desired_2_confirmed_2)
+      # Cannot clear just one at a time.
+      assert_raise ArvadosModel::PermissionDeniedError do
+        c.update_attributes replication_confirmed: nil
+      end
+      c.reload
+      assert_raise ArvadosModel::PermissionDeniedError do
+        c.update_attributes replication_confirmed_at: nil
+      end
+      # Can clear both at once.
+      c.reload
+      assert c.update_attributes(replication_confirmed: nil,
+                                 replication_confirmed_at: nil)
+    end
+  end
+
+  test "clear replication_confirmed* when introducing a new block in manifest" do
+    c = collections(:replication_desired_2_confirmed_2)
+    act_as_user users(:active) do
+      assert c.update_attributes(manifest_text: collections(:user_agreement).signed_manifest_text)
+      assert_nil c.replication_confirmed
+      assert_nil c.replication_confirmed_at
+    end
+  end
+
+  test "don't clear replication_confirmed* when just renaming a file" do
+    c = collections(:replication_desired_2_confirmed_2)
+    act_as_user users(:active) do
+      new_manifest = c.signed_manifest_text.sub(':bar', ':foo')
+      assert c.update_attributes(manifest_text: new_manifest)
+      assert_equal 2, c.replication_confirmed
+      assert_not_nil c.replication_confirmed_at
+    end
+  end
+
+  test "don't clear replication_confirmed* when just deleting a data block" do
+    c = collections(:replication_desired_2_confirmed_2)
+    act_as_user users(:active) do
+      new_manifest = c.signed_manifest_text
+      new_manifest.sub!(/ \S+:bar/, '')
+      new_manifest.sub!(/ acbd\S+/, '')
+      # We really deleted a block there, right?
+      assert_operator new_manifest.length+40, :<, c.signed_manifest_text.length
+
+      assert c.update_attributes(manifest_text: new_manifest)
+      assert_equal 2, c.replication_confirmed
+      assert_not_nil c.replication_confirmed_at
+    end
+  end
 end

commit 1361ff3051cef16a444daa89f78c358b8b677073
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri Feb 6 18:51:12 2015 -0500

    3410: Add replication attributes (and rules about updating them) to model and docs.

diff --git a/doc/api/schema/Collection.html.textile.liquid b/doc/api/schema/Collection.html.textile.liquid
index 69a8dc3..30b01e4 100644
--- a/doc/api/schema/Collection.html.textile.liquid
+++ b/doc/api/schema/Collection.html.textile.liquid
@@ -30,11 +30,10 @@ Each collection has, in addition to the usual "attributes of Arvados resources":
 
 table(table table-bordered table-condensed).
 |_. Attribute|_. Type|_. Description|_. Example|
-|locator|string|||
-|portable_data_hash|string|||
 |name|string|||
-|redundancy|number|||
-|redundancy_confirmed_by_client_uuid|string|API client||
-|redundancy_confirmed_at|datetime|||
-|redundancy_confirmed_as|number|||
+|description|text|||
+|portable_data_hash|string|||
 |manifest_text|text|||
+|replication_desired|number|Minimum storage replication level desired for each data block referenced by this collection. If a client sets this attribute to null, the site default (typically 2) will be given in API responses.|@2@|
+|replication_confirmed|number|Replication level most recently confirmed by the storage system. This field is null when a collection is first created, and is reset to null when the manifest_text changes in a way that introduces a new data block.|@2@, null|
+|replication_confirmed_at|datetime|When replication_confirmed was confirmed. If replication_confirmed is null, this field is also null.||
diff --git a/services/api/app/models/collection.rb b/services/api/app/models/collection.rb
index e210888..166292a 100644
--- a/services/api/app/models/collection.rb
+++ b/services/api/app/models/collection.rb
@@ -9,6 +9,7 @@ class Collection < ArvadosModel
   before_validation :check_signatures
   before_validation :strip_manifest_text
   before_validation :set_portable_data_hash
+  before_validation :maybe_clear_redundancy_confirmed
   validate :ensure_hash_matches_manifest_text
   before_save :set_file_names
 
@@ -22,6 +23,8 @@ class Collection < ArvadosModel
     t.add :portable_data_hash
     t.add :signed_manifest_text, as: :manifest_text
     t.add :replication_desired
+    t.add :replication_confirmed
+    t.add :replication_confirmed_at
   end
 
   def self.attributes_required_columns
@@ -180,24 +183,7 @@ class Collection < ArvadosModel
   end
 
   def replication_desired
-    # Shim until database columns get fixed up in #3410.
-    redundancy or 2
-  end
-
-  def redundancy_status
-    if redundancy_confirmed_as.nil?
-      'unconfirmed'
-    elsif redundancy_confirmed_as < redundancy
-      'degraded'
-    else
-      if redundancy_confirmed_at.nil?
-        'unconfirmed'
-      elsif Time.now - redundancy_confirmed_at < 7.days
-        'OK'
-      else
-        'stale'
-      end
-    end
+    super or Rails.configuration.default_collection_replication
   end
 
   def signed_manifest_text
@@ -232,6 +218,15 @@ class Collection < ArvadosModel
     end
   end
 
+  def self.each_manifest_locator manifest
+    # Given a manifest text and a block, yield each locator.
+    manifest.andand.scan(/ ([[:xdigit:]]{32}(\+[[:digit:]]+)?(\+\S+))/) do |word|
+      if loc = Keep::Locator.parse(word[1])
+        yield loc
+      end
+    end
+  end
+
   def self.normalize_uuid uuid
     hash_part = nil
     size_part = nil
@@ -324,4 +319,32 @@ class Collection < ArvadosModel
      '+' +
      portable_manifest.bytesize.to_s)
   end
+
+  def maybe_clear_redundancy_confirmed
+    if manifest_text_changed?
+      # If the new manifest_text contains locators whose hashes
+      # weren't in the old manifest_text, storage replication is no
+      # longer confirmed.
+      in_old_manifest = {}
+      self.class.each_manifest_locator(manifest_text_was) do |loc|
+        in_old_manifest[loc.hash] = true
+      end
+      self.class.each_manifest_locator(manifest_text) do |loc|
+        if not in_old_manifest[loc.hash]
+          replication_confirmed_at = nil
+          replication_confirmed = nil
+          break
+        end
+      end
+    end
+  end
+
+  def ensure_permission_to_save
+    if (not current_user.andand.is_admin and
+        (replication_confirmed_at_changed? or replication_confirmed_changed?) and
+        not (replication_confirmed_at.nil? and replication_confirmed.nil?))
+      raise ArvadosModel::PermissionDeniedError.new("replication_confirmed and replication_confirmed_at attributes cannot be changed, except by setting both to nil")
+    end
+    super
+  end
 end

commit fc0bdda63ea9596e1cd2cf28b7bf7cbdc3f68375
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri Feb 6 18:49:59 2015 -0500

    3410: Add default_collection_replication to config and discovery doc.

diff --git a/services/api/app/controllers/arvados/v1/schema_controller.rb b/services/api/app/controllers/arvados/v1/schema_controller.rb
index bc5a20f..9e694fb 100644
--- a/services/api/app/controllers/arvados/v1/schema_controller.rb
+++ b/services/api/app/controllers/arvados/v1/schema_controller.rb
@@ -20,6 +20,7 @@ class Arvados::V1::SchemaController < ApplicationController
         title: "Arvados API",
         description: "The API to interact with Arvados.",
         documentationLink: "http://doc.arvados.org/api/index.html",
+        defaultCollectionReplication: Rails.configuration.default_collection_replication,
         protocol: "rest",
         baseUrl: root_url + "arvados/v1/",
         basePath: "/arvados/v1/",
diff --git a/services/api/config/application.default.yml b/services/api/config/application.default.yml
index 2d62e40..f40508e 100644
--- a/services/api/config/application.default.yml
+++ b/services/api/config/application.default.yml
@@ -244,4 +244,8 @@ common:
   # Permit insecure (OpenSSL::SSL::VERIFY_NONE) connections to the Single Sign
   # On (sso) server.  Should only be enabled during development when the SSO
   # server is using a self-signed cert.
-  sso_insecure: false
\ No newline at end of file
+  sso_insecure: false
+
+  # Set replication level for collections whose replication_desired
+  # attribute is nil.
+  default_collection_replication: 2

commit 6f7820b3d58e321d6ed6a0daeb9ed6116123d42d
Author: Tom Clegg <tom at curoverse.com>
Date:   Fri Feb 6 18:49:33 2015 -0500

    3410: Rename redundancy -> replication columns.

diff --git a/services/api/app/models/collection.rb b/services/api/app/models/collection.rb
index 76d5dc6..e210888 100644
--- a/services/api/app/models/collection.rb
+++ b/services/api/app/models/collection.rb
@@ -32,10 +32,6 @@ class Collection < ArvadosModel
                 # API response, and never let clients select the
                 # manifest_text column.
                 'manifest_text' => ['manifest_text'],
-
-                # This is a shim until the database column gets
-                # renamed to replication_desired in #3410.
-                'replication_desired' => ['redundancy'],
                 )
   end
 
diff --git a/services/api/db/migrate/20150206230342_rename_replication_attributes.rb b/services/api/db/migrate/20150206230342_rename_replication_attributes.rb
new file mode 100644
index 0000000..f85a71d
--- /dev/null
+++ b/services/api/db/migrate/20150206230342_rename_replication_attributes.rb
@@ -0,0 +1,24 @@
+class RenameReplicationAttributes < ActiveRecord::Migration
+  RENAME = [[:redundancy, :replication_desired],
+            [:redundancy_confirmed_as, :replication_confirmed],
+            [:redundancy_confirmed_at, :replication_confirmed_at]]
+
+  def up
+    RENAME.each do |oldname, newname|
+      rename_column :collections, oldname, newname
+    end
+    remove_column :collections, :redundancy_confirmed_by_client_uuid
+
+    # Removing that column dropped the index. Let's put it back.
+    add_index :collections, ["owner_uuid", "modified_by_client_uuid", "modified_by_user_uuid", "portable_data_hash", "uuid", "name", "file_names"], name: 'collections_search_index'
+  end
+
+  def down
+    remove_index :collections, name: 'collections_search_index'
+    add_column :collections, :redundancy_confirmed_by_client_uuid, :string
+    add_index :collections, ["owner_uuid", "modified_by_client_uuid", "modified_by_user_uuid", "portable_data_hash", "redundancy_confirmed_by_client_uuid", "uuid", "name", "file_names"], name: 'collections_search_index'
+    RENAME.reverse.each do |oldname, newname|
+      rename_column :collections, newname, oldname
+    end
+  end
+end
diff --git a/services/api/db/structure.sql b/services/api/db/structure.sql
index 5d9e3e5..8c12c67 100644
--- a/services/api/db/structure.sql
+++ b/services/api/db/structure.sql
@@ -159,10 +159,9 @@ CREATE TABLE collections (
     modified_by_user_uuid character varying(255),
     modified_at timestamp without time zone,
     portable_data_hash character varying(255),
-    redundancy integer,
-    redundancy_confirmed_by_client_uuid character varying(255),
-    redundancy_confirmed_at timestamp without time zone,
-    redundancy_confirmed_as integer,
+    replication_desired integer,
+    replication_confirmed_at timestamp without time zone,
+    replication_confirmed integer,
     updated_at timestamp without time zone NOT NULL,
     uuid character varying(255),
     manifest_text text,
@@ -1311,7 +1310,7 @@ CREATE UNIQUE INDEX collection_owner_uuid_name_unique ON collections USING btree
 -- Name: collections_search_index; Type: INDEX; Schema: public; Owner: -; Tablespace: 
 --
 
-CREATE INDEX collections_search_index ON collections USING btree (owner_uuid, modified_by_client_uuid, modified_by_user_uuid, portable_data_hash, redundancy_confirmed_by_client_uuid, uuid, name, file_names);
+CREATE INDEX collections_search_index ON collections USING btree (owner_uuid, modified_by_client_uuid, modified_by_user_uuid, portable_data_hash, uuid, name, file_names);
 
 
 --
@@ -2318,4 +2317,6 @@ INSERT INTO schema_migrations (version) VALUES ('20141208185217');
 
 INSERT INTO schema_migrations (version) VALUES ('20150122175935');
 
-INSERT INTO schema_migrations (version) VALUES ('20150203180223');
\ No newline at end of file
+INSERT INTO schema_migrations (version) VALUES ('20150203180223');
+
+INSERT INTO schema_migrations (version) VALUES ('20150206230342');
\ No newline at end of file

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list