[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