2021-04-06 20:19:11 +01:00
|
|
|
// Copyright (C) 2021 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2021-05-06 17:56:57 +01:00
|
|
|
package ulfs
|
2021-04-06 20:19:11 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
|
|
|
"storj.io/uplink"
|
|
|
|
)
|
|
|
|
|
2021-05-06 17:56:57 +01:00
|
|
|
// Remote implements something close to a filesystem but backed by an uplink project.
|
|
|
|
type Remote struct {
|
2021-04-06 20:19:11 +01:00
|
|
|
project *uplink.Project
|
|
|
|
}
|
|
|
|
|
2021-05-06 17:56:57 +01:00
|
|
|
// NewRemote returns something close to a filesystem and returns objects using the project.
|
|
|
|
func NewRemote(project *uplink.Project) *Remote {
|
|
|
|
return &Remote{
|
|
|
|
project: project,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close releases any resources that the Remote contains.
|
|
|
|
func (r *Remote) Close() error {
|
2021-04-06 20:19:11 +01:00
|
|
|
return r.project.Close()
|
|
|
|
}
|
|
|
|
|
2021-05-06 17:56:57 +01:00
|
|
|
// Open returns a ReadHandle for the object identified by a given bucket and key.
|
|
|
|
func (r *Remote) Open(ctx context.Context, bucket, key string) (ReadHandle, error) {
|
2021-04-06 20:19:11 +01:00
|
|
|
fh, err := r.project.DownloadObject(ctx, bucket, key, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
return newUplinkReadHandle(bucket, fh), nil
|
|
|
|
}
|
|
|
|
|
2021-05-06 17:56:57 +01:00
|
|
|
// Create returns a WriteHandle for the object identified by a given bucket and key.
|
|
|
|
func (r *Remote) Create(ctx context.Context, bucket, key string) (WriteHandle, error) {
|
2021-04-06 20:19:11 +01:00
|
|
|
fh, err := r.project.UploadObject(ctx, bucket, key, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newUplinkWriteHandle(fh), nil
|
|
|
|
}
|
|
|
|
|
2021-05-14 20:20:21 +01:00
|
|
|
// Remove deletes the object at the provided key and bucket.
|
|
|
|
func (r *Remote) Remove(ctx context.Context, bucket, key string) error {
|
|
|
|
_, err := r.project.DeleteObject(ctx, bucket, key)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-05-06 17:56:57 +01:00
|
|
|
// ListObjects lists all of the objects in some bucket that begin with the given prefix.
|
|
|
|
func (r *Remote) ListObjects(ctx context.Context, bucket, prefix string, recursive bool) ObjectIterator {
|
2021-04-06 20:19:11 +01:00
|
|
|
parentPrefix := ""
|
|
|
|
if idx := strings.LastIndexByte(prefix, '/'); idx >= 0 {
|
|
|
|
parentPrefix = prefix[:idx+1]
|
|
|
|
}
|
|
|
|
|
|
|
|
trim := parentPrefix
|
|
|
|
if recursive {
|
|
|
|
trim = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return &filteredObjectIterator{
|
|
|
|
trim: trim,
|
|
|
|
filter: prefix,
|
|
|
|
iter: newUplinkObjectIterator(bucket, r.project.ListObjects(ctx, bucket,
|
|
|
|
&uplink.ListObjectsOptions{
|
|
|
|
Prefix: parentPrefix,
|
|
|
|
Recursive: recursive,
|
|
|
|
System: true,
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-06 17:56:57 +01:00
|
|
|
// ListUploads lists all of the pending uploads in some bucket that begin with the given prefix.
|
|
|
|
func (r *Remote) ListUploads(ctx context.Context, bucket, prefix string, recursive bool) ObjectIterator {
|
2021-04-06 20:19:11 +01:00
|
|
|
parentPrefix := ""
|
|
|
|
if idx := strings.LastIndexByte(prefix, '/'); idx >= 0 {
|
|
|
|
parentPrefix = prefix[:idx+1]
|
|
|
|
}
|
|
|
|
|
|
|
|
trim := parentPrefix
|
|
|
|
if recursive {
|
|
|
|
trim = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return &filteredObjectIterator{
|
|
|
|
trim: trim,
|
|
|
|
filter: prefix,
|
|
|
|
iter: newUplinkUploadIterator(bucket, r.project.ListUploads(ctx, bucket,
|
|
|
|
&uplink.ListUploadsOptions{
|
|
|
|
Prefix: parentPrefix,
|
|
|
|
Recursive: recursive,
|
|
|
|
System: true,
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// uplinkObjectIterator implements objectIterator for *uplink.ObjectIterator.
|
|
|
|
type uplinkObjectIterator struct {
|
|
|
|
bucket string
|
|
|
|
iter *uplink.ObjectIterator
|
|
|
|
}
|
|
|
|
|
|
|
|
// newUplinkObjectIterator constructs an *uplinkObjectIterator from an *uplink.ObjectIterator.
|
|
|
|
func newUplinkObjectIterator(bucket string, iter *uplink.ObjectIterator) *uplinkObjectIterator {
|
|
|
|
return &uplinkObjectIterator{
|
|
|
|
bucket: bucket,
|
|
|
|
iter: iter,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *uplinkObjectIterator) Next() bool { return u.iter.Next() }
|
|
|
|
func (u *uplinkObjectIterator) Err() error { return u.iter.Err() }
|
2021-05-06 17:56:57 +01:00
|
|
|
func (u *uplinkObjectIterator) Item() ObjectInfo {
|
2021-04-06 20:19:11 +01:00
|
|
|
return uplinkObjectToObjectInfo(u.bucket, u.iter.Item())
|
|
|
|
}
|
|
|
|
|
|
|
|
// uplinkUploadIterator implements objectIterator for *multipart.UploadIterators.
|
|
|
|
type uplinkUploadIterator struct {
|
|
|
|
bucket string
|
|
|
|
iter *uplink.UploadIterator
|
|
|
|
}
|
|
|
|
|
|
|
|
// newUplinkUploadIterator constructs a *uplinkUploadIterator from a *uplink.UploadIterator.
|
|
|
|
func newUplinkUploadIterator(bucket string, iter *uplink.UploadIterator) *uplinkUploadIterator {
|
|
|
|
return &uplinkUploadIterator{
|
|
|
|
bucket: bucket,
|
|
|
|
iter: iter,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *uplinkUploadIterator) Next() bool { return u.iter.Next() }
|
|
|
|
func (u *uplinkUploadIterator) Err() error { return u.iter.Err() }
|
2021-05-06 17:56:57 +01:00
|
|
|
func (u *uplinkUploadIterator) Item() ObjectInfo {
|
2021-04-06 20:19:11 +01:00
|
|
|
return uplinkUploadInfoToObjectInfo(u.bucket, u.iter.Item())
|
|
|
|
}
|