[ARVADOS] created: da57a99777f022ae6f6a4122c7940d00339f5503
git at public.curoverse.com
git at public.curoverse.com
Fri May 1 03:11:37 EDT 2015
at da57a99777f022ae6f6a4122c7940d00339f5503 (commit)
commit da57a99777f022ae6f6a4122c7940d00339f5503
Author: Tom Clegg <tom at curoverse.com>
Date: Fri May 1 03:13:47 2015 -0400
5724: gofmt fixes.
diff --git a/services/keepstore/handler_test.go b/services/keepstore/handler_test.go
index 71da956..5b65cd8 100644
--- a/services/keepstore/handler_test.go
+++ b/services/keepstore/handler_test.go
@@ -228,13 +228,13 @@ func TestPutAndDeleteSkipReadonlyVolumes(t *testing.T) {
IssueRequest(
&RequestTester{
method: "PUT",
- uri: "/"+TEST_HASH,
+ uri: "/" + TEST_HASH,
request_body: TEST_BLOCK,
})
IssueRequest(
&RequestTester{
method: "DELETE",
- uri: "/"+TEST_HASH,
+ uri: "/" + TEST_HASH,
request_body: TEST_BLOCK,
api_token: data_manager_token,
})
diff --git a/services/keepstore/keepstore_test.go b/services/keepstore/keepstore_test.go
index a6e29f4..8874674 100644
--- a/services/keepstore/keepstore_test.go
+++ b/services/keepstore/keepstore_test.go
@@ -345,7 +345,7 @@ func TestDiscoverTmpfs(t *testing.T) {
t.Errorf("Discover returned %s, expected %s\n",
resultVols[i].(*UnixVolume).root, tmpdir)
}
- if expectReadonly := i % 2 == 1; expectReadonly != resultVols[i].(*UnixVolume).readonly {
+ if expectReadonly := i%2 == 1; expectReadonly != resultVols[i].(*UnixVolume).readonly {
t.Errorf("Discover added %s with readonly=%v, should be %v",
tmpdir, !expectReadonly, expectReadonly)
}
diff --git a/services/keepstore/perms_test.go b/services/keepstore/perms_test.go
index d0081cd..7367dbf 100644
--- a/services/keepstore/perms_test.go
+++ b/services/keepstore/perms_test.go
@@ -48,15 +48,15 @@ func TestVerifySignatureExtraHints(t *testing.T) {
PermissionSecret = []byte(known_key)
defer func() { PermissionSecret = nil }()
- if !VerifySignature(known_locator + "+K at xyzzy" + known_sig_hint, known_token) {
+ if !VerifySignature(known_locator+"+K at xyzzy"+known_sig_hint, known_token) {
t.Fatal("Verify cannot handle hint before permission signature")
}
- if !VerifySignature(known_locator + known_sig_hint + "+Zfoo", known_token) {
+ if !VerifySignature(known_locator+known_sig_hint+"+Zfoo", known_token) {
t.Fatal("Verify cannot handle hint after permission signature")
}
- if !VerifySignature(known_locator + "+K at xyzzy" + known_sig_hint + "+Zfoo", known_token) {
+ if !VerifySignature(known_locator+"+K at xyzzy"+known_sig_hint+"+Zfoo", known_token) {
t.Fatal("Verify cannot handle hints around permission signature")
}
}
@@ -66,11 +66,11 @@ func TestVerifySignatureWrongSize(t *testing.T) {
PermissionSecret = []byte(known_key)
defer func() { PermissionSecret = nil }()
- if !VerifySignature(known_hash + "+999999" + known_sig_hint, known_token) {
+ if !VerifySignature(known_hash+"+999999"+known_sig_hint, known_token) {
t.Fatal("Verify cannot handle incorrect size hint")
}
- if !VerifySignature(known_hash + known_sig_hint, known_token) {
+ if !VerifySignature(known_hash+known_sig_hint, known_token) {
t.Fatal("Verify cannot handle missing size hint")
}
}
diff --git a/services/keepstore/trash_worker_test.go b/services/keepstore/trash_worker_test.go
index a11756b..0511b48 100644
--- a/services/keepstore/trash_worker_test.go
+++ b/services/keepstore/trash_worker_test.go
@@ -15,8 +15,8 @@ type TrashWorkerTestData struct {
Block2 []byte
BlockMtime2 int64
- CreateData bool
- CreateInVolume1 bool
+ CreateData bool
+ CreateInVolume1 bool
UseTrashLifeTime bool
DifferentMtimes bool
@@ -202,7 +202,7 @@ func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
}
}
- oldBlockTime := time.Now().Add(-blob_signature_ttl-time.Minute)
+ oldBlockTime := time.Now().Add(-blob_signature_ttl - time.Minute)
// Create TrashRequest for the test
trashRequest := TrashRequest{
diff --git a/services/keepstore/volume.go b/services/keepstore/volume.go
index 0f9fcff..f581b28 100644
--- a/services/keepstore/volume.go
+++ b/services/keepstore/volume.go
@@ -69,7 +69,7 @@ func (vm *RRVolumeManager) NextWritable() Volume {
return nil
}
i := atomic.AddUint32(&vm.counter, 1)
- return vm.writables[i % uint32(len(vm.writables))]
+ return vm.writables[i%uint32(len(vm.writables))]
}
func (vm *RRVolumeManager) Close() {
diff --git a/services/keepstore/volume_test.go b/services/keepstore/volume_test.go
index 3d6b95f..379c890 100644
--- a/services/keepstore/volume_test.go
+++ b/services/keepstore/volume_test.go
@@ -14,15 +14,15 @@ type MockVolume struct {
Store map[string][]byte
Timestamps map[string]time.Time
// Bad volumes return an error for every operation.
- Bad bool
+ Bad bool
// Touchable volumes' Touch() method succeeds for a locator
// that has been Put().
- Touchable bool
+ Touchable bool
// Readonly volumes return an error for Put, Delete, and
// Touch.
- Readonly bool
- called map[string]int
- mutex sync.Mutex
+ Readonly bool
+ called map[string]int
+ mutex sync.Mutex
}
// CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
diff --git a/services/keepstore/volume_unix.go b/services/keepstore/volume_unix.go
index f825988..3b7c993 100644
--- a/services/keepstore/volume_unix.go
+++ b/services/keepstore/volume_unix.go
@@ -70,12 +70,12 @@ func (v *UnixVolume) IOHandler() {
func MakeUnixVolume(root string, serialize bool, readonly bool) *UnixVolume {
v := &UnixVolume{
- root: root,
- queue: nil,
+ root: root,
+ queue: nil,
readonly: readonly,
}
if serialize {
- v.queue =make(chan *IORequest)
+ v.queue = make(chan *IORequest)
go v.IOHandler()
}
return v
commit 5bf77ec1bbbd91ee2f1d063d963850c600251ca7
Author: Tom Clegg <tom at curoverse.com>
Date: Fri May 1 03:11:46 2015 -0400
5724: Rename -permission-* flags to -blob-sign* to match apiserver. Tidy up some time-related code.
diff --git a/services/keepstore/handler_test.go b/services/keepstore/handler_test.go
index 1765ddf..71da956 100644
--- a/services/keepstore/handler_test.go
+++ b/services/keepstore/handler_test.go
@@ -54,11 +54,11 @@ func TestGetHandler(t *testing.T) {
// Turn on permission settings so we can generate signed locators.
enforce_permissions = true
PermissionSecret = []byte(known_key)
- permission_ttl = time.Duration(300) * time.Second
+ blob_signature_ttl = 300 * time.Second
var (
unsigned_locator = "/" + TEST_HASH
- valid_timestamp = time.Now().Add(permission_ttl)
+ valid_timestamp = time.Now().Add(blob_signature_ttl)
expired_timestamp = time.Now().Add(-time.Hour)
signed_locator = "/" + SignLocator(TEST_HASH, known_token, valid_timestamp)
expired_locator = "/" + SignLocator(TEST_HASH, known_token, expired_timestamp)
@@ -176,7 +176,7 @@ func TestPutHandler(t *testing.T) {
// With a server key.
PermissionSecret = []byte(known_key)
- permission_ttl = time.Duration(300) * time.Second
+ blob_signature_ttl = 300 * time.Second
// When a permission key is available, the locator returned
// from an authenticated PUT request will be signed.
@@ -440,10 +440,10 @@ func TestDeleteHandler(t *testing.T) {
vols := KeepVM.AllWritable()
vols[0].Put(TEST_HASH, TEST_BLOCK)
- // Explicitly set the permission_ttl to 0 for these
+ // Explicitly set the blob_signature_ttl to 0 for these
// tests, to ensure the MockVolume deletes the blocks
// even though they have just been created.
- permission_ttl = time.Duration(0)
+ blob_signature_ttl = time.Duration(0)
var user_token = "NOT DATA MANAGER TOKEN"
data_manager_token = "DATA MANAGER TOKEN"
@@ -528,10 +528,10 @@ func TestDeleteHandler(t *testing.T) {
t.Error("superuser_existing_block_req: block not deleted")
}
- // A DELETE request on a block newer than permission_ttl should return
- // success but leave the block on the volume.
+ // A DELETE request on a block newer than blob_signature_ttl
+ // should return success but leave the block on the volume.
vols[0].Put(TEST_HASH, TEST_BLOCK)
- permission_ttl = time.Duration(1) * time.Hour
+ blob_signature_ttl = time.Hour
response = IssueRequest(superuser_existing_block_req)
ExpectStatusCode(t,
diff --git a/services/keepstore/handlers.go b/services/keepstore/handlers.go
index b64294f..d355e92 100644
--- a/services/keepstore/handlers.go
+++ b/services/keepstore/handlers.go
@@ -197,7 +197,7 @@ func PutBlockHandler(resp http.ResponseWriter, req *http.Request) {
return_hash := fmt.Sprintf("%s+%d", hash, len(buf))
api_token := GetApiToken(req)
if PermissionSecret != nil && api_token != "" {
- expiry := time.Now().Add(permission_ttl)
+ expiry := time.Now().Add(blob_signature_ttl)
return_hash = SignLocator(return_hash, api_token, expiry)
}
resp.Write([]byte(return_hash + "\n"))
diff --git a/services/keepstore/keepstore.go b/services/keepstore/keepstore.go
index 401d66f..c6cb00d 100644
--- a/services/keepstore/keepstore.go
+++ b/services/keepstore/keepstore.go
@@ -39,17 +39,17 @@ var PROC_MOUNTS = "/proc/mounts"
// enforce_permissions controls whether permission signatures
// should be enforced (affecting GET and DELETE requests).
-// Initialized by the --enforce-permissions flag.
+// Initialized by the -enforce-permissions flag.
var enforce_permissions bool
-// permission_ttl is the time duration for which new permission
+// blob_signature_ttl is the time duration for which new permission
// signatures (returned by PUT requests) will be valid.
-// Initialized by the --permission-ttl flag.
-var permission_ttl time.Duration
+// Initialized by the -permission-ttl flag.
+var blob_signature_ttl time.Duration
// data_manager_token represents the API token used by the
// Data Manager, and is required on certain privileged operations.
-// Initialized by the --data-manager-token-file flag.
+// Initialized by the -data-manager-token-file flag.
var data_manager_token string
// never_delete can be used to prevent the DELETE handler from
@@ -200,7 +200,7 @@ func main() {
var (
data_manager_token_file string
listen string
- permission_key_file string
+ blob_signing_key_file string
permission_ttl_sec int
volumes volumeSet
pidfile string
@@ -228,17 +228,27 @@ func main() {
"If set, nothing will be deleted. HTTP 405 will be returned "+
"for valid DELETE requests.")
flag.StringVar(
- &permission_key_file,
+ &blob_signing_key_file,
"permission-key-file",
"",
+ "Synonym for -blob-signing-key-file.")
+ flag.StringVar(
+ &blob_signing_key_file,
+ "blob-signing-key-file",
+ "",
"File containing the secret key for generating and verifying "+
- "permission signatures.")
+ "blob permission signatures.")
flag.IntVar(
&permission_ttl_sec,
"permission-ttl",
- 1209600,
- "Expiration time (in seconds) for newly generated permission "+
- "signatures.")
+ 0,
+ "Synonym for -blob-signature-ttl.")
+ flag.IntVar(
+ &permission_ttl_sec,
+ "blob-signature-ttl",
+ int(time.Duration(2*7*24*time.Hour).Seconds()),
+ "Lifetime of blob permission signatures. "+
+ "See services/api/config/application.default.yml.")
flag.BoolVar(
&flagSerializeIO,
"serialize",
@@ -285,28 +295,25 @@ func main() {
log.Fatalf("reading data manager token: %s\n", err)
}
}
- if permission_key_file != "" {
- if buf, err := ioutil.ReadFile(permission_key_file); err == nil {
+ if blob_signing_key_file != "" {
+ if buf, err := ioutil.ReadFile(blob_signing_key_file); err == nil {
PermissionSecret = bytes.TrimSpace(buf)
} else {
log.Fatalf("reading permission key: %s\n", err)
}
}
- // Initialize permission TTL
- permission_ttl = time.Duration(permission_ttl_sec) * time.Second
+ blob_signature_ttl = time.Duration(permission_ttl_sec) * time.Second
- // If --enforce-permissions is true, we must have a permission key
- // to continue.
if PermissionSecret == nil {
if enforce_permissions {
- log.Fatal("--enforce-permissions requires a permission key")
+ log.Fatal("-enforce-permissions requires a permission key")
} else {
log.Println("Running without a PermissionSecret. Block locators " +
"returned by this server will not be signed, and will be rejected " +
"by a server that enforces permissions.")
- log.Println("To fix this, run Keep with --permission-key-file=<path> " +
- "to define the location of a file containing the permission key.")
+ log.Println("To fix this, use the -permission-key-file flag " +
+ "to specify the file containing the permission key.")
}
}
diff --git a/services/keepstore/trash_worker.go b/services/keepstore/trash_worker.go
index ca26912..bc1775f 100644
--- a/services/keepstore/trash_worker.go
+++ b/services/keepstore/trash_worker.go
@@ -14,30 +14,34 @@ import (
*/
func RunTrashWorker(trashq *WorkQueue) {
- nextItem := trashq.NextItem
- for item := range nextItem {
+ for item := range trashq.NextItem {
trashRequest := item.(TrashRequest)
- err := TrashItem(trashRequest)
- if err != nil {
- log.Printf("Trash request error for %s: %s", trashRequest, err)
- }
+ TrashItem(trashRequest)
}
}
-/*
- Delete the block indicated by the Locator in TrashRequest.
-*/
-func TrashItem(trashRequest TrashRequest) (err error) {
- // Verify if the block is to be deleted based on its Mtime
+// TrashItem deletes the indicated block from every writable volume.
+func TrashItem(trashRequest TrashRequest) {
+ reqMtime := time.Unix(trashRequest.BlockMtime, 0)
+ if time.Since(reqMtime) < blob_signature_ttl {
+ log.Printf("WARNING: data manager asked to delete a %v old block %v (BlockMtime %d = %v), but my blob_signature_ttl is %v! Skipping.",
+ time.Since(reqMtime),
+ trashRequest.Locator,
+ trashRequest.BlockMtime,
+ reqMtime,
+ blob_signature_ttl)
+ return
+ }
for _, volume := range KeepVM.AllWritable() {
mtime, err := volume.Mtime(trashRequest.Locator)
if err != nil || trashRequest.BlockMtime != mtime.Unix() {
continue
}
- currentTime := time.Now().Unix()
- if time.Duration(currentTime-trashRequest.BlockMtime)*time.Second >= permission_ttl {
- err = volume.Delete(trashRequest.Locator)
+ err = volume.Delete(trashRequest.Locator)
+ if err != nil {
+ log.Printf("%v Delete(%v): %v", volume, trashRequest.Locator, err)
+ continue
}
+ log.Printf("%v Delete(%v) OK", volume, trashRequest.Locator)
}
- return
}
diff --git a/services/keepstore/trash_worker_test.go b/services/keepstore/trash_worker_test.go
index af4040a..a11756b 100644
--- a/services/keepstore/trash_worker_test.go
+++ b/services/keepstore/trash_worker_test.go
@@ -17,9 +17,9 @@ type TrashWorkerTestData struct {
CreateData bool
CreateInVolume1 bool
- UseDelayToCreate bool
UseTrashLifeTime bool
+ DifferentMtimes bool
DeleteLocator string
@@ -122,8 +122,8 @@ func TestTrashWorkerIntegration_MtimeMatchesForLocator1ButNotForLocator2(t *test
Locator2: TEST_HASH,
Block2: TEST_BLOCK,
- CreateData: true,
- UseDelayToCreate: true,
+ CreateData: true,
+ DifferentMtimes: true,
DeleteLocator: TEST_HASH,
@@ -183,8 +183,6 @@ func TestTrashWorkerIntegration_SameLocatorInTwoVolumesWithDefaultTrashLifeTime(
/* Perform the test */
func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
- actual_permission_ttl := permission_ttl
-
// Create Keep Volumes
KeepVM = MakeTestVolumeManager(2)
defer KeepVM.Close()
@@ -195,11 +193,6 @@ func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
vols[0].Put(testData.Locator1, testData.Block1)
vols[0].Put(testData.Locator1+".meta", []byte("metadata"))
- // One of the tests deletes a locator with different Mtimes in two different volumes
- if testData.UseDelayToCreate {
- time.Sleep(1 * time.Second)
- }
-
if testData.CreateInVolume1 {
vols[0].Put(testData.Locator2, testData.Block2)
vols[0].Put(testData.Locator2+".meta", []byte("metadata"))
@@ -209,24 +202,30 @@ func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
}
}
+ oldBlockTime := time.Now().Add(-blob_signature_ttl-time.Minute)
+
// Create TrashRequest for the test
trashRequest := TrashRequest{
Locator: testData.DeleteLocator,
- BlockMtime: time.Now().Unix(),
+ BlockMtime: oldBlockTime.Unix(),
}
- // delay by permission_ttl to allow deletes to work
- time.Sleep(1 * time.Second)
-
// Run trash worker and put the trashRequest on trashq
trashList := list.New()
trashList.PushBack(trashRequest)
trashq = NewWorkQueue()
+ defer trashq.Close()
- // Trash worker would not delete block if its Mtime is within trash life time.
- // Hence, we will have to bypass it to allow the deletion to succeed.
if !testData.UseTrashLifeTime {
- permission_ttl = time.Duration(1) * time.Second
+ // Trash worker would not delete block if its Mtime is
+ // within trash life time. Back-date the block to
+ // allow the deletion to succeed.
+ for _, v := range vols {
+ v.(*MockVolume).Timestamps[testData.DeleteLocator] = oldBlockTime
+ if testData.DifferentMtimes {
+ oldBlockTime = oldBlockTime.Add(time.Second)
+ }
+ }
}
go RunTrashWorker(trashq)
@@ -259,10 +258,10 @@ func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
}
}
- // One test used the same locator in two different volumes but with different Mtime values
- // Hence let's verify that only one volume has it and the other is deleted
- if (testData.ExpectLocator1) &&
- (testData.Locator1 == testData.Locator2) {
+ // The DifferentMtimes test puts the same locator in two
+ // different volumes, but only one copy has an Mtime matching
+ // the trash request.
+ if testData.DifferentMtimes {
locatorFoundIn := 0
for _, volume := range KeepVM.AllReadable() {
if _, err := volume.Get(testData.Locator1); err == nil {
@@ -270,11 +269,7 @@ func performTrashWorkerTest(testData TrashWorkerTestData, t *testing.T) {
}
}
if locatorFoundIn != 1 {
- t.Errorf("Expected locator to be found in only one volume after deleting. But found: %s", locatorFoundIn)
+ t.Errorf("Found %d copies of %s, expected 1", locatorFoundIn, testData.Locator1)
}
}
-
- // Done
- permission_ttl = actual_permission_ttl
- trashq.Close()
}
diff --git a/services/keepstore/volume_test.go b/services/keepstore/volume_test.go
index e93bb03..3d6b95f 100644
--- a/services/keepstore/volume_test.go
+++ b/services/keepstore/volume_test.go
@@ -125,7 +125,7 @@ func (v *MockVolume) Delete(loc string) error {
return MethodDisabledError
}
if _, ok := v.Store[loc]; ok {
- if time.Since(v.Timestamps[loc]) < permission_ttl {
+ if time.Since(v.Timestamps[loc]) < blob_signature_ttl {
return nil
}
delete(v.Store, loc)
diff --git a/services/keepstore/volume_unix.go b/services/keepstore/volume_unix.go
index 20bc9c5..f825988 100644
--- a/services/keepstore/volume_unix.go
+++ b/services/keepstore/volume_unix.go
@@ -287,15 +287,15 @@ func (v *UnixVolume) Delete(loc string) error {
}
defer unlockfile(f)
- // If the block has been PUT more recently than -permission_ttl,
- // return success without removing the block. This guards against
- // a race condition where a block is old enough that Data Manager
- // has added it to the trash list, but the user submitted a PUT
- // for the block since then.
+ // If the block has been PUT in the last blob_signature_ttl
+ // seconds, return success without removing the block. This
+ // protects data from garbage collection until it is no longer
+ // possible for clients to retrieve the unreferenced blocks
+ // anyway (because the permission signatures have expired).
if fi, err := os.Stat(p); err != nil {
return err
} else {
- if time.Since(fi.ModTime()) < permission_ttl {
+ if time.Since(fi.ModTime()) < blob_signature_ttl {
return nil
}
}
commit 6fc44a67b911cead1513e6ddb517f56dd509663b
Author: Tom Clegg <tom at curoverse.com>
Date: Fri May 1 00:59:20 2015 -0400
5724: Add blobSignatureTtl to discovery doc. Add config comment/explanation.
diff --git a/services/api/app/controllers/arvados/v1/schema_controller.rb b/services/api/app/controllers/arvados/v1/schema_controller.rb
index dcc9c63..62d5e59 100644
--- a/services/api/app/controllers/arvados/v1/schema_controller.rb
+++ b/services/api/app/controllers/arvados/v1/schema_controller.rb
@@ -35,6 +35,7 @@ class Arvados::V1::SchemaController < ApplicationController
servicePath: "arvados/v1/",
batchPath: "batch",
defaultTrashLifetime: Rails.application.config.default_trash_lifetime,
+ blobSignatureTtl: Rails.application.config.blob_signature_ttl,
maxRequestSize: Rails.application.config.max_request_size,
parameters: {
alt: {
diff --git a/services/api/app/models/collection.rb b/services/api/app/models/collection.rb
index 89ad874..ccfb35e 100644
--- a/services/api/app/models/collection.rb
+++ b/services/api/app/models/collection.rb
@@ -60,7 +60,7 @@ class Collection < ArvadosModel
signing_opts = {
key: Rails.configuration.blob_signing_key,
api_token: api_token,
- ttl: Rails.configuration.blob_signing_ttl,
+ ttl: Rails.configuration.blob_signature_ttl,
}
self.manifest_text.lines.each do |entry|
entry.split[1..-1].each do |tok|
@@ -195,7 +195,7 @@ class Collection < ArvadosModel
signing_opts = {
key: Rails.configuration.blob_signing_key,
api_token: token,
- ttl: Rails.configuration.blob_signing_ttl,
+ ttl: Rails.configuration.blob_signature_ttl,
}
m = manifest.dup
munge_manifest_locators!(m) do |loc|
diff --git a/services/api/config/application.default.yml b/services/api/config/application.default.yml
index e7dbf29..b57c016 100644
--- a/services/api/config/application.default.yml
+++ b/services/api/config/application.default.yml
@@ -220,9 +220,23 @@ common:
# a site secret. It should be at least 50 characters.
blob_signing_key: ~
- # Amount of time (in seconds) for which a blob permission signature
- # remains valid. Default: 2 weeks (1209600 seconds)
- blob_signing_ttl: 1209600
+ # Lifetime (in seconds) of blob permission signatures generated by
+ # the API server. This determines how long a client can take (after
+ # retrieving a collection record) to retrieve the collection data
+ # from Keep. If the client needs more time than that (assuming the
+ # collection still has the same content and the relevant user/token
+ # still has permission) the client can retrieve the collection again
+ # to get fresh signatures.
+ #
+ # Datamanager considers an unreferenced block older than this to be
+ # eligible for garbage collection. Therefore, it should never be
+ # smaller than the corresponding value used by any local keepstore
+ # service (see keepstore -blob-signing-ttl flag). This rule prevents
+ # datamanager from trying to garbage-collect recently written blocks
+ # while clients are still holding valid signatures.
+ #
+ # The default is 2 weeks.
+ blob_signature_ttl: 1209600
# Allow clients to create collections by providing a manifest with
# unsigned data blob locators. IMPORTANT: This effectively disables
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list