mirror of
https://github.com/kopia/kopia.git
synced 2026-05-19 20:24:46 -04:00
refactor(cli): An in-memory storage mock setup for CLI tests (#1697)
* refactor cli tests to allow the use of in-memory mock * use in-memory repo for set-parameters cli tests * move inmemory storage provider into test package Co-authored-by: Shikhar Mall <shikhar@kasten.io>
This commit is contained in:
15
cli/app.go
15
cli/app.go
@@ -89,7 +89,7 @@ type appServices interface {
|
||||
|
||||
type advancedAppServices interface {
|
||||
appServices
|
||||
storageProviderServices
|
||||
StorageProviderServices
|
||||
|
||||
runConnectCommandWithStorage(ctx context.Context, co *connectOptions, st blob.Storage) error
|
||||
runConnectCommandWithStorageAndPassword(ctx context.Context, co *connectOptions, st blob.Storage, password string) error
|
||||
@@ -121,6 +121,7 @@ type App struct {
|
||||
persistCredentials bool
|
||||
disableInternalLog bool
|
||||
AdvancedCommands string
|
||||
cliStorageProviders []StorageProvider
|
||||
|
||||
currentAction string
|
||||
onExitCallbacks []func()
|
||||
@@ -274,6 +275,18 @@ type commandParent interface {
|
||||
func NewApp() *App {
|
||||
return &App{
|
||||
progress: &cliProgress{},
|
||||
cliStorageProviders: []StorageProvider{
|
||||
{"from-config", "the provided configuration file", func() StorageFlags { return &storageFromConfigFlags{} }},
|
||||
|
||||
{"azure", "an Azure blob storage", func() StorageFlags { return &storageAzureFlags{} }},
|
||||
{"b2", "a B2 bucket", func() StorageFlags { return &storageB2Flags{} }},
|
||||
{"filesystem", "a filesystem", func() StorageFlags { return &storageFilesystemFlags{} }},
|
||||
{"gcs", "a Google Cloud Storage bucket", func() StorageFlags { return &storageGCSFlags{} }},
|
||||
{"rclone", "a rclone-based provided", func() StorageFlags { return &storageRcloneFlags{} }},
|
||||
{"s3", "an S3 bucket", func() StorageFlags { return &storageS3Flags{} }},
|
||||
{"sftp", "an SFTP storage", func() StorageFlags { return &storageSFTPFlags{} }},
|
||||
{"webdav", "a WebDAV storage", func() StorageFlags { return &storageWebDAVFlags{} }},
|
||||
},
|
||||
|
||||
// testability hooks
|
||||
osExit: os.Exit,
|
||||
|
||||
@@ -25,14 +25,14 @@ func (c *commandRepositoryConnect) setup(svc advancedAppServices, parent command
|
||||
c.co.setup(cmd)
|
||||
c.server.setup(svc, cmd, &c.co)
|
||||
|
||||
for _, prov := range cliStorageProviders() {
|
||||
for _, prov := range svc.storageProviders() {
|
||||
// Set up 'connect' subcommand
|
||||
f := prov.newFlags()
|
||||
cc := cmd.Command(prov.name, "Connect to repository in "+prov.description)
|
||||
f.setup(svc, cc)
|
||||
f := prov.NewFlags()
|
||||
cc := cmd.Command(prov.Name, "Connect to repository in "+prov.Description)
|
||||
f.Setup(svc, cc)
|
||||
cc.Action(func(_ *kingpin.ParseContext) error {
|
||||
ctx := svc.rootContext()
|
||||
st, err := f.connect(ctx, false, 0)
|
||||
st, err := f.Connect(ctx, false, 0)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "can't connect to storage")
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ type storageFromConfigFlags struct {
|
||||
connectFromConfigFile string
|
||||
connectFromConfigToken string
|
||||
|
||||
sps storageProviderServices
|
||||
sps StorageProviderServices
|
||||
}
|
||||
|
||||
func (c *storageFromConfigFlags) setup(sps storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageFromConfigFlags) Setup(sps StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("file", "Path to the configuration file").StringVar(&c.connectFromConfigFile)
|
||||
cmd.Flag("token", "Configuration token").StringVar(&c.connectFromConfigToken)
|
||||
|
||||
c.sps = sps
|
||||
}
|
||||
|
||||
func (c *storageFromConfigFlags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageFromConfigFlags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
if isCreate {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
|
||||
@@ -52,18 +52,18 @@ func (c *commandRepositoryCreate) setup(svc advancedAppServices, parent commandP
|
||||
c.svc = svc
|
||||
c.out.setup(svc)
|
||||
|
||||
for _, prov := range cliStorageProviders() {
|
||||
if prov.name == "from-config" {
|
||||
for _, prov := range svc.storageProviders() {
|
||||
if prov.Name == "from-config" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set up 'create' subcommand
|
||||
f := prov.newFlags()
|
||||
cc := cmd.Command(prov.name, "Create repository in "+prov.description)
|
||||
f.setup(svc, cc)
|
||||
f := prov.NewFlags()
|
||||
cc := cmd.Command(prov.Name, "Create repository in "+prov.Description)
|
||||
f.Setup(svc, cc)
|
||||
cc.Action(func(_ *kingpin.ParseContext) error {
|
||||
ctx := svc.rootContext()
|
||||
st, err := f.connect(ctx, true, c.createFormatVersion)
|
||||
st, err := f.Connect(ctx, true, c.createFormatVersion)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "can't connect to storage")
|
||||
}
|
||||
|
||||
@@ -25,13 +25,13 @@ func (c *commandRepositoryRepair) setup(svc advancedAppServices, parent commandP
|
||||
cmd.Flag("recover-format-block-prefixes", "Prefixes of file names").StringsVar(&c.repairCommandRecoverFormatBlobPrefixes)
|
||||
cmd.Flag("dry-run", "Do not modify repository").Short('n').BoolVar(&c.repairDryRun)
|
||||
|
||||
for _, prov := range cliStorageProviders() {
|
||||
f := prov.newFlags()
|
||||
cc := cmd.Command(prov.name, "Repair repository in "+prov.description)
|
||||
f.setup(svc, cc)
|
||||
for _, prov := range svc.storageProviders() {
|
||||
f := prov.NewFlags()
|
||||
cc := cmd.Command(prov.Name, "Repair repository in "+prov.Description)
|
||||
f.Setup(svc, cc)
|
||||
cc.Action(func(_ *kingpin.ParseContext) error {
|
||||
ctx := svc.rootContext()
|
||||
st, err := f.connect(ctx, false, 0)
|
||||
st, err := f.Connect(ctx, false, 0)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "can't connect to storage")
|
||||
}
|
||||
|
||||
@@ -3,16 +3,39 @@
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/kingpin"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/blobtesting"
|
||||
"github.com/kopia/kopia/internal/repotesting"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/tests/testenv"
|
||||
)
|
||||
|
||||
func (s *formatSpecificTestSuite) TestRepositorySetParameters(t *testing.T) {
|
||||
env := testenv.NewCLITest(t, s.formatFlags, testenv.NewInProcRunner(t))
|
||||
func (s *formatSpecificTestSuite) setupInMemoryRepo(t *testing.T) *testenv.CLITest {
|
||||
t.Helper()
|
||||
|
||||
env.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", env.RepoDir)
|
||||
runner := testenv.NewInProcRunner(t)
|
||||
runner.CustomizeApp = func(a *cli.App, kp *kingpin.Application) {
|
||||
a.AddStorageProvider(cli.StorageProvider{
|
||||
Name: "in-memory",
|
||||
Description: "in-memory storage backend",
|
||||
NewFlags: func() cli.StorageFlags { return &storageInMemoryFlags{} },
|
||||
})
|
||||
}
|
||||
|
||||
env := testenv.NewCLITest(t, s.formatFlags, runner)
|
||||
st := repotesting.NewReconnectableStorage(t, blobtesting.NewVersionedMapStorage(nil))
|
||||
|
||||
env.RunAndExpectSuccess(t, "repo", "create", "in-memory", "--uuid",
|
||||
st.ConnectionInfo().Config.(*repotesting.ReconnectableStorageOptions).UUID)
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func (s *formatSpecificTestSuite) TestRepositorySetParameters(t *testing.T) {
|
||||
env := s.setupInMemoryRepo(t)
|
||||
out := env.RunAndExpectSuccess(t, "repository", "status")
|
||||
|
||||
// default values
|
||||
@@ -40,9 +63,7 @@ func (s *formatSpecificTestSuite) TestRepositorySetParameters(t *testing.T) {
|
||||
}
|
||||
|
||||
func (s *formatSpecificTestSuite) TestRepositorySetParametersUpgrade(t *testing.T) {
|
||||
env := testenv.NewCLITest(t, s.formatFlags, testenv.NewInProcRunner(t))
|
||||
|
||||
env.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", env.RepoDir)
|
||||
env := s.setupInMemoryRepo(t)
|
||||
out := env.RunAndExpectSuccess(t, "repository", "status")
|
||||
|
||||
// default values
|
||||
|
||||
@@ -50,14 +50,14 @@ func (c *commandRepositorySyncTo) setup(svc advancedAppServices, parent commandP
|
||||
// needs to be 64-bit aligned on ARM
|
||||
c.nextSyncOutputTime = new(timetrack.Throttle)
|
||||
|
||||
for _, prov := range cliStorageProviders() {
|
||||
for _, prov := range svc.storageProviders() {
|
||||
// Set up 'sync-to' subcommand
|
||||
f := prov.newFlags()
|
||||
cc := cmd.Command(prov.name, "Synchronize repository data to another repository in "+prov.description)
|
||||
f.setup(svc, cc)
|
||||
f := prov.NewFlags()
|
||||
cc := cmd.Command(prov.Name, "Synchronize repository data to another repository in "+prov.Description)
|
||||
f.Setup(svc, cc)
|
||||
cc.Action(func(_ *kingpin.ParseContext) error {
|
||||
ctx := svc.rootContext()
|
||||
st, err := f.connect(ctx, false, 0)
|
||||
st, err := f.Connect(ctx, false, 0)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "can't connect to storage")
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type storageAzureFlags struct {
|
||||
azOptions azure.Options
|
||||
}
|
||||
|
||||
func (c *storageAzureFlags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageAzureFlags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("container", "Name of the Azure blob container").Required().StringVar(&c.azOptions.Container)
|
||||
cmd.Flag("storage-account", "Azure storage account name (overrides AZURE_STORAGE_ACCOUNT environment variable)").Required().Envar("AZURE_STORAGE_ACCOUNT").StringVar(&c.azOptions.StorageAccount)
|
||||
cmd.Flag("storage-key", "Azure storage account key (overrides AZURE_STORAGE_KEY environment variable)").Envar("AZURE_STORAGE_KEY").StringVar(&c.azOptions.StorageKey)
|
||||
@@ -24,7 +24,7 @@ func (c *storageAzureFlags) setup(_ storageProviderServices, cmd *kingpin.CmdCla
|
||||
commonThrottlingFlags(cmd, &c.azOptions.Limits)
|
||||
}
|
||||
|
||||
func (c *storageAzureFlags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageAzureFlags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
// nolint:wrapcheck
|
||||
return azure.New(ctx, &c.azOptions)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type storageB2Flags struct {
|
||||
b2options b2.Options
|
||||
}
|
||||
|
||||
func (c *storageB2Flags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageB2Flags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("bucket", "Name of the B2 bucket").Required().StringVar(&c.b2options.BucketName)
|
||||
cmd.Flag("key-id", "Key ID (overrides B2_KEY_ID environment variable)").Required().Envar("B2_KEY_ID").StringVar(&c.b2options.KeyID)
|
||||
cmd.Flag("key", "Secret key (overrides B2_KEY environment variable)").Required().Envar("B2_KEY").StringVar(&c.b2options.Key)
|
||||
@@ -21,7 +21,7 @@ func (c *storageB2Flags) setup(_ storageProviderServices, cmd *kingpin.CmdClause
|
||||
commonThrottlingFlags(cmd, &c.b2options.Limits)
|
||||
}
|
||||
|
||||
func (c *storageB2Flags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageB2Flags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
// nolint:wrapcheck
|
||||
return b2.New(ctx, &c.b2options)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ type storageFilesystemFlags struct {
|
||||
connectFlat bool
|
||||
}
|
||||
|
||||
func (c *storageFilesystemFlags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageFilesystemFlags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("path", "Path to the repository").Required().StringVar(&c.options.Path)
|
||||
cmd.Flag("owner-uid", "User ID owning newly created files").PlaceHolder("USER").StringVar(&c.connectOwnerUID)
|
||||
cmd.Flag("owner-gid", "Group ID owning newly created files").PlaceHolder("GROUP").StringVar(&c.connectOwnerGID)
|
||||
@@ -40,7 +40,7 @@ func (c *storageFilesystemFlags) setup(_ storageProviderServices, cmd *kingpin.C
|
||||
commonThrottlingFlags(cmd, &c.options.Limits)
|
||||
}
|
||||
|
||||
func (c *storageFilesystemFlags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageFilesystemFlags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
fso := c.options
|
||||
|
||||
fso.Path = ospath.ResolveUserFriendlyPath(fso.Path, false)
|
||||
|
||||
@@ -18,7 +18,7 @@ type storageGCSFlags struct {
|
||||
embedCredentials bool
|
||||
}
|
||||
|
||||
func (c *storageGCSFlags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageGCSFlags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("bucket", "Name of the Google Cloud Storage bucket").Required().StringVar(&c.options.BucketName)
|
||||
cmd.Flag("prefix", "Prefix to use for objects in the bucket").StringVar(&c.options.Prefix)
|
||||
cmd.Flag("read-only", "Use read-only GCS scope to prevent write access").BoolVar(&c.options.ReadOnly)
|
||||
@@ -28,7 +28,7 @@ func (c *storageGCSFlags) setup(_ storageProviderServices, cmd *kingpin.CmdClaus
|
||||
commonThrottlingFlags(cmd, &c.options.Limits)
|
||||
}
|
||||
|
||||
func (c *storageGCSFlags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageGCSFlags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
if c.embedCredentials {
|
||||
data, err := os.ReadFile(c.options.ServiceAccountCredentialsFile)
|
||||
if err != nil {
|
||||
|
||||
29
cli/storage_inmemory_test.go
Normal file
29
cli/storage_inmemory_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/alecthomas/kingpin"
|
||||
|
||||
"github.com/kopia/kopia/cli"
|
||||
"github.com/kopia/kopia/internal/repotesting"
|
||||
"github.com/kopia/kopia/repo/blob"
|
||||
)
|
||||
|
||||
// storageInMemoryFlags is in-memory storage initialization flags for cli
|
||||
// setup.
|
||||
type storageInMemoryFlags struct {
|
||||
options repotesting.ReconnectableStorageOptions
|
||||
}
|
||||
|
||||
func (c *storageInMemoryFlags) Setup(_ cli.StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("uuid", "UUID of the reconnectable in-memory storage").Required().StringVar(&c.options.UUID)
|
||||
}
|
||||
|
||||
func (c *storageInMemoryFlags) Connect(ctx context.Context, isCreate bool, _ int) (blob.Storage, error) {
|
||||
// nolint:wrapcheck
|
||||
return blob.NewStorage(ctx, blob.ConnectionInfo{
|
||||
Type: repotesting.ReconnectableStorageType,
|
||||
Config: &c.options,
|
||||
}, isCreate)
|
||||
}
|
||||
@@ -9,37 +9,43 @@
|
||||
"github.com/kopia/kopia/repo/blob/throttling"
|
||||
)
|
||||
|
||||
type storageProviderServices interface {
|
||||
// StorageProviderServices is implemented by the cli App that allows the cli
|
||||
// and tests to mutate the default storage providers.
|
||||
type StorageProviderServices interface {
|
||||
AddStorageProvider(p StorageProvider)
|
||||
|
||||
setPasswordFromToken(pwd string)
|
||||
storageProviders() []StorageProvider
|
||||
}
|
||||
|
||||
type storageFlags interface {
|
||||
setup(sps storageProviderServices, cmd *kingpin.CmdClause)
|
||||
connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error)
|
||||
// StorageFlags is implemented by cli storage providers which need to support a
|
||||
// particular backend. This requires the common setup and connection methods
|
||||
// implemented by all the cli storage providers.
|
||||
type StorageFlags interface {
|
||||
Setup(sps StorageProviderServices, cmd *kingpin.CmdClause)
|
||||
Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error)
|
||||
}
|
||||
|
||||
type storageProvider struct {
|
||||
name string
|
||||
description string
|
||||
newFlags func() storageFlags
|
||||
}
|
||||
|
||||
func cliStorageProviders() []storageProvider {
|
||||
return []storageProvider{
|
||||
{"from-config", "the provided configuration file", func() storageFlags { return &storageFromConfigFlags{} }},
|
||||
|
||||
{"azure", "an Azure blob storage", func() storageFlags { return &storageAzureFlags{} }},
|
||||
{"b2", "a B2 bucket", func() storageFlags { return &storageB2Flags{} }},
|
||||
{"filesystem", "a filesystem", func() storageFlags { return &storageFilesystemFlags{} }},
|
||||
{"gcs", "a Google Cloud Storage bucket", func() storageFlags { return &storageGCSFlags{} }},
|
||||
{"rclone", "a rclone-based provided", func() storageFlags { return &storageRcloneFlags{} }},
|
||||
{"s3", "an S3 bucket", func() storageFlags { return &storageS3Flags{} }},
|
||||
{"sftp", "an SFTP storage", func() storageFlags { return &storageSFTPFlags{} }},
|
||||
{"webdav", "a WebDAV storage", func() storageFlags { return &storageWebDAVFlags{} }},
|
||||
}
|
||||
// StorageProvider is a CLI provider for storage options and allows the CLI to
|
||||
// multiplex between various provider CLI flag constructors.
|
||||
type StorageProvider struct {
|
||||
Name string
|
||||
Description string
|
||||
NewFlags func() StorageFlags
|
||||
}
|
||||
|
||||
func commonThrottlingFlags(cmd *kingpin.CmdClause, limits *throttling.Limits) {
|
||||
cmd.Flag("max-download-speed", "Limit the download speed.").PlaceHolder("BYTES_PER_SEC").FloatVar(&limits.DownloadBytesPerSecond)
|
||||
cmd.Flag("max-upload-speed", "Limit the upload speed.").PlaceHolder("BYTES_PER_SEC").FloatVar(&limits.UploadBytesPerSecond)
|
||||
}
|
||||
|
||||
// AddStorageProvider adds a new StorageProvider at runtime after the App has
|
||||
// been initialized with the default providers. This is used in tests which
|
||||
// require custom storage providers to simulate various edge cases.
|
||||
func (c *App) AddStorageProvider(p StorageProvider) {
|
||||
c.cliStorageProviders = append(c.cliStorageProviders, p)
|
||||
}
|
||||
|
||||
func (c *App) storageProviders() []StorageProvider {
|
||||
return c.cliStorageProviders
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ type storageRcloneFlags struct {
|
||||
embedRCloneConfigFile string
|
||||
}
|
||||
|
||||
func (c *storageRcloneFlags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageRcloneFlags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("remote-path", "Rclone remote:path").Required().StringVar(&c.opt.RemotePath)
|
||||
cmd.Flag("flat", "Use flat directory structure").BoolVar(&c.connectFlat)
|
||||
cmd.Flag("rclone-exe", "Path to rclone binary").StringVar(&c.opt.RCloneExe)
|
||||
@@ -32,7 +32,7 @@ func (c *storageRcloneFlags) setup(_ storageProviderServices, cmd *kingpin.CmdCl
|
||||
commonThrottlingFlags(cmd, &c.opt.Limits)
|
||||
}
|
||||
|
||||
func (c *storageRcloneFlags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageRcloneFlags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
c.opt.DirectoryShards = initialDirectoryShards(c.connectFlat, formatVersion)
|
||||
|
||||
if c.embedRCloneConfigFile != "" {
|
||||
|
||||
@@ -15,7 +15,7 @@ type storageS3Flags struct {
|
||||
s3options s3.Options
|
||||
}
|
||||
|
||||
func (c *storageS3Flags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageS3Flags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("bucket", "Name of the S3 bucket").Required().StringVar(&c.s3options.BucketName)
|
||||
cmd.Flag("endpoint", "Endpoint to use").Default("s3.amazonaws.com").StringVar(&c.s3options.Endpoint)
|
||||
cmd.Flag("region", "S3 Region").Default("").StringVar(&c.s3options.Region)
|
||||
@@ -46,7 +46,7 @@ func (c *storageS3Flags) setup(_ storageProviderServices, cmd *kingpin.CmdClause
|
||||
cmd.Flag("point-in-time", "Use a point-in-time view of the storage repository when supported").PlaceHolder(time.RFC3339).PreAction(pitPreAction).StringVar(&pointInTimeStr)
|
||||
}
|
||||
|
||||
func (c *storageS3Flags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageS3Flags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
if isCreate && c.s3options.PointInTime != nil && !c.s3options.PointInTime.IsZero() {
|
||||
return nil, errors.New("Cannot specify a 'point-in-time' option when creating a repository")
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ type storageSFTPFlags struct {
|
||||
embedCredentials bool
|
||||
}
|
||||
|
||||
func (c *storageSFTPFlags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageSFTPFlags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("path", "Path to the repository in the SFTP/SSH server").Required().StringVar(&c.options.Path)
|
||||
cmd.Flag("host", "SFTP/SSH server hostname").Required().StringVar(&c.options.Host)
|
||||
cmd.Flag("port", "SFTP/SSH server port").Default("22").IntVar(&c.options.Port)
|
||||
@@ -109,7 +109,7 @@ func (c *storageSFTPFlags) getOptions(formatVersion int) (*sftp.Options, error)
|
||||
return &sftpo, nil
|
||||
}
|
||||
|
||||
func (c *storageSFTPFlags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageSFTPFlags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
opt, err := c.getOptions(formatVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -15,7 +15,7 @@ type storageWebDAVFlags struct {
|
||||
connectFlat bool
|
||||
}
|
||||
|
||||
func (c *storageWebDAVFlags) setup(_ storageProviderServices, cmd *kingpin.CmdClause) {
|
||||
func (c *storageWebDAVFlags) Setup(_ StorageProviderServices, cmd *kingpin.CmdClause) {
|
||||
cmd.Flag("url", "URL of WebDAV server").Required().StringVar(&c.options.URL)
|
||||
cmd.Flag("flat", "Use flat directory structure").BoolVar(&c.connectFlat)
|
||||
cmd.Flag("webdav-username", "WebDAV username").Envar("KOPIA_WEBDAV_USERNAME").StringVar(&c.options.Username)
|
||||
@@ -26,7 +26,7 @@ func (c *storageWebDAVFlags) setup(_ storageProviderServices, cmd *kingpin.CmdCl
|
||||
commonThrottlingFlags(cmd, &c.options.Limits)
|
||||
}
|
||||
|
||||
func (c *storageWebDAVFlags) connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
func (c *storageWebDAVFlags) Connect(ctx context.Context, isCreate bool, formatVersion int) (blob.Storage, error) {
|
||||
wo := c.options
|
||||
|
||||
if wo.Username != "" && wo.Password == "" {
|
||||
|
||||
@@ -18,23 +18,25 @@
|
||||
type reconnectableStorage struct {
|
||||
blob.Storage
|
||||
|
||||
opt *reconnectableStorageOptions
|
||||
opt *ReconnectableStorageOptions
|
||||
}
|
||||
|
||||
const reconnectableStorageType = "reconnectable"
|
||||
// ReconnectableStorageType is the unique storage type identifier for
|
||||
// reconnectable storage backend.
|
||||
const ReconnectableStorageType = "reconnectable"
|
||||
|
||||
// reconnectableStorageOptions provides options to reconnectable storage.
|
||||
type reconnectableStorageOptions struct {
|
||||
// ReconnectableStorageOptions provides options to reconnectable storage.
|
||||
type ReconnectableStorageOptions struct {
|
||||
UUID string
|
||||
}
|
||||
|
||||
// newReconnectableStorage wraps the provided storage that may or may not be round-trippable
|
||||
// NewReconnectableStorage wraps the provided storage that may or may not be round-trippable
|
||||
// in a wrapper that globally caches storage instance and ensures its connection info is
|
||||
// round-trippable.
|
||||
func newReconnectableStorage(tb testing.TB, st blob.Storage) blob.Storage {
|
||||
func NewReconnectableStorage(tb testing.TB, st blob.Storage) blob.Storage {
|
||||
tb.Helper()
|
||||
|
||||
st2 := reconnectableStorage{st, &reconnectableStorageOptions{UUID: uuid.NewString()}}
|
||||
st2 := reconnectableStorage{st, &ReconnectableStorageOptions{UUID: uuid.NewString()}}
|
||||
|
||||
reconnectableStorageByUUID.Store(st2.opt.UUID, st2)
|
||||
tb.Cleanup(func() {
|
||||
@@ -49,17 +51,17 @@ func newReconnectableStorage(tb testing.TB, st blob.Storage) blob.Storage {
|
||||
|
||||
func (s reconnectableStorage) ConnectionInfo() blob.ConnectionInfo {
|
||||
return blob.ConnectionInfo{
|
||||
Type: reconnectableStorageType,
|
||||
Type: ReconnectableStorageType,
|
||||
Config: s.opt,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
blob.AddSupportedStorage(
|
||||
reconnectableStorageType,
|
||||
func() interface{} { return &reconnectableStorageOptions{} },
|
||||
ReconnectableStorageType,
|
||||
func() interface{} { return &ReconnectableStorageOptions{} },
|
||||
func(ctx context.Context, o interface{}, isCreate bool) (blob.Storage, error) {
|
||||
opt, ok := o.(*reconnectableStorageOptions)
|
||||
opt, ok := o.(*ReconnectableStorageOptions)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid options %T", o)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func (e *Environment) setup(tb testing.TB, version content.FormatVersion, opts .
|
||||
st = blobtesting.NewVersionedMapStorage(openOpt.TimeNowFunc)
|
||||
}
|
||||
|
||||
st = newReconnectableStorage(tb, st)
|
||||
st = NewReconnectableStorage(tb, st)
|
||||
e.st = st
|
||||
|
||||
if e.Password == "" {
|
||||
|
||||
Reference in New Issue
Block a user