From 548eb1be72684d09e87e0884ef5fd8881a2152b8 Mon Sep 17 00:00:00 2001 From: Keepers <104464746+ryanfkeepers@users.noreply.github.com> Date: Wed, 25 May 2022 14:06:05 -0600 Subject: [PATCH] add e2e wiring of cli to kopia (#77) * add e2e wiring of cli to kopia Now that pkg/storage and internal/kopia are in place, we can wire up the init flow from the cli all the way to kopia. Testing harness for this functionality still needs investigation afterward. * factor out awsVars struct for s3Cfg --- src/cli/repo/repo.go | 2 +- src/cli/repo/s3.go | 73 +++++++++++---------------- src/pkg/repository/repository.go | 42 +++++++-------- src/pkg/repository/repository_test.go | 62 +++++++++++++++-------- 4 files changed, 92 insertions(+), 87 deletions(-) diff --git a/src/cli/repo/repo.go b/src/cli/repo/repo.go index a3f236def..42a0ae3d1 100644 --- a/src/cli/repo/repo.go +++ b/src/cli/repo/repo.go @@ -61,7 +61,7 @@ var connectCmd = &cobra.Command{ Use: connectCommand, Short: "Connect to a repository.", Long: `Connect to an existing repository.`, - Run: handleInitCmd, + Run: handleConnectCmd, Args: cobra.NoArgs, } diff --git a/src/cli/repo/s3.go b/src/cli/repo/s3.go index c9b3c4558..e89c8828e 100644 --- a/src/cli/repo/s3.go +++ b/src/cli/repo/s3.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/cobra" "github.com/alcionai/corso/pkg/repository" - "github.com/alcionai/corso/pkg/repository/s3" + "github.com/alcionai/corso/pkg/storage" ) // s3 bucket info from flags @@ -45,26 +45,23 @@ var s3InitCmd = &cobra.Command{ // initializes a s3 repo. func initS3Cmd(cmd *cobra.Command, args []string) { mv := getM365Vars() - av := getAwsVars() + s3Cfg := makeS3Config() fmt.Printf( "Called -\n`corso repo init s3`\nbucket:\t%s\nkey:\t%s\n356Client:\t%s\nfound 356Secret:\t%v\nfound awsSecret:\t%v\n", - bucket, - av.accessKey, + s3Cfg.Bucket, + s3Cfg.AccessKey, mv.clientID, len(mv.clientSecret) > 0, - len(av.accessSecret) > 0) + len(s3Cfg.SecretKey) > 0) - _, err := repository.Initialize( - cmd.Context(), - repository.ProviderS3, - repository.Account{ - TenantID: mv.tenantID, - ClientID: mv.clientID, - ClientSecret: mv.clientSecret, - }, - s3.NewConfig(av.bucket, av.accessKey, av.accessSecret), - ) - if err != nil { + a := repository.Account{ + TenantID: mv.tenantID, + ClientID: mv.clientID, + ClientSecret: mv.clientSecret, + } + s := storage.NewStorage(storage.ProviderS3, s3Cfg) + + if _, err := repository.Initialize(cmd.Context(), a, s); err != nil { fmt.Printf("Failed to initialize a new S3 repository: %v", err) os.Exit(1) } @@ -82,47 +79,37 @@ var s3ConnectCmd = &cobra.Command{ // connects to an existing s3 repo. func connectS3Cmd(cmd *cobra.Command, args []string) { mv := getM365Vars() - av := getAwsVars() + s3Cfg := makeS3Config() fmt.Printf( "Called -\n`corso repo connect s3`\nbucket:\t%s\nkey:\t%s\n356Client:\t%s\nfound 356Secret:\t%v\nfound awsSecret:\t%v\n", - bucket, - accessKey, + s3Cfg.Bucket, + s3Cfg.AccessKey, mv.clientID, len(mv.clientSecret) > 0, - len(av.accessSecret) > 0) + len(s3Cfg.SecretKey) > 0) - _, err := repository.Connect( - cmd.Context(), - repository.ProviderS3, - repository.Account{ - TenantID: mv.tenantID, - ClientID: mv.clientID, - ClientSecret: mv.clientSecret, - }, - s3.NewConfig(av.bucket, av.accessKey, av.accessSecret), - ) - if err != nil { + a := repository.Account{ + TenantID: mv.tenantID, + ClientID: mv.clientID, + ClientSecret: mv.clientSecret, + } + s := storage.NewStorage(storage.ProviderS3, s3Cfg) + + if _, err := repository.Connect(cmd.Context(), a, s); err != nil { fmt.Printf("Failed to connect to the S3 repository: %v", err) os.Exit(1) } } -// aggregates aws details from flag and env_var values. -type awsVars struct { - accessKey string - accessSecret string - bucket string -} - // helper for aggregating aws connection details. -func getAwsVars() awsVars { +func makeS3Config() storage.S3Config { ak := os.Getenv("AWS_ACCESS_KEY_ID") if len(accessKey) > 0 { ak = accessKey } - return awsVars{ - accessKey: ak, - accessSecret: os.Getenv("AWS_SECRET_ACCESS_KEY"), - bucket: bucket, + return storage.S3Config{ + AccessKey: ak, + SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"), + Bucket: bucket, } } diff --git a/src/pkg/repository/repository.go b/src/pkg/repository/repository.go index f61a308e2..8e63d426d 100644 --- a/src/pkg/repository/repository.go +++ b/src/pkg/repository/repository.go @@ -4,8 +4,9 @@ import ( "context" "time" + "github.com/alcionai/corso/internal/kopia" + "github.com/alcionai/corso/pkg/storage" "github.com/google/uuid" - "github.com/kopia/kopia/repo/blob" ) type repoProvider int @@ -22,9 +23,8 @@ type Repository struct { CreatedAt time.Time Version string // in case of future breaking changes - Provider repoProvider // must be repository.S3Provider - Account Account // the user's m365 account connection details - Config Config // provider-based configuration details + Account Account // the user's m365 account connection details + Storage storage.Storage // the storage provider details and configuration } // Account holds the user's m365 account details. @@ -34,10 +34,6 @@ type Account struct { ClientSecret string } -type Config interface { - KopiaStorage(ctx context.Context, create bool) (blob.Storage, error) -} - // Initialize will: // * validate the m365 account & secrets // * connect to the m365 account to ensure communication capability @@ -48,16 +44,18 @@ type Config interface { // * return the connected repository func Initialize( ctx context.Context, - provider repoProvider, acct Account, - cfg Config, + storage storage.Storage, ) (Repository, error) { + k := kopia.New(storage) + if err := k.Initialize(ctx); err != nil { + return Repository{}, err + } r := Repository{ - ID: uuid.New(), - Version: "v1", - Provider: provider, - Account: acct, - Config: cfg, + ID: uuid.New(), + Version: "v1", + Account: acct, + Storage: storage, } return r, nil } @@ -69,16 +67,18 @@ func Initialize( // * return the connected repository func Connect( ctx context.Context, - provider repoProvider, acct Account, - cfg Config, + storage storage.Storage, ) (Repository, error) { + k := kopia.New(storage) + if err := k.Connect(ctx); err != nil { + return Repository{}, err + } // todo: ID and CreatedAt should get retrieved from a stored kopia config. r := Repository{ - Version: "v1", - Provider: provider, - Account: acct, - Config: cfg, + Version: "v1", + Account: acct, + Storage: storage, } return r, nil } diff --git a/src/pkg/repository/repository_test.go b/src/pkg/repository/repository_test.go index c54cc03b8..55d1ea984 100644 --- a/src/pkg/repository/repository_test.go +++ b/src/pkg/repository/repository_test.go @@ -2,37 +2,55 @@ package repository_test import ( "context" + "strings" "testing" - "github.com/kopia/kopia/repo/blob" - "github.com/alcionai/corso/pkg/repository" + "github.com/alcionai/corso/pkg/storage" ) -type testConfig struct{} - -func (tc testConfig) KopiaStorage(ctx context.Context, create bool) (blob.Storage, error) { - return nil, nil -} - func TestInitialize(t *testing.T) { - _, err := repository.Initialize( - context.Background(), - repository.ProviderUnknown, - repository.Account{}, - testConfig{}) - if err != nil { - t.Fatalf("didn't expect initialize to error, got [%v]", err) + table := []struct { + storage storage.Storage + account repository.Account + expectedErr string + }{ + { + storage.NewStorage(storage.ProviderUnknown), + repository.Account{}, + "provider details are required", + }, + } + for _, test := range table { + t.Run(test.expectedErr, func(t *testing.T) { + _, err := repository.Initialize(context.Background(), test.account, test.storage) + if err == nil || !strings.Contains(err.Error(), test.expectedErr) { + t.Fatalf("expected error with [%s], got [%v]", test.expectedErr, err) + } + }) } } +// repository.Connect involves end-to-end communication with kopia, therefore this only +// tests expected error cases from func TestConnect(t *testing.T) { - _, err := repository.Connect( - context.Background(), - repository.ProviderUnknown, - repository.Account{}, - testConfig{}) - if err != nil { - t.Fatalf("didn't expect connect to error, got [%v]", err) + table := []struct { + storage storage.Storage + account repository.Account + expectedErr string + }{ + { + storage.NewStorage(storage.ProviderUnknown), + repository.Account{}, + "provider details are required", + }, + } + for _, test := range table { + t.Run(test.expectedErr, func(t *testing.T) { + _, err := repository.Connect(context.Background(), test.account, test.storage) + if err == nil || !strings.Contains(err.Error(), test.expectedErr) { + t.Fatalf("expected error with [%s], got [%v]", test.expectedErr, err) + } + }) } }