// Copyright (C) 2021 Storj Labs, Inc. // See LICENSE for copying information. package ulfs import ( "context" "strings" "github.com/zeebo/errs" "storj.io/storj/cmd/uplinkng/ulloc" "storj.io/uplink" ) // Remote implements something close to a filesystem but backed by an uplink project. type Remote struct { project *uplink.Project } // 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 { return r.project.Close() } // 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) { fh, err := r.project.DownloadObject(ctx, bucket, key, nil) if err != nil { return nil, errs.Wrap(err) } return newUplinkReadHandle(bucket, fh), nil } // 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) { fh, err := r.project.UploadObject(ctx, bucket, key, nil) if err != nil { return nil, err } return newUplinkWriteHandle(fh), nil } // 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 } // 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 { parentPrefix := "" if idx := strings.LastIndexByte(prefix, '/'); idx >= 0 { parentPrefix = prefix[:idx+1] } trim := ulloc.NewRemote(bucket, "") if !recursive { trim = ulloc.NewRemote(bucket, parentPrefix) } return &filteredObjectIterator{ trim: trim, filter: ulloc.NewRemote(bucket, prefix), iter: newUplinkObjectIterator(bucket, r.project.ListObjects(ctx, bucket, &uplink.ListObjectsOptions{ Prefix: parentPrefix, Recursive: recursive, System: true, })), } } // 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 { parentPrefix := "" if idx := strings.LastIndexByte(prefix, '/'); idx >= 0 { parentPrefix = prefix[:idx+1] } trim := ulloc.NewRemote(bucket, "") if !recursive { trim = ulloc.NewRemote(bucket, parentPrefix) } return &filteredObjectIterator{ trim: trim, filter: ulloc.NewRemote(bucket, 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() } func (u *uplinkObjectIterator) Item() ObjectInfo { 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() } func (u *uplinkUploadIterator) Item() ObjectInfo { return uplinkUploadInfoToObjectInfo(u.bucket, u.iter.Item()) }