diff --git a/src/internal/kopia/kopia.go b/src/internal/kopia/kopia.go index e162808ac..9e76ddd22 100644 --- a/src/internal/kopia/kopia.go +++ b/src/internal/kopia/kopia.go @@ -3,21 +3,32 @@ package kopia import ( "context" + "github.com/kopia/kopia/fs" + "github.com/kopia/kopia/fs/virtualfs" "github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo/blob" "github.com/kopia/kopia/snapshot" + "github.com/kopia/kopia/snapshot/policy" + "github.com/kopia/kopia/snapshot/snapshotfs" "github.com/pkg/errors" + "github.com/alcionai/corso/internal/connector" "github.com/alcionai/corso/pkg/storage" ) const ( defaultKopiaConfigFilePath = "/tmp/repository.config" + + // TODO(ashmrtnz): These should be some values from upper layer corso, + // possibly corresponding to who is making the backup. + kTestHost = "a-test-machine" + kTestUser = "testUser" ) var ( - errInit = errors.New("initializing repo") - errConnect = errors.New("connecting repo") + errInit = errors.New("initializing repo") + errConnect = errors.New("connecting repo") + errNotConnected = errors.New("not connected to repo") ) type BackupStats struct { @@ -35,7 +46,7 @@ func manifestToStats(man *snapshot.Manifest) BackupStats { TotalDirectoryCount: int(man.Stats.TotalDirectoryCount), IgnoredErrorCount: int(man.Stats.IgnoredErrorCount), ErrorCount: int(man.Stats.ErrorCount), - Incomplete: man.IncompleteReason == "", + Incomplete: man.IncompleteReason != "", IncompleteReason: man.IncompleteReason, } } @@ -148,3 +159,68 @@ func (kw *KopiaWrapper) open(ctx context.Context, password string) error { kw.rep = rep return nil } + +func inflateDirTree(ctx context.Context, collections []connector.DataCollection) (fs.Directory, error) { + // TODO(ashmrtnz): Implement when virtualfs.StreamingDirectory is available. + return virtualfs.NewStaticDirectory("sample-dir", []fs.Entry{}), nil +} + +func (kw KopiaWrapper) BackupCollections( + ctx context.Context, + collections []connector.DataCollection, +) (*BackupStats, error) { + if kw.rep == nil { + return nil, errNotConnected + } + + dirTree, err := inflateDirTree(ctx, collections) + if err != nil { + return nil, errors.Wrap(err, "building kopia directories") + } + + stats, err := kw.makeSnapshotWithRoot(ctx, dirTree) + if err != nil { + return nil, err + } + + return stats, nil +} + +func (kw KopiaWrapper) makeSnapshotWithRoot( + ctx context.Context, + root fs.Directory, +) (*BackupStats, error) { + si := snapshot.SourceInfo{ + Host: kTestHost, + UserName: kTestUser, + // TODO(ashmrtnz): will this be something useful for snapshot lookups later? + Path: root.Name(), + } + ctx, rw, err := kw.rep.NewWriter(ctx, repo.WriteSessionOptions{}) + if err != nil { + return nil, errors.Wrap(err, "get repo writer") + } + + policyTree, err := policy.TreeForSource(ctx, kw.rep, si) + if err != nil { + return nil, errors.Wrap(err, "get policy tree") + } + + u := snapshotfs.NewUploader(rw) + + man, err := u.Upload(ctx, root, policyTree, si) + if err != nil { + return nil, errors.Wrap(err, "uploading data") + } + + if _, err := snapshot.SaveSnapshot(ctx, rw, man); err != nil { + return nil, errors.Wrap(err, "saving snapshot") + } + + if err := rw.Flush(ctx); err != nil { + return nil, errors.Wrap(err, "flushing writer") + } + + res := manifestToStats(man) + return &res, nil +} diff --git a/src/internal/kopia/kopia_test.go b/src/internal/kopia/kopia_test.go index 2f05ec9e1..6d54b7809 100644 --- a/src/internal/kopia/kopia_test.go +++ b/src/internal/kopia/kopia_test.go @@ -50,8 +50,11 @@ type KopiaIntegrationSuite struct { } func TestKopiaIntegrationSuite(t *testing.T) { - if err := ctesting.RunOnAny(ctesting.CORSO_CI_TESTS); err != nil { - t.Skip(err) + if err := ctesting.RunOnAny( + ctesting.CORSO_CI_TESTS, + ctesting.CORSO_KOPIA_WRAPPER_TESTS, + ); err != nil { + t.Skip() } suite.Run(t, new(KopiaIntegrationSuite)) @@ -71,3 +74,22 @@ func (suite *KopiaIntegrationSuite) TestCloseTwiceDoesNotCrash() { assert.Nil(suite.T(), k.rep) assert.NoError(suite.T(), k.Close(ctx)) } + +func (suite *KopiaIntegrationSuite) TestBackupCollections() { + ctx := context.Background() + timeOfTest := ctesting.LogTimeOfTest(suite.T()) + + k, err := openKopiaRepo(ctx, "init-s3-"+timeOfTest) + assert.NoError(suite.T(), err) + defer func() { + assert.NoError(suite.T(), k.Close(ctx)) + }() + + stats, err := k.BackupCollections(ctx, nil) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), stats.TotalFileCount, 0) + assert.Equal(suite.T(), stats.TotalDirectoryCount, 1) + assert.Equal(suite.T(), stats.IgnoredErrorCount, 0) + assert.Equal(suite.T(), stats.ErrorCount, 0) + assert.False(suite.T(), stats.Incomplete) +} diff --git a/src/internal/testing/integration_runners.go b/src/internal/testing/integration_runners.go index 0b7d8e31b..099e7808d 100644 --- a/src/internal/testing/integration_runners.go +++ b/src/internal/testing/integration_runners.go @@ -13,6 +13,7 @@ const ( CORSO_CI_TESTS = "CORSO_CI_TESTS" CORSO_GRAPH_CONNECTOR_TESTS = "CORSO_GRAPH_CONNECTOR_TESTS" CORSO_REPOSITORY_TESTS = "CORSO_REPOSITORY_TESTS" + CORSO_KOPIA_WRAPPER_TESTS = "CORSO_KOPIA_WRAPPER_TESTS" ) // RunOnAny takes in a list of env variable names and returns