alias: add new backend to create aliases for remote names #1049
The alias backend is a wrapper for an existing remote. It allows you to name a "remote:path" as an "alias:".
This commit is contained in:
parent
5055b340da
commit
b183bd7f00
11 changed files with 289 additions and 0 deletions
45
backend/alias/alias.go
Normal file
45
backend/alias/alias.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package alias
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ncw/rclone/fs"
|
||||
"github.com/ncw/rclone/fs/config"
|
||||
)
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fsi := &fs.RegInfo{
|
||||
Name: "alias",
|
||||
Description: "Alias for a existing remote",
|
||||
NewFs: NewFs,
|
||||
Options: []fs.Option{{
|
||||
Name: "remote",
|
||||
Help: "Remote or path to alias.\nCan be \"myremote:path/to/dir\", \"myremote:bucket\", \"myremote:\" or \"/local/path\".",
|
||||
}},
|
||||
}
|
||||
fs.Register(fsi)
|
||||
}
|
||||
|
||||
// NewFs contstructs an Fs from the path.
|
||||
//
|
||||
// The returned Fs is the actual Fs, referenced by remote in the config
|
||||
func NewFs(name, root string) (fs.Fs, error) {
|
||||
remote := config.FileGet(name, "remote")
|
||||
if remote == "" {
|
||||
return nil, errors.New("alias can't point to an empty remote - check the value of the remote setting")
|
||||
}
|
||||
if strings.HasPrefix(remote, name+":") {
|
||||
return nil, errors.New("can't point alias remote at itself - check the value of the remote setting")
|
||||
}
|
||||
fsInfo, configName, fsPath, err := fs.ParseRemote(remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root = filepath.ToSlash(root)
|
||||
return fsInfo.NewFs(configName, path.Join(fsPath, root))
|
||||
}
|
106
backend/alias/alias_internal_test.go
Normal file
106
backend/alias/alias_internal_test.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package alias
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
_ "github.com/ncw/rclone/backend/local" // pull in test backend
|
||||
"github.com/ncw/rclone/fs"
|
||||
"github.com/ncw/rclone/fs/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
remoteName = "TestAlias"
|
||||
testPath = "test"
|
||||
filesPath = filepath.Join(testPath, "files")
|
||||
)
|
||||
|
||||
func prepare(t *testing.T, root string) {
|
||||
config.LoadConfig()
|
||||
|
||||
// Configure the remote
|
||||
config.FileSet(remoteName, "type", "alias")
|
||||
config.FileSet(remoteName, "remote", root)
|
||||
}
|
||||
|
||||
func TestNewFS(t *testing.T) {
|
||||
type testEntry struct {
|
||||
remote string
|
||||
size int64
|
||||
isDir bool
|
||||
}
|
||||
for testi, test := range []struct {
|
||||
remoteRoot string
|
||||
fsRoot string
|
||||
fsList string
|
||||
wantOK bool
|
||||
entries []testEntry
|
||||
}{
|
||||
{"", "", "", true, []testEntry{
|
||||
{"four", -1, true},
|
||||
{"one%.txt", 6, false},
|
||||
{"three", -1, true},
|
||||
{"two.html", 7, false},
|
||||
}},
|
||||
{"", "four", "", true, []testEntry{
|
||||
{"five", -1, true},
|
||||
{"under four.txt", 9, false},
|
||||
}},
|
||||
{"", "", "four", true, []testEntry{
|
||||
{"four/five", -1, true},
|
||||
{"four/under four.txt", 9, false},
|
||||
}},
|
||||
{"four", "..", "", true, []testEntry{
|
||||
{"four", -1, true},
|
||||
{"one%.txt", 6, false},
|
||||
{"three", -1, true},
|
||||
{"two.html", 7, false},
|
||||
}},
|
||||
{"four", "../three", "", true, []testEntry{
|
||||
{"underthree.txt", 9, false},
|
||||
}},
|
||||
} {
|
||||
what := fmt.Sprintf("test %d remoteRoot=%q, fsRoot=%q, fsList=%q", testi, test.remoteRoot, test.fsRoot, test.fsList)
|
||||
|
||||
remoteRoot, err := filepath.Abs(filepath.FromSlash(path.Join("test/files", test.remoteRoot)))
|
||||
require.NoError(t, err, what)
|
||||
prepare(t, remoteRoot)
|
||||
f, err := fs.NewFs(fmt.Sprintf("%s:%s", remoteName, test.fsRoot))
|
||||
require.NoError(t, err, what)
|
||||
gotEntries, err := f.List(test.fsList)
|
||||
require.NoError(t, err, what)
|
||||
|
||||
sort.Sort(gotEntries)
|
||||
|
||||
require.Equal(t, len(test.entries), len(gotEntries), what)
|
||||
for i, gotEntry := range gotEntries {
|
||||
what := fmt.Sprintf("%s, entry=%d", what, i)
|
||||
wantEntry := test.entries[i]
|
||||
|
||||
require.Equal(t, wantEntry.remote, gotEntry.Remote(), what)
|
||||
require.Equal(t, wantEntry.size, int64(gotEntry.Size()), what)
|
||||
_, isDir := gotEntry.(fs.Directory)
|
||||
require.Equal(t, wantEntry.isDir, isDir, what)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFSNoRemote(t *testing.T) {
|
||||
prepare(t, "")
|
||||
f, err := fs.NewFs(fmt.Sprintf("%s:", remoteName))
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, f)
|
||||
}
|
||||
|
||||
func TestNewFSInvalidRemote(t *testing.T) {
|
||||
prepare(t, "not_existing_test_remote:")
|
||||
f, err := fs.NewFs(fmt.Sprintf("%s:", remoteName))
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, f)
|
||||
}
|
1
backend/alias/test/files/four/five/underfive.txt
Normal file
1
backend/alias/test/files/four/five/underfive.txt
Normal file
|
@ -0,0 +1 @@
|
|||
apple
|
1
backend/alias/test/files/four/under four.txt
Normal file
1
backend/alias/test/files/four/under four.txt
Normal file
|
@ -0,0 +1 @@
|
|||
beetroot
|
1
backend/alias/test/files/one%.txt
Normal file
1
backend/alias/test/files/one%.txt
Normal file
|
@ -0,0 +1 @@
|
|||
hello
|
1
backend/alias/test/files/three/underthree.txt
Normal file
1
backend/alias/test/files/three/underthree.txt
Normal file
|
@ -0,0 +1 @@
|
|||
rutabaga
|
1
backend/alias/test/files/two.html
Normal file
1
backend/alias/test/files/two.html
Normal file
|
@ -0,0 +1 @@
|
|||
potato
|
|
@ -2,6 +2,7 @@ package all
|
|||
|
||||
import (
|
||||
// Active file systems
|
||||
_ "github.com/ncw/rclone/backend/alias"
|
||||
_ "github.com/ncw/rclone/backend/amazonclouddrive"
|
||||
_ "github.com/ncw/rclone/backend/azureblob"
|
||||
_ "github.com/ncw/rclone/backend/b2"
|
||||
|
|
|
@ -21,6 +21,7 @@ docs = [
|
|||
"overview.md",
|
||||
|
||||
# Keep these alphabetical by full name
|
||||
"alias.md",
|
||||
"amazonclouddrive.md",
|
||||
"s3.md",
|
||||
"b2.md",
|
||||
|
|
130
docs/content/alias.md
Normal file
130
docs/content/alias.md
Normal file
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
title: "Alias"
|
||||
description: "Remote Aliases"
|
||||
date: "2018-01-30"
|
||||
---
|
||||
|
||||
<i class="fa fa-link"></i> Alias
|
||||
-----------------------------------------
|
||||
|
||||
The `alias` remote provides a new name for another remote.
|
||||
|
||||
Paths may be as deep as required or a local path,
|
||||
eg `remote:directory/subdirectory` or `/directory/subdirectory`.
|
||||
|
||||
During the initial setup with `rclone config` you will specify the target
|
||||
remote. The target remote can either be a local path or another remote.
|
||||
|
||||
Subfolders can be used in target remote. Asume a alias remote named `backup`
|
||||
with the target `mydrive:private/backup`. Invoking `rclone mkdir backup:desktop`
|
||||
is exactly the same as invoking `rclone mkdir mydrive:private/backup/desktop`.
|
||||
|
||||
There will be no special handling of paths containing `..` segments.
|
||||
Invoking `rclone mkdir backup:../desktop` is exactly the same as invoking
|
||||
`rclone mkdir mydrive:private/backup/../desktop`.
|
||||
The empty path is not allowed as a remote. To alias the current directory
|
||||
use `.` instead.
|
||||
|
||||
Here is an example of how to make a alias called `remote` for local folder.
|
||||
First run:
|
||||
|
||||
rclone config
|
||||
|
||||
This will guide you through an interactive setup process:
|
||||
|
||||
```
|
||||
No remotes found - make a new one
|
||||
n) New remote
|
||||
s) Set configuration password
|
||||
q) Quit config
|
||||
n/s/q> n
|
||||
name> remote
|
||||
Type of storage to configure.
|
||||
Choose a number from below, or type in your own value
|
||||
1 / Alias for a existing remote
|
||||
\ "alias"
|
||||
2 / Amazon Drive
|
||||
\ "amazon cloud drive"
|
||||
3 / Amazon S3 (also Dreamhost, Ceph, Minio)
|
||||
\ "s3"
|
||||
4 / Backblaze B2
|
||||
\ "b2"
|
||||
5 / Box
|
||||
\ "box"
|
||||
6 / Cache a remote
|
||||
\ "cache"
|
||||
7 / Dropbox
|
||||
\ "dropbox"
|
||||
8 / Encrypt/Decrypt a remote
|
||||
\ "crypt"
|
||||
9 / FTP Connection
|
||||
\ "ftp"
|
||||
10 / Google Cloud Storage (this is not Google Drive)
|
||||
\ "google cloud storage"
|
||||
11 / Google Drive
|
||||
\ "drive"
|
||||
12 / Hubic
|
||||
\ "hubic"
|
||||
13 / Local Disk
|
||||
\ "local"
|
||||
14 / Microsoft Azure Blob Storage
|
||||
\ "azureblob"
|
||||
15 / Microsoft OneDrive
|
||||
\ "onedrive"
|
||||
16 / Openstack Swift (Rackspace Cloud Files, Memset Memstore, OVH)
|
||||
\ "swift"
|
||||
17 / Pcloud
|
||||
\ "pcloud"
|
||||
18 / QingCloud Object Storage
|
||||
\ "qingstor"
|
||||
19 / SSH/SFTP Connection
|
||||
\ "sftp"
|
||||
20 / Webdav
|
||||
\ "webdav"
|
||||
21 / Yandex Disk
|
||||
\ "yandex"
|
||||
22 / http Connection
|
||||
\ "http"
|
||||
Storage> 1
|
||||
Remote or path to alias.
|
||||
Can be "myremote:path/to/dir", "myremote:bucket", "myremote:" or "/local/path".
|
||||
remote> /mnt/storage/backup
|
||||
Remote config
|
||||
--------------------
|
||||
[remote]
|
||||
remote = /mnt/storage/backup
|
||||
--------------------
|
||||
y) Yes this is OK
|
||||
e) Edit this remote
|
||||
d) Delete this remote
|
||||
y/e/d> y
|
||||
Current remotes:
|
||||
|
||||
Name Type
|
||||
==== ====
|
||||
remote alias
|
||||
|
||||
e) Edit existing remote
|
||||
n) New remote
|
||||
d) Delete remote
|
||||
r) Rename remote
|
||||
c) Copy remote
|
||||
s) Set configuration password
|
||||
q) Quit config
|
||||
e/n/d/r/c/s/q> q
|
||||
```
|
||||
|
||||
Once configured you can then use `rclone` like this,
|
||||
|
||||
List directories in top level in `/mnt/storage/backup`
|
||||
|
||||
rclone lsd remote:
|
||||
|
||||
List all the files in `/mnt/storage/backup`
|
||||
|
||||
rclone ls remote:
|
||||
|
||||
Copy another local directory to the alias directory called source
|
||||
|
||||
rclone copy /home/source remote:source
|
||||
|
|
@ -19,6 +19,7 @@ option:
|
|||
|
||||
See the following for detailed instructions for
|
||||
|
||||
* [Alias](/alias/)
|
||||
* [Amazon Drive](/amazonclouddrive/)
|
||||
* [Amazon S3](/s3/)
|
||||
* [Backblaze B2](/b2/)
|
||||
|
|
Loading…
Reference in a new issue