diff --git a/internal/storage/rclone.go b/internal/storage/rclone.go index 43ac7ba..4ca2be1 100644 --- a/internal/storage/rclone.go +++ b/internal/storage/rclone.go @@ -7,10 +7,12 @@ import ( "errors" "fmt" "io" + "os" "strings" "time" "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/configfile" "github.com/rclone/rclone/fs/operations" @@ -33,6 +35,15 @@ type RcloneStorer struct { // The remote parameter is the rclone remote name (as configured via `rclone config`). // The path parameter is the path within the remote. func NewRcloneStorer(ctx context.Context, remote, path string) (*RcloneStorer, error) { + // rclone reads RCLONE_CONFIG only as the value of its --config flag, + // which vaultik does not have, so it is applied here as that flag would. + if configPath, ok := os.LookupEnv("RCLONE_CONFIG"); ok { + err := config.SetConfigPath(configPath) + if err != nil { + return nil, fmt.Errorf("using RCLONE_CONFIG %q: %w", configPath, err) + } + } + // Install the default config file handler configfile.Install() diff --git a/internal/storage/rclone_test.go b/internal/storage/rclone_test.go index c666a64..6e45274 100644 --- a/internal/storage/rclone_test.go +++ b/internal/storage/rclone_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/configmap" "sneak.berlin/go/vaultik/internal/storage" ) @@ -390,3 +391,40 @@ func TestNewRcloneStorerUnknownRemote(t *testing.T) { t.Errorf("NewRcloneStorer error = %v, want ErrRemoteNotFound", err) } } + +// TestNewRcloneStorerReadsRcloneConfigEnv checks that a remote defined only +// in the file RCLONE_CONFIG names is found and written to, as rclone itself +// would. +func TestNewRcloneStorerReadsRcloneConfigEnv(t *testing.T) { + // rclone keeps the config file path in a process-global variable, which + // NewRcloneStorer leaves pointing at this test's file. + previous := config.GetConfigPath() + t.Cleanup(func() { _ = config.SetConfigPath(previous) }) + + confFile := filepath.Join(t.TempDir(), "rclone.conf") + + err := os.WriteFile(confFile, []byte("[loc]\ntype = local\n"), 0o600) + if err != nil { + t.Fatalf("writing rclone config: %v", err) + } + + t.Setenv("RCLONE_CONFIG", confFile) + + dir := t.TempDir() + ctx := context.Background() + + s, err := storage.NewRcloneStorer(ctx, "loc", dir) + if err != nil { + t.Fatalf("NewRcloneStorer: %v", err) + } + + err = s.Put(ctx, testBlobKey, strings.NewReader("blob-bytes")) + if err != nil { + t.Fatalf("Put: %v", err) + } + + _, err = os.Stat(filepath.Join(dir, testBlobKey)) + if err != nil { + t.Errorf("object is not in the remote's directory: %v", err) + } +}