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:
parent
569ba524ac
commit
548eb1be72
@ -61,7 +61,7 @@ var connectCmd = &cobra.Command{
|
|||||||
Use: connectCommand,
|
Use: connectCommand,
|
||||||
Short: "Connect to a repository.",
|
Short: "Connect to a repository.",
|
||||||
Long: `Connect to an existing repository.`,
|
Long: `Connect to an existing repository.`,
|
||||||
Run: handleInitCmd,
|
Run: handleConnectCmd,
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/alcionai/corso/pkg/repository"
|
"github.com/alcionai/corso/pkg/repository"
|
||||||
"github.com/alcionai/corso/pkg/repository/s3"
|
"github.com/alcionai/corso/pkg/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// s3 bucket info from flags
|
// s3 bucket info from flags
|
||||||
@ -45,26 +45,23 @@ var s3InitCmd = &cobra.Command{
|
|||||||
// initializes a s3 repo.
|
// initializes a s3 repo.
|
||||||
func initS3Cmd(cmd *cobra.Command, args []string) {
|
func initS3Cmd(cmd *cobra.Command, args []string) {
|
||||||
mv := getM365Vars()
|
mv := getM365Vars()
|
||||||
av := getAwsVars()
|
s3Cfg := makeS3Config()
|
||||||
fmt.Printf(
|
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",
|
"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,
|
s3Cfg.Bucket,
|
||||||
av.accessKey,
|
s3Cfg.AccessKey,
|
||||||
mv.clientID,
|
mv.clientID,
|
||||||
len(mv.clientSecret) > 0,
|
len(mv.clientSecret) > 0,
|
||||||
len(av.accessSecret) > 0)
|
len(s3Cfg.SecretKey) > 0)
|
||||||
|
|
||||||
_, err := repository.Initialize(
|
a := repository.Account{
|
||||||
cmd.Context(),
|
TenantID: mv.tenantID,
|
||||||
repository.ProviderS3,
|
ClientID: mv.clientID,
|
||||||
repository.Account{
|
ClientSecret: mv.clientSecret,
|
||||||
TenantID: mv.tenantID,
|
}
|
||||||
ClientID: mv.clientID,
|
s := storage.NewStorage(storage.ProviderS3, s3Cfg)
|
||||||
ClientSecret: mv.clientSecret,
|
|
||||||
},
|
if _, err := repository.Initialize(cmd.Context(), a, s); err != nil {
|
||||||
s3.NewConfig(av.bucket, av.accessKey, av.accessSecret),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to initialize a new S3 repository: %v", err)
|
fmt.Printf("Failed to initialize a new S3 repository: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@ -82,47 +79,37 @@ var s3ConnectCmd = &cobra.Command{
|
|||||||
// connects to an existing s3 repo.
|
// connects to an existing s3 repo.
|
||||||
func connectS3Cmd(cmd *cobra.Command, args []string) {
|
func connectS3Cmd(cmd *cobra.Command, args []string) {
|
||||||
mv := getM365Vars()
|
mv := getM365Vars()
|
||||||
av := getAwsVars()
|
s3Cfg := makeS3Config()
|
||||||
fmt.Printf(
|
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",
|
"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,
|
s3Cfg.Bucket,
|
||||||
accessKey,
|
s3Cfg.AccessKey,
|
||||||
mv.clientID,
|
mv.clientID,
|
||||||
len(mv.clientSecret) > 0,
|
len(mv.clientSecret) > 0,
|
||||||
len(av.accessSecret) > 0)
|
len(s3Cfg.SecretKey) > 0)
|
||||||
|
|
||||||
_, err := repository.Connect(
|
a := repository.Account{
|
||||||
cmd.Context(),
|
TenantID: mv.tenantID,
|
||||||
repository.ProviderS3,
|
ClientID: mv.clientID,
|
||||||
repository.Account{
|
ClientSecret: mv.clientSecret,
|
||||||
TenantID: mv.tenantID,
|
}
|
||||||
ClientID: mv.clientID,
|
s := storage.NewStorage(storage.ProviderS3, s3Cfg)
|
||||||
ClientSecret: mv.clientSecret,
|
|
||||||
},
|
if _, err := repository.Connect(cmd.Context(), a, s); err != nil {
|
||||||
s3.NewConfig(av.bucket, av.accessKey, av.accessSecret),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to connect to the S3 repository: %v", err)
|
fmt.Printf("Failed to connect to the S3 repository: %v", err)
|
||||||
os.Exit(1)
|
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.
|
// helper for aggregating aws connection details.
|
||||||
func getAwsVars() awsVars {
|
func makeS3Config() storage.S3Config {
|
||||||
ak := os.Getenv("AWS_ACCESS_KEY_ID")
|
ak := os.Getenv("AWS_ACCESS_KEY_ID")
|
||||||
if len(accessKey) > 0 {
|
if len(accessKey) > 0 {
|
||||||
ak = accessKey
|
ak = accessKey
|
||||||
}
|
}
|
||||||
return awsVars{
|
return storage.S3Config{
|
||||||
accessKey: ak,
|
AccessKey: ak,
|
||||||
accessSecret: os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
||||||
bucket: bucket,
|
Bucket: bucket,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/internal/kopia"
|
||||||
|
"github.com/alcionai/corso/pkg/storage"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/kopia/kopia/repo/blob"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type repoProvider int
|
type repoProvider int
|
||||||
@ -22,9 +23,8 @@ type Repository struct {
|
|||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
Version string // in case of future breaking changes
|
Version string // in case of future breaking changes
|
||||||
|
|
||||||
Provider repoProvider // must be repository.S3Provider
|
Account Account // the user's m365 account connection details
|
||||||
Account Account // the user's m365 account connection details
|
Storage storage.Storage // the storage provider details and configuration
|
||||||
Config Config // provider-based configuration details
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account holds the user's m365 account details.
|
// Account holds the user's m365 account details.
|
||||||
@ -34,10 +34,6 @@ type Account struct {
|
|||||||
ClientSecret string
|
ClientSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config interface {
|
|
||||||
KopiaStorage(ctx context.Context, create bool) (blob.Storage, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize will:
|
// Initialize will:
|
||||||
// * validate the m365 account & secrets
|
// * validate the m365 account & secrets
|
||||||
// * connect to the m365 account to ensure communication capability
|
// * connect to the m365 account to ensure communication capability
|
||||||
@ -48,16 +44,18 @@ type Config interface {
|
|||||||
// * return the connected repository
|
// * return the connected repository
|
||||||
func Initialize(
|
func Initialize(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
provider repoProvider,
|
|
||||||
acct Account,
|
acct Account,
|
||||||
cfg Config,
|
storage storage.Storage,
|
||||||
) (Repository, error) {
|
) (Repository, error) {
|
||||||
|
k := kopia.New(storage)
|
||||||
|
if err := k.Initialize(ctx); err != nil {
|
||||||
|
return Repository{}, err
|
||||||
|
}
|
||||||
r := Repository{
|
r := Repository{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Provider: provider,
|
Account: acct,
|
||||||
Account: acct,
|
Storage: storage,
|
||||||
Config: cfg,
|
|
||||||
}
|
}
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
@ -69,16 +67,18 @@ func Initialize(
|
|||||||
// * return the connected repository
|
// * return the connected repository
|
||||||
func Connect(
|
func Connect(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
provider repoProvider,
|
|
||||||
acct Account,
|
acct Account,
|
||||||
cfg Config,
|
storage storage.Storage,
|
||||||
) (Repository, error) {
|
) (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.
|
// todo: ID and CreatedAt should get retrieved from a stored kopia config.
|
||||||
r := Repository{
|
r := Repository{
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Provider: provider,
|
Account: acct,
|
||||||
Account: acct,
|
Storage: storage,
|
||||||
Config: cfg,
|
|
||||||
}
|
}
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,37 +2,55 @@ package repository_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kopia/kopia/repo/blob"
|
|
||||||
|
|
||||||
"github.com/alcionai/corso/pkg/repository"
|
"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) {
|
func TestInitialize(t *testing.T) {
|
||||||
_, err := repository.Initialize(
|
table := []struct {
|
||||||
context.Background(),
|
storage storage.Storage
|
||||||
repository.ProviderUnknown,
|
account repository.Account
|
||||||
repository.Account{},
|
expectedErr string
|
||||||
testConfig{})
|
}{
|
||||||
if err != nil {
|
{
|
||||||
t.Fatalf("didn't expect initialize to error, got [%v]", err)
|
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) {
|
func TestConnect(t *testing.T) {
|
||||||
_, err := repository.Connect(
|
table := []struct {
|
||||||
context.Background(),
|
storage storage.Storage
|
||||||
repository.ProviderUnknown,
|
account repository.Account
|
||||||
repository.Account{},
|
expectedErr string
|
||||||
testConfig{})
|
}{
|
||||||
if err != nil {
|
{
|
||||||
t.Fatalf("didn't expect connect to error, got [%v]", err)
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user