Create wrapper around kopia maintenance (#3224)

Create a wrapper function around the kopia
maintenance operation. Will allow users to
run maintenance, though they still need to
be careful not to run maintenance
concurrently if they override the username
and hostname that kopia uses

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)

* #3077

#### Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-05-01 17:52:15 -07:00 committed by GitHub
parent 631e1a3b61
commit b6ef2d5266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 375 additions and 40 deletions

View File

@ -44,6 +44,7 @@ require (
github.com/andybalholm/brotli v1.0.4 // indirect github.com/andybalholm/brotli v1.0.4 // indirect
github.com/dnaeon/go-vcr v1.2.0 // indirect github.com/dnaeon/go-vcr v1.2.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect

View File

@ -131,6 +131,7 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=

View File

@ -18,7 +18,7 @@ import (
"github.com/kopia/kopia/snapshot/snapshotfs" "github.com/kopia/kopia/snapshot/snapshotfs"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/storage" "github.com/alcionai/corso/src/pkg/storage"
) )
@ -70,7 +70,7 @@ func NewConn(s storage.Storage) *conn {
} }
} }
func (w *conn) Initialize(ctx context.Context, opts control.RepoOptions) error { func (w *conn) Initialize(ctx context.Context, opts repository.Options) error {
bst, err := blobStoreByProvider(ctx, w.storage) bst, err := blobStoreByProvider(ctx, w.storage)
if err != nil { if err != nil {
return clues.Wrap(err, "initializing storage") return clues.Wrap(err, "initializing storage")
@ -110,7 +110,7 @@ func (w *conn) Initialize(ctx context.Context, opts control.RepoOptions) error {
return nil return nil
} }
func (w *conn) Connect(ctx context.Context, opts control.RepoOptions) error { func (w *conn) Connect(ctx context.Context, opts repository.Options) error {
bst, err := blobStoreByProvider(ctx, w.storage) bst, err := blobStoreByProvider(ctx, w.storage)
if err != nil { if err != nil {
return clues.Wrap(err, "initializing storage") return clues.Wrap(err, "initializing storage")
@ -134,7 +134,7 @@ func (w *conn) Connect(ctx context.Context, opts control.RepoOptions) error {
func (w *conn) commonConnect( func (w *conn) commonConnect(
ctx context.Context, ctx context.Context,
opts control.RepoOptions, opts repository.Options,
configDir string, configDir string,
bst blob.Storage, bst blob.Storage,
password, compressor string, password, compressor string,

View File

@ -14,7 +14,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/storage" "github.com/alcionai/corso/src/pkg/storage"
) )
@ -25,7 +25,7 @@ func openKopiaRepo(
st := tester.NewPrefixedS3Storage(t) st := tester.NewPrefixedS3Storage(t)
k := NewConn(st) k := NewConn(st)
if err := k.Initialize(ctx, control.RepoOptions{}); err != nil { if err := k.Initialize(ctx, repository.Options{}); err != nil {
return nil, err return nil, err
} }
@ -79,13 +79,13 @@ func (suite *WrapperIntegrationSuite) TestRepoExistsError() {
st := tester.NewPrefixedS3Storage(t) st := tester.NewPrefixedS3Storage(t)
k := NewConn(st) k := NewConn(st)
err := k.Initialize(ctx, control.RepoOptions{}) err := k.Initialize(ctx, repository.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
err = k.Close(ctx) err = k.Close(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
err = k.Initialize(ctx, control.RepoOptions{}) err = k.Initialize(ctx, repository.Options{})
assert.Error(t, err, clues.ToCore(err)) assert.Error(t, err, clues.ToCore(err))
assert.ErrorIs(t, err, ErrorRepoAlreadyExists) assert.ErrorIs(t, err, ErrorRepoAlreadyExists)
} }
@ -99,7 +99,7 @@ func (suite *WrapperIntegrationSuite) TestBadProviderErrors() {
st.Provider = storage.ProviderUnknown st.Provider = storage.ProviderUnknown
k := NewConn(st) k := NewConn(st)
err := k.Initialize(ctx, control.RepoOptions{}) err := k.Initialize(ctx, repository.Options{})
assert.Error(t, err, clues.ToCore(err)) assert.Error(t, err, clues.ToCore(err))
} }
@ -111,7 +111,7 @@ func (suite *WrapperIntegrationSuite) TestConnectWithoutInitErrors() {
st := tester.NewPrefixedS3Storage(t) st := tester.NewPrefixedS3Storage(t)
k := NewConn(st) k := NewConn(st)
err := k.Connect(ctx, control.RepoOptions{}) err := k.Connect(ctx, repository.Options{})
assert.Error(t, err, clues.ToCore(err)) assert.Error(t, err, clues.ToCore(err))
} }
@ -358,7 +358,7 @@ func (suite *WrapperIntegrationSuite) TestConfigDefaultsSetOnInitAndNotOnConnect
err = k.Close(ctx) err = k.Close(ctx)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
err = k.Connect(ctx, control.RepoOptions{}) err = k.Connect(ctx, repository.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
defer func() { defer func() {
@ -386,7 +386,7 @@ func (suite *WrapperIntegrationSuite) TestInitAndConnWithTempDirectory() {
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
// Re-open with Connect. // Re-open with Connect.
err = k.Connect(ctx, control.RepoOptions{}) err = k.Connect(ctx, repository.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
err = k.Close(ctx) err = k.Close(ctx)
@ -397,7 +397,7 @@ func (suite *WrapperIntegrationSuite) TestSetUserAndHost() {
ctx, flush := tester.NewContext() ctx, flush := tester.NewContext()
defer flush() defer flush()
opts := control.RepoOptions{ opts := repository.Options{
User: "foo", User: "foo",
Host: "bar", Host: "bar",
} }

View File

@ -17,7 +17,7 @@ import (
"github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/model"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control/repository"
) )
type fooModel struct { type fooModel struct {
@ -804,7 +804,7 @@ func openConnAndModelStore(
st := tester.NewPrefixedS3Storage(t) st := tester.NewPrefixedS3Storage(t)
c := NewConn(st) c := NewConn(st)
err := c.Initialize(ctx, control.RepoOptions{}) err := c.Initialize(ctx, repository.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
defer func() { defer func() {
@ -823,7 +823,7 @@ func reconnectToModelStore(
ctx context.Context, //revive:disable-line:context-as-argument ctx context.Context, //revive:disable-line:context-as-argument
c *conn, c *conn,
) *ModelStore { ) *ModelStore {
err := c.Connect(ctx, control.RepoOptions{}) err := c.Connect(ctx, repository.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
defer func() { defer func() {

View File

@ -7,15 +7,19 @@ import (
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/kopia/kopia/fs" "github.com/kopia/kopia/fs"
"github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/maintenance"
"github.com/kopia/kopia/repo/manifest" "github.com/kopia/kopia/repo/manifest"
"github.com/kopia/kopia/snapshot" "github.com/kopia/kopia/snapshot"
"github.com/kopia/kopia/snapshot/policy" "github.com/kopia/kopia/snapshot/policy"
"github.com/kopia/kopia/snapshot/snapshotfs" "github.com/kopia/kopia/snapshot/snapshotfs"
"github.com/kopia/kopia/snapshot/snapshotmaintenance"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/diagnostics" "github.com/alcionai/corso/src/internal/diagnostics"
"github.com/alcionai/corso/src/internal/observe"
"github.com/alcionai/corso/src/internal/stats" "github.com/alcionai/corso/src/internal/stats"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
@ -515,3 +519,128 @@ func isErrEntryNotFound(err error) bool {
return strings.Contains(err.Error(), "entry not found") && return strings.Contains(err.Error(), "entry not found") &&
!strings.Contains(err.Error(), "parent is not a directory") !strings.Contains(err.Error(), "parent is not a directory")
} }
func (w Wrapper) Maintenance(
ctx context.Context,
opts repository.Maintenance,
) error {
kopiaSafety, err := translateSafety(opts.Safety)
if err != nil {
return clues.Wrap(err, "identifying safety level")
}
mode, err := translateMode(opts.Type)
if err != nil {
return clues.Wrap(err, "identifying maintenance mode")
}
currentOwner := w.c.ClientOptions().UsernameAtHost()
ctx = clues.Add(
ctx,
"kopia_safety", kopiaSafety,
"kopia_maintenance_mode", mode,
"force", opts.Force,
"current_local_owner", clues.Hide(currentOwner))
dr, ok := w.c.Repository.(repo.DirectRepository)
if !ok {
return clues.New("unable to get valid handle to repo").WithClues(ctx)
}
// Below write session options pulled from kopia's CLI code that runs
// maintenance.
err = repo.DirectWriteSession(
ctx,
dr,
repo.WriteSessionOptions{
Purpose: "Corso maintenance",
},
func(ctx context.Context, dw repo.DirectRepositoryWriter) error {
params, err := maintenance.GetParams(ctx, w.c)
if err != nil {
return clues.Wrap(err, "getting maintenance user@host").WithClues(ctx)
}
// Need to do some fixup here as the user/host may not have been set.
if len(params.Owner) == 0 || (params.Owner != currentOwner && opts.Force) {
observe.Message(
ctx,
"updating maintenance user@host to ",
clues.Hide(currentOwner))
if err := w.setMaintenanceParams(ctx, dw, params, currentOwner); err != nil {
return clues.Wrap(err, "updating maintenance parameters").
WithClues(ctx)
}
}
ctx = clues.Add(ctx, "expected_owner", clues.Hide(params.Owner))
logger.Ctx(ctx).Info("running kopia maintenance")
err = snapshotmaintenance.Run(ctx, dw, mode, opts.Force, kopiaSafety)
if err != nil {
return clues.Wrap(err, "running kopia maintenance").WithClues(ctx)
}
return nil
})
if err != nil {
return err
}
return nil
}
func translateSafety(
s repository.MaintenanceSafety,
) (maintenance.SafetyParameters, error) {
switch s {
case repository.FullMaintenanceSafety:
return maintenance.SafetyFull, nil
case repository.NoMaintenanceSafety:
return maintenance.SafetyNone, nil
default:
return maintenance.SafetyParameters{}, clues.New("bad safety value").
With("input_safety", s.String())
}
}
func translateMode(t repository.MaintenanceType) (maintenance.Mode, error) {
switch t {
case repository.CompleteMaintenance:
return maintenance.ModeFull, nil
case repository.MetadataMaintenance:
return maintenance.ModeQuick, nil
default:
return maintenance.ModeNone, clues.New("bad maintenance type").
With("input_maintenance_type", t.String())
}
}
// setMaintenanceUserHost sets the user and host for maintenance to the the
// user and host in the kopia config.
func (w Wrapper) setMaintenanceParams(
ctx context.Context,
drw repo.DirectRepositoryWriter,
p *maintenance.Params,
userAtHost string,
) error {
// This will source user/host from the kopia config file or fallback to
// fetching the values from the OS.
p.Owner = userAtHost
// Disable automatic maintenance for now since it can start matching on the
// user/host of at least one machine now.
p.QuickCycle.Enabled = false
p.FullCycle.Enabled = false
err := maintenance.SetParams(ctx, drw, p)
if err != nil {
return clues.Wrap(err, "setting maintenance user/host")
}
return nil
}

View File

@ -10,6 +10,7 @@ import (
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/maintenance"
"github.com/kopia/kopia/repo/manifest" "github.com/kopia/kopia/repo/manifest"
"github.com/kopia/kopia/snapshot" "github.com/kopia/kopia/snapshot"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -23,6 +24,7 @@ import (
"github.com/alcionai/corso/src/internal/data/mock" "github.com/alcionai/corso/src/internal/data/mock"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
@ -143,7 +145,127 @@ func (suite *KopiaUnitSuite) TestCloseWithoutInitDoesNotPanic() {
} }
// --------------- // ---------------
// integration tests that use kopia // integration tests that use kopia.
// ---------------
type BasicKopiaIntegrationSuite struct {
tester.Suite
}
func TestBasicKopiaIntegrationSuite(t *testing.T) {
suite.Run(t, &BasicKopiaIntegrationSuite{
Suite: tester.NewIntegrationSuite(
t,
[][]string{tester.AWSStorageCredEnvs},
),
})
}
// TestMaintenance checks that different username/hostname pairs will or won't
// cause maintenance to run. It treats kopia maintenance as a black box and
// only checks the returned error.
func (suite *BasicKopiaIntegrationSuite) TestMaintenance_FirstRun_NoChanges() {
ctx, flush := tester.NewContext()
defer flush()
t := suite.T()
k, err := openKopiaRepo(t, ctx)
require.NoError(t, err, clues.ToCore(err))
w := &Wrapper{k}
opts := repository.Maintenance{
Safety: repository.FullMaintenanceSafety,
Type: repository.MetadataMaintenance,
}
err = w.Maintenance(ctx, opts)
require.NoError(t, err, clues.ToCore(err))
}
func (suite *BasicKopiaIntegrationSuite) TestMaintenance_WrongUser_NoForce_Fails() {
ctx, flush := tester.NewContext()
defer flush()
t := suite.T()
k, err := openKopiaRepo(t, ctx)
require.NoError(t, err, clues.ToCore(err))
w := &Wrapper{k}
mOpts := repository.Maintenance{
Safety: repository.FullMaintenanceSafety,
Type: repository.MetadataMaintenance,
}
// This will set the user.
err = w.Maintenance(ctx, mOpts)
require.NoError(t, err, clues.ToCore(err))
err = k.Close(ctx)
require.NoError(t, err, clues.ToCore(err))
opts := repository.Options{
User: "foo",
Host: "bar",
}
err = k.Connect(ctx, opts)
require.NoError(t, err, clues.ToCore(err))
var notOwnedErr maintenance.NotOwnedError
err = w.Maintenance(ctx, mOpts)
assert.ErrorAs(t, err, &notOwnedErr, clues.ToCore(err))
}
func (suite *BasicKopiaIntegrationSuite) TestMaintenance_WrongUser_Force_Succeeds() {
ctx, flush := tester.NewContext()
defer flush()
t := suite.T()
k, err := openKopiaRepo(t, ctx)
require.NoError(t, err, clues.ToCore(err))
w := &Wrapper{k}
mOpts := repository.Maintenance{
Safety: repository.FullMaintenanceSafety,
Type: repository.MetadataMaintenance,
}
// This will set the user.
err = w.Maintenance(ctx, mOpts)
require.NoError(t, err, clues.ToCore(err))
err = k.Close(ctx)
require.NoError(t, err, clues.ToCore(err))
opts := repository.Options{
User: "foo",
Host: "bar",
}
err = k.Connect(ctx, opts)
require.NoError(t, err, clues.ToCore(err))
mOpts.Force = true
// This will set the user.
err = w.Maintenance(ctx, mOpts)
require.NoError(t, err, clues.ToCore(err))
mOpts.Force = false
// Running without force should succeed now.
err = w.Maintenance(ctx, mOpts)
require.NoError(t, err, clues.ToCore(err))
}
// ---------------
// integration tests that use kopia and initialize a repo
// --------------- // ---------------
type KopiaIntegrationSuite struct { type KopiaIntegrationSuite struct {
tester.Suite tester.Suite

View File

@ -41,6 +41,7 @@ import (
"github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
@ -83,7 +84,7 @@ func prepNewTestBackupOp(
k = kopia.NewConn(st) k = kopia.NewConn(st)
) )
err := k.Initialize(ctx, control.RepoOptions{}) err := k.Initialize(ctx, repository.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
// kopiaRef comes with a count of 1 and Wrapper bumps it again so safe // kopiaRef comes with a count of 1 and Wrapper bumps it again so safe

View File

@ -28,6 +28,7 @@ import (
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/store" "github.com/alcionai/corso/src/pkg/store"
) )
@ -175,7 +176,7 @@ func (suite *RestoreOpIntegrationSuite) SetupSuite() {
suite.acct = tester.NewM365Account(t) suite.acct = tester.NewM365Account(t)
err := k.Initialize(ctx, control.RepoOptions{}) err := k.Initialize(ctx, repository.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
suite.kopiaCloser = func(ctx context.Context) { suite.kopiaCloser = func(ctx context.Context) {

View File

@ -12,7 +12,7 @@ import (
"github.com/alcionai/corso/src/internal/kopia" "github.com/alcionai/corso/src/internal/kopia"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
) )
@ -42,7 +42,7 @@ func (suite *StreamStoreIntgSuite) SetupSubTest() {
st := tester.NewPrefixedS3Storage(t) st := tester.NewPrefixedS3Storage(t)
k := kopia.NewConn(st) k := kopia.NewConn(st)
require.NoError(t, k.Initialize(ctx, control.RepoOptions{})) require.NoError(t, k.Initialize(ctx, repository.Options{}))
suite.kcloser = func() { k.Close(ctx) } suite.kcloser = func() { k.Close(ctx) }

View File

@ -2,18 +2,19 @@ package control
import ( import (
"github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/pkg/control/repository"
) )
// Options holds the optional configurations for a process // Options holds the optional configurations for a process
type Options struct { type Options struct {
Collision CollisionPolicy `json:"-"` Collision CollisionPolicy `json:"-"`
DisableMetrics bool `json:"disableMetrics"` DisableMetrics bool `json:"disableMetrics"`
FailureHandling FailureBehavior `json:"failureHandling"` FailureHandling FailureBehavior `json:"failureHandling"`
RestorePermissions bool `json:"restorePermissions"` RestorePermissions bool `json:"restorePermissions"`
SkipReduce bool `json:"skipReduce"` SkipReduce bool `json:"skipReduce"`
ToggleFeatures Toggles `json:"toggleFeatures"` ToggleFeatures Toggles `json:"toggleFeatures"`
Parallelism Parallelism `json:"parallelism"` Parallelism Parallelism `json:"parallelism"`
Repo RepoOptions `json:"repo"` Repo repository.Options `json:"repo"`
} }
type FailureBehavior string type FailureBehavior string
@ -34,12 +35,6 @@ const (
BestEffort FailureBehavior = "best-effort" BestEffort FailureBehavior = "best-effort"
) )
// Repo represents options that are specific to the repo storing backed up data.
type RepoOptions struct {
User string `json:"user"`
Host string `json:"host"`
}
// Defaults provides an Options with the default values set. // Defaults provides an Options with the default values set.
func Defaults() Options { func Defaults() Options {
return Options{ return Options{

View File

@ -0,0 +1,24 @@
// Code generated by "stringer -type=MaintenanceSafety -linecomment"; DO NOT EDIT.
package repository
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[FullMaintenanceSafety-0]
_ = x[NoMaintenanceSafety-1]
}
const _MaintenanceSafety_name = "FullMaintenanceSafetyNoMaintenanceSafety"
var _MaintenanceSafety_index = [...]uint8{0, 21, 40}
func (i MaintenanceSafety) String() string {
if i < 0 || i >= MaintenanceSafety(len(_MaintenanceSafety_index)-1) {
return "MaintenanceSafety(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _MaintenanceSafety_name[_MaintenanceSafety_index[i]:_MaintenanceSafety_index[i+1]]
}

View File

@ -0,0 +1,24 @@
// Code generated by "stringer -type=MaintenanceType -linecomment"; DO NOT EDIT.
package repository
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[CompleteMaintenance-0]
_ = x[MetadataMaintenance-1]
}
const _MaintenanceType_name = "completemetadata"
var _MaintenanceType_index = [...]uint8{0, 8, 16}
func (i MaintenanceType) String() string {
if i < 0 || i >= MaintenanceType(len(_MaintenanceType_index)-1) {
return "MaintenanceType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _MaintenanceType_name[_MaintenanceType_index[i]:_MaintenanceType_index[i+1]]
}

View File

@ -0,0 +1,37 @@
package repository
// Repo represents options that are specific to the repo storing backed up data.
type Options struct {
User string `json:"user"`
Host string `json:"host"`
}
type Maintenance struct {
Type MaintenanceType `json:"type"`
Safety MaintenanceSafety `json:"safety"`
Force bool `json:"force"`
}
// ---------------------------------------------------------------------------
// Maintenance flags
// ---------------------------------------------------------------------------
type MaintenanceType int
// Can't be reordered as we rely on iota for numbering.
//
//go:generate stringer -type=MaintenanceType -linecomment
const (
CompleteMaintenance MaintenanceType = iota // complete
MetadataMaintenance // metadata
)
type MaintenanceSafety int
// Can't be reordered as we rely on iota for numbering.
//
//go:generate stringer -type=MaintenanceSafety -linecomment
const (
FullMaintenanceSafety MaintenanceSafety = iota
NoMaintenanceSafety
)

View File

@ -20,7 +20,7 @@ import (
"github.com/alcionai/corso/src/internal/version" "github.com/alcionai/corso/src/internal/version"
"github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control" rep "github.com/alcionai/corso/src/pkg/control/repository"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
@ -237,10 +237,10 @@ func (suite *RepositoryModelIntgSuite) SetupSuite() {
require.NotNil(t, k) require.NotNil(t, k)
err = k.Initialize(ctx, control.RepoOptions{}) err = k.Initialize(ctx, rep.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
err = k.Connect(ctx, control.RepoOptions{}) err = k.Connect(ctx, rep.Options{})
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
suite.kopiaCloser = func(ctx context.Context) { suite.kopiaCloser = func(ctx context.Context) {
@ -287,8 +287,8 @@ func (suite *RepositoryModelIntgSuite) TestGetRepositoryModel() {
k = kopia.NewConn(s) k = kopia.NewConn(s)
) )
require.NoError(t, k.Initialize(ctx, control.RepoOptions{})) require.NoError(t, k.Initialize(ctx, rep.Options{}))
require.NoError(t, k.Connect(ctx, control.RepoOptions{})) require.NoError(t, k.Connect(ctx, rep.Options{}))
defer k.Close(ctx) defer k.Close(ctx)