storj/cmd/uplinkng/ulfs/mixed.go
Clement Sam 16a334020f cmd/uplinkng: add ranged download
This change adds the ability to download byte ranges
to uplinkng.

Extended the uplinkng Filesystem interface with Stat
method and an OpenOptions struct as parameter for the
Open method.

Also added a few tests for the ranged download

Change-Id: I89a7276a75c51a4b22d7a450f15b3eb18ba838d4
2021-11-03 19:51:25 +00:00

92 lines
2.9 KiB
Go

// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package ulfs
import (
"context"
"github.com/zeebo/clingy"
"github.com/zeebo/errs"
"storj.io/storj/cmd/uplinkng/ulloc"
)
// Mixed dispatches to either the local or remote filesystem depending on the location.
type Mixed struct {
local *Local
remote *Remote
}
// NewMixed returns a Mixed backed by the provided local and remote filesystems.
func NewMixed(local *Local, remote *Remote) *Mixed {
return &Mixed{
local: local,
remote: remote,
}
}
// Close releases any resources that the Mixed contails.
func (m *Mixed) Close() error {
return m.remote.Close()
}
// Open returns a ReadHandle to either a local file, remote object, or stdin.
func (m *Mixed) Open(ctx clingy.Context, loc ulloc.Location, opts *OpenOptions) (ReadHandle, error) {
if bucket, key, ok := loc.RemoteParts(); ok {
return m.remote.Open(ctx, bucket, key, opts)
} else if path, ok := loc.LocalParts(); ok {
return m.local.Open(ctx, path, opts)
}
return newGenericReadHandle(ctx.Stdin()), nil
}
// Create returns a WriteHandle to either a local file, remote object, or stdout.
func (m *Mixed) Create(ctx clingy.Context, loc ulloc.Location) (WriteHandle, error) {
if bucket, key, ok := loc.RemoteParts(); ok {
return m.remote.Create(ctx, bucket, key)
} else if path, ok := loc.LocalParts(); ok {
return m.local.Create(ctx, path)
}
return newGenericWriteHandle(ctx.Stdout()), nil
}
// Remove deletes either a local file or remote object.
func (m *Mixed) Remove(ctx context.Context, loc ulloc.Location, opts *RemoveOptions) error {
if bucket, key, ok := loc.RemoteParts(); ok {
return m.remote.Remove(ctx, bucket, key, opts)
} else if path, ok := loc.LocalParts(); ok {
return m.local.Remove(ctx, path, opts)
}
return nil
}
// List lists either files and directories with some local path prefix or remote objects
// with a given bucket and key.
func (m *Mixed) List(ctx context.Context, prefix ulloc.Location, opts *ListOptions) (ObjectIterator, error) {
if bucket, key, ok := prefix.RemoteParts(); ok {
return m.remote.List(ctx, bucket, key, opts), nil
} else if path, ok := prefix.LocalParts(); ok {
return m.local.List(ctx, path, opts)
}
return nil, errs.New("unable to list objects for prefix %q", prefix)
}
// IsLocalDir returns true if the location is a directory that is local.
func (m *Mixed) IsLocalDir(ctx context.Context, loc ulloc.Location) bool {
if path, ok := loc.LocalParts(); ok {
return m.local.IsLocalDir(ctx, path)
}
return false
}
// Stat returns information about an object at the specified Location.
func (m *Mixed) Stat(ctx context.Context, loc ulloc.Location) (*ObjectInfo, error) {
if bucket, key, ok := loc.RemoteParts(); ok {
return m.remote.Stat(ctx, bucket, key)
} else if path, ok := loc.LocalParts(); ok {
return m.local.Stat(ctx, path)
}
return nil, errs.New("unable to stat loc %q", loc.Loc())
}