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
This commit is contained in:
Keepers 2022-05-25 14:06:05 -06:00 committed by GitHub
parent 569ba524ac
commit 548eb1be72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 92 additions and 87 deletions

View File

@ -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,
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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)
}
})
}
}