89ccfe2dd7
recursive copy had a bug with relative local paths. this fixes that bug and changes the test framework to use more of the code that actually runs in uplink and only mocks out the direct interaction with the operating system. Change-Id: I9da2a80bfda8f86a8d05879b87171f299f759c7e
174 lines
5.5 KiB
Go
174 lines
5.5 KiB
Go
// Copyright (C) 2021 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package ulfs
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/zeebo/clingy"
|
|
|
|
"storj.io/storj/cmd/uplink/ulloc"
|
|
"storj.io/uplink"
|
|
)
|
|
|
|
// CreateOptions contains extra options to create an object.
|
|
type CreateOptions struct {
|
|
Expires time.Time
|
|
}
|
|
|
|
// ListOptions describes options to the List command.
|
|
type ListOptions struct {
|
|
Recursive bool
|
|
Pending bool
|
|
Expanded bool
|
|
}
|
|
|
|
func (lo *ListOptions) isRecursive() bool { return lo != nil && lo.Recursive }
|
|
func (lo *ListOptions) isPending() bool { return lo != nil && lo.Pending }
|
|
|
|
// RemoveOptions describes options to the Remove command.
|
|
type RemoveOptions struct {
|
|
Pending bool
|
|
}
|
|
|
|
func (ro *RemoveOptions) isPending() bool { return ro != nil && ro.Pending }
|
|
|
|
// Filesystem represents either the local filesystem or the data backed by a project.
|
|
type Filesystem interface {
|
|
Close() error
|
|
Open(ctx clingy.Context, loc ulloc.Location) (MultiReadHandle, error)
|
|
Create(ctx clingy.Context, loc ulloc.Location, opts *CreateOptions) (MultiWriteHandle, error)
|
|
Move(ctx clingy.Context, source, dest ulloc.Location) error
|
|
Copy(ctx clingy.Context, source, dest ulloc.Location) error
|
|
Remove(ctx context.Context, loc ulloc.Location, opts *RemoveOptions) error
|
|
List(ctx context.Context, prefix ulloc.Location, opts *ListOptions) (ObjectIterator, error)
|
|
IsLocalDir(ctx context.Context, loc ulloc.Location) bool
|
|
Stat(ctx context.Context, loc ulloc.Location) (*ObjectInfo, error)
|
|
}
|
|
|
|
// FilesystemLocal is the interface for a local filesystem.
|
|
type FilesystemLocal interface {
|
|
IsLocalDir(ctx context.Context, path string) bool
|
|
Open(ctx context.Context, path string) (MultiReadHandle, error)
|
|
Create(ctx context.Context, path string) (MultiWriteHandle, error)
|
|
Move(ctx context.Context, oldpath string, newpath string) error
|
|
Copy(ctx context.Context, oldpath string, newpath string) error
|
|
Remove(ctx context.Context, path string, opts *RemoveOptions) error
|
|
List(ctx context.Context, path string, opts *ListOptions) (ObjectIterator, error)
|
|
Stat(ctx context.Context, path string) (*ObjectInfo, error)
|
|
}
|
|
|
|
// FilesystemRemote is the interface for a remote filesystem.
|
|
type FilesystemRemote interface {
|
|
Close() error
|
|
Open(ctx context.Context, bucket, key string) (MultiReadHandle, error)
|
|
Create(ctx context.Context, bucket, key string, opts *CreateOptions) (MultiWriteHandle, error)
|
|
Move(ctx context.Context, oldbucket, oldkey string, newbucket, newkey string) error
|
|
Copy(ctx context.Context, oldbucket, oldkey string, newbucket, newkey string) error
|
|
Remove(ctx context.Context, bucket, key string, opts *RemoveOptions) error
|
|
List(ctx context.Context, bucket, key string, opts *ListOptions) ObjectIterator
|
|
Stat(ctx context.Context, bucket, key string) (*ObjectInfo, error)
|
|
}
|
|
|
|
//
|
|
// object info
|
|
//
|
|
|
|
// ObjectInfo is a simpler *uplink.Object that contains the minimal information the
|
|
// uplink command needs that multiple types can be converted to.
|
|
type ObjectInfo struct {
|
|
Loc ulloc.Location
|
|
IsPrefix bool
|
|
Created time.Time
|
|
ContentLength int64
|
|
Expires time.Time
|
|
Metadata uplink.CustomMetadata
|
|
}
|
|
|
|
// uplinkObjectToObjectInfo returns an objectInfo converted from an *uplink.Object.
|
|
func uplinkObjectToObjectInfo(bucket string, obj *uplink.Object) ObjectInfo {
|
|
return ObjectInfo{
|
|
Loc: ulloc.NewRemote(bucket, obj.Key),
|
|
IsPrefix: obj.IsPrefix,
|
|
Created: obj.System.Created,
|
|
ContentLength: obj.System.ContentLength,
|
|
Expires: obj.System.Expires,
|
|
Metadata: obj.Custom,
|
|
}
|
|
}
|
|
|
|
// uplinkUploadInfoToObjectInfo returns an objectInfo converted from an *uplink.Object.
|
|
func uplinkUploadInfoToObjectInfo(bucket string, upl *uplink.UploadInfo) ObjectInfo {
|
|
return ObjectInfo{
|
|
Loc: ulloc.NewRemote(bucket, upl.Key),
|
|
IsPrefix: upl.IsPrefix,
|
|
Created: upl.System.Created,
|
|
ContentLength: upl.System.ContentLength,
|
|
Expires: upl.System.Expires,
|
|
Metadata: upl.Custom,
|
|
}
|
|
}
|
|
|
|
//
|
|
// read handles
|
|
//
|
|
|
|
// MultiReadHandle allows one to read different sections of something.
|
|
// The offset parameter can be negative to signal that the offset should
|
|
// start that many bytes back from the end. Any negative value for length
|
|
// indicates to read up to the end.
|
|
//
|
|
// TODO: A negative offset requires a negative length, but there is no
|
|
// reason why that must be so.
|
|
type MultiReadHandle interface {
|
|
io.Closer
|
|
SetOffset(offset int64) error
|
|
NextPart(ctx context.Context, length int64) (ReadHandle, error)
|
|
Info(ctx context.Context) (*ObjectInfo, error)
|
|
}
|
|
|
|
// ReadHandle is something that can be read from distinct parts possibly
|
|
// in parallel.
|
|
type ReadHandle interface {
|
|
io.Closer
|
|
io.Reader
|
|
Info() ObjectInfo
|
|
}
|
|
|
|
//
|
|
// write handles
|
|
//
|
|
|
|
// MultiWriteHandle lets one create multiple sequential WriteHandles for
|
|
// different sections of something.
|
|
//
|
|
// The returned WriteHandle will error if data is attempted to be written
|
|
// past the provided length. A negative length implies an unknown amount
|
|
// of data, and future calls to NextPart will error.
|
|
type MultiWriteHandle interface {
|
|
NextPart(ctx context.Context, length int64) (WriteHandle, error)
|
|
Commit(ctx context.Context) error
|
|
Abort(ctx context.Context) error
|
|
}
|
|
|
|
// WriteHandle is anything that can be written to with commit/abort semantics.
|
|
type WriteHandle interface {
|
|
io.Writer
|
|
Commit() error
|
|
Abort() error
|
|
}
|
|
|
|
//
|
|
// object iteration
|
|
//
|
|
|
|
// ObjectIterator is an interface type for iterating over objectInfo values.
|
|
type ObjectIterator interface {
|
|
Next() bool
|
|
Err() error
|
|
Item() ObjectInfo
|
|
}
|