mirror of
https://github.com/rclone/rclone.git
synced 2026-11-07 02:02:53 -05:00
serve s3: add --directory-markers to list every directory as a marker
Clients which rely on directory markers, such as rclone's s3 backend with --s3-directory-markers, need the markers of directories holding other keys listed, which serve s3 only does while it remembers the marker was put. With --directory-markers every directory is listed as a directory marker, and isn't removed when the last key below it is deleted. See #10082
This commit is contained in:
1 parent
cb150c014d
commit
4dfbe7f320
4 files changed
+49
-4
No files matched your search
@@ -257,9 +257,12 @@ func (t *tenant) storeDirMarker(fp string) {
|
||||
t.meta.Add(fp, objectMeta{dirMarker: true})
|
||||
}
|
||||
|
||||
// hasDirMarker reports whether a directory marker was put for the
|
||||
// directory at fp.
|
||||
// hasDirMarker reports whether the directory at fp has a directory
|
||||
// marker: one was put for it, or --directory-markers is set.
|
||||
func (t *tenant) hasDirMarker(fp string) bool {
|
||||
if t.b.s.opt.DirectoryMarkers {
|
||||
return true
|
||||
}
|
||||
om, ok := t.meta.Get(fp)
|
||||
return ok && om.dirMarker
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ import (
|
||||
// newListBackend serves a temporary directory populated with the given files,
|
||||
// whose paths are relative to the serve root and so start with a bucket name.
|
||||
func newListBackend(t *testing.T, files ...string) *s3Backend {
|
||||
return newListBackendOpt(t, Opt, files...)
|
||||
}
|
||||
|
||||
// newListBackendOpt is newListBackend with the options opt.
|
||||
func newListBackendOpt(t *testing.T, opt Options, files ...string) *s3Backend {
|
||||
fstest.Initialise()
|
||||
ctx := context.Background()
|
||||
root := t.TempDir()
|
||||
@@ -36,7 +41,6 @@ func newListBackend(t *testing.T, files ...string) *s3Backend {
|
||||
f, err := fs.NewFs(ctx, root)
|
||||
require.NoError(t, err)
|
||||
|
||||
opt := Opt
|
||||
opt.HTTP.ListenAddr = []string{endpoint}
|
||||
w, err := newServer(ctx, f, &opt, &vfscommon.Opt, &proxy.Opt)
|
||||
require.NoError(t, err)
|
||||
@@ -422,3 +426,29 @@ func TestListStorageClass(t *testing.T) {
|
||||
"plain": gofakes3.StorageStandard,
|
||||
}, got)
|
||||
}
|
||||
|
||||
// TestListDirectoryMarkers checks every directory is listed as a
|
||||
// directory marker with --directory-markers.
|
||||
func TestListDirectoryMarkers(t *testing.T) {
|
||||
opt := Opt
|
||||
opt.DirectoryMarkers = true
|
||||
b := newListBackendOpt(t, opt, "bucket/implicit/file", "bucket/a/b/c")
|
||||
ctx := context.Background()
|
||||
all := func() []string {
|
||||
return listPages(t, b, listPrefix("", ""), 1000)
|
||||
}
|
||||
|
||||
want := []string{"a/", "a/b/", "a/b/c", "implicit/", "implicit/file"}
|
||||
assert.Equal(t, want, all())
|
||||
assert.Equal(t, want, listPages(t, b, listPrefix("", ""), 1))
|
||||
keys, _ := listOnce(t, b, listPrefix("a/", "/"), gofakes3.ListBucketPage{})
|
||||
assert.Equal(t, []string{"a/", "a/b/"}, keys)
|
||||
keys, _ = listOnce(t, b, listPrefix("", "b"), gofakes3.ListBucketPage{})
|
||||
assert.Equal(t, []string{"a/", "a/b", "implicit/", "implicit/file"}, keys)
|
||||
|
||||
// The directories are kept when the keys below them are deleted
|
||||
require.NoError(t, b.deleteObject(ctx, "bucket", "a/b/c"))
|
||||
assert.Equal(t, []string{"a/", "a/b/", "implicit/", "implicit/file"}, all())
|
||||
require.NoError(t, b.deleteObject(ctx, "bucket", "a/b/"))
|
||||
assert.Equal(t, []string{"a/", "implicit/", "implicit/file"}, all())
|
||||
}
|
||||
@@ -57,6 +57,10 @@ var OptionsInfo = fs.Options{{
|
||||
Name: "metadata_max_objects",
|
||||
Default: 100000,
|
||||
Help: "Maximum number of objects of a user whose metadata is kept in memory, 0 for unlimited",
|
||||
}, {
|
||||
Name: "directory_markers",
|
||||
Default: false,
|
||||
Help: "List every directory as a directory marker object",
|
||||
}, {
|
||||
Name: "multipart_expiry",
|
||||
Default: fs.Duration(24 * time.Hour),
|
||||
@@ -78,6 +82,7 @@ type Options struct {
|
||||
MultipartMaxUploads int `config:"multipart_max_uploads"`
|
||||
MetadataMaxObjects int `config:"metadata_max_objects"`
|
||||
MultipartExpiry fs.Duration `config:"multipart_expiry"`
|
||||
DirectoryMarkers bool `config:"directory_markers"`
|
||||
Auth httplib.AuthConfig
|
||||
HTTP httplib.Config
|
||||
}
|
||||
|
||||
@@ -502,6 +502,13 @@ the marker of a directory holding other keys is no longer listed.
|
||||
While a marker is remembered its directory isn't removed when the
|
||||
last key below it is deleted.
|
||||
|
||||
With `--directory-markers` every directory is listed as a directory
|
||||
marker, so the markers are listed after a restart too, as needed by
|
||||
clients which rely on them, such as rclone's s3 backend with
|
||||
`--s3-directory-markers`. As every directory has a marker, a directory
|
||||
isn't removed when the last key below it is deleted, and deleting the
|
||||
marker of a directory which holds other keys has no effect.
|
||||
|
||||
As keys are file paths, a key can't be both an object and a directory
|
||||
holding other objects, for example `a/b` and `a/b/c`. Writing an
|
||||
object which would need this fails with `400 Bad Request`
|
||||
@@ -528,7 +535,7 @@ ignored.
|
||||
| Authentication | Partial | AWS Signature Version 4, in headers, presigned URLs and browser form (`POST`) uploads. Signature Version 2 isn't supported. |
|
||||
| Anonymous access | Partial | All requests are allowed without `--auth-key` or `--auth-proxy`, and none are with them, as buckets and objects can't be made public. |
|
||||
| Metadata | Partial | `x-amz-meta-*`, the storage class and the standard headers such as `Content-Type` are kept in memory only (see [Limitations](#limitations)), except the modification time. The storage class is only recorded, so doesn't change how the object is stored. |
|
||||
| Directory markers | Partial | Keys ending in `/` are stored as directories, so can't hold data. A marker put for a directory holding other keys is only listed while it is remembered in memory (see [Object names](#object-names)). |
|
||||
| Directory markers | Partial | Keys ending in `/` are stored as directories, so can't hold data. A marker put for a directory holding other keys is only listed while it is remembered in memory, unless `--directory-markers` is set (see [Object names](#object-names)). |
|
||||
| Checksums | Partial | The `x-amz-checksum-*` of a `PutObject` (CRC32, CRC32C, CRC64NVME, SHA1 or SHA256) is checked and returned with the metadata. The checksums of multipart uploads aren't supported. |
|
||||
| `GetObjectAttributes` | Partial | Everything but the checksums of multipart uploads. |
|
||||
| Object tagging | Partial | `GetObjectTagging`, `PutObjectTagging` and `DeleteObjectTagging` work, but tags are kept in memory with the metadata. |
|
||||
|
||||
Reference in new issue
Block a user