From ed87ae51c0fa42f9523a9df4d87d452452d1fd84 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 29 Jun 2022 14:29:15 +0100 Subject: [PATCH] union: support metadata --- backend/union/entry.go | 47 +++++++++++++++++++++++++-- backend/union/union.go | 8 ++++- backend/union/upstream/upstream.go | 52 ++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/backend/union/entry.go b/backend/union/entry.go index efd2cbaef..023f3bb45 100644 --- a/backend/union/entry.go +++ b/backend/union/entry.go @@ -2,6 +2,7 @@ package union import ( "context" + "errors" "fmt" "io" "io/ioutil" @@ -34,9 +35,8 @@ type entry interface { candidates() []upstream.Entry } -// UnWrap returns the Object that this Object is wrapping or -// nil if it isn't wrapping anything -func (o *Object) UnWrap() *upstream.Object { +// UnWrapUpstream returns the upstream Object that this Object is wrapping +func (o *Object) UnWrapUpstream() *upstream.Object { return o.Object } @@ -140,6 +140,42 @@ func (o *Object) SetModTime(ctx context.Context, t time.Time) error { return errs.Err() } +// GetTier returns storage tier or class of the Object +func (o *Object) GetTier() string { + do, ok := o.Object.Object.(fs.GetTierer) + if !ok { + return "" + } + return do.GetTier() +} + +// ID returns the ID of the Object if known, or "" if not +func (o *Object) ID() string { + do, ok := o.Object.Object.(fs.IDer) + if !ok { + return "" + } + return do.ID() +} + +// MimeType returns the content type of the Object if known +func (o *Object) MimeType(ctx context.Context) (mimeType string) { + if do, ok := o.Object.Object.(fs.MimeTyper); ok { + mimeType = do.MimeType(ctx) + } + return mimeType +} + +// SetTier performs changing storage tier of the Object if +// multiple storage classes supported +func (o *Object) SetTier(tier string) error { + do, ok := o.Object.Object.(fs.SetTierer) + if !ok { + return errors.New("underlying remote does not support SetTier") + } + return do.SetTier(tier) +} + // ModTime returns the modification date of the directory // It returns the latest ModTime of all candidates func (d *Directory) ModTime(ctx context.Context) (t time.Time) { @@ -164,3 +200,8 @@ func (d *Directory) Size() (s int64) { } return s } + +// Check the interfaces are satisfied +var ( + _ fs.FullObject = (*Object)(nil) +) diff --git a/backend/union/union.go b/backend/union/union.go index 9776c55d2..e1ec53e85 100644 --- a/backend/union/union.go +++ b/backend/union/union.go @@ -30,6 +30,9 @@ func init() { Name: "union", Description: "Union merges the contents of several upstream fs", NewFs: NewFs, + MetadataInfo: &fs.MetadataInfo{ + Help: `Any metadata supported by the underlying remote is read and written.`, + }, Options: []fs.Option{{ Name: "upstreams", Help: "List of space separated upstreams.\n\nCan be 'upstreama:test/dir upstreamb:', '\"upstreama:test/space:ro dir\" upstreamb:', etc.", @@ -219,7 +222,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, fs.Debugf(src, "Can't copy - not same remote type") return nil, fs.ErrorCantCopy } - o := srcObj.UnWrap() + o := srcObj.UnWrapUpstream() su := o.UpstreamFs() if su.Features().Copy == nil { return nil, fs.ErrorCantCopy @@ -881,6 +884,9 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e BucketBased: true, SetTier: true, GetTier: true, + ReadMetadata: true, + WriteMetadata: true, + UserMetadata: true, }).Fill(ctx, f) canMove := true for _, f := range upstreams { diff --git a/backend/union/upstream/upstream.go b/backend/union/upstream/upstream.go index 1a85d783f..c6e906107 100644 --- a/backend/union/upstream/upstream.go +++ b/backend/union/upstream/upstream.go @@ -245,6 +245,53 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op return nil } +// GetTier returns storage tier or class of the Object +func (o *Object) GetTier() string { + do, ok := o.Object.(fs.GetTierer) + if !ok { + return "" + } + return do.GetTier() +} + +// ID returns the ID of the Object if known, or "" if not +func (o *Object) ID() string { + do, ok := o.Object.(fs.IDer) + if !ok { + return "" + } + return do.ID() +} + +// MimeType returns the content type of the Object if known +func (o *Object) MimeType(ctx context.Context) (mimeType string) { + if do, ok := o.Object.(fs.MimeTyper); ok { + mimeType = do.MimeType(ctx) + } + return mimeType +} + +// SetTier performs changing storage tier of the Object if +// multiple storage classes supported +func (o *Object) SetTier(tier string) error { + do, ok := o.Object.(fs.SetTierer) + if !ok { + return errors.New("underlying remote does not support SetTier") + } + return do.SetTier(tier) +} + +// Metadata returns metadata for an object +// +// It should return nil if there is no Metadata +func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) { + do, ok := o.Object.(fs.Metadataer) + if !ok { + return nil, nil + } + return do.Metadata(ctx) +} + // About gets quota information from the Fs func (f *Fs) About(ctx context.Context) (*fs.Usage, error) { if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() { @@ -363,3 +410,8 @@ func (f *Fs) updateUsageCore(lock bool) error { f.usage = usage return nil } + +// Check the interfaces are satisfied +var ( + _ fs.FullObject = (*Object)(nil) +)