diff --git a/src/go.mod b/src/go.mod index 6ed7b775d..5d4031704 100644 --- a/src/go.mod +++ b/src/go.mod @@ -44,6 +44,7 @@ require ( github.com/andybalholm/brotli v1.0.4 // indirect github.com/dnaeon/go-vcr v1.2.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/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/src/go.sum b/src/go.sum index fe76814d3..fb5e3f84a 100644 --- a/src/go.sum +++ b/src/go.sum @@ -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/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/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 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/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= diff --git a/src/internal/kopia/conn.go b/src/internal/kopia/conn.go index c04875f75..09d4a34ff 100644 --- a/src/internal/kopia/conn.go +++ b/src/internal/kopia/conn.go @@ -18,7 +18,7 @@ import ( "github.com/kopia/kopia/snapshot/snapshotfs" "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" ) @@ -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) if err != nil { return clues.Wrap(err, "initializing storage") @@ -110,7 +110,7 @@ func (w *conn) Initialize(ctx context.Context, opts control.RepoOptions) error { 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) if err != nil { 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( ctx context.Context, - opts control.RepoOptions, + opts repository.Options, configDir string, bst blob.Storage, password, compressor string, diff --git a/src/internal/kopia/conn_test.go b/src/internal/kopia/conn_test.go index 2a8d06d49..fd619f6da 100644 --- a/src/internal/kopia/conn_test.go +++ b/src/internal/kopia/conn_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/suite" "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" ) @@ -25,7 +25,7 @@ func openKopiaRepo( st := tester.NewPrefixedS3Storage(t) k := NewConn(st) - if err := k.Initialize(ctx, control.RepoOptions{}); err != nil { + if err := k.Initialize(ctx, repository.Options{}); err != nil { return nil, err } @@ -79,13 +79,13 @@ func (suite *WrapperIntegrationSuite) TestRepoExistsError() { st := tester.NewPrefixedS3Storage(t) k := NewConn(st) - err := k.Initialize(ctx, control.RepoOptions{}) + err := k.Initialize(ctx, repository.Options{}) require.NoError(t, err, clues.ToCore(err)) err = k.Close(ctx) 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.ErrorIs(t, err, ErrorRepoAlreadyExists) } @@ -99,7 +99,7 @@ func (suite *WrapperIntegrationSuite) TestBadProviderErrors() { st.Provider = storage.ProviderUnknown k := NewConn(st) - err := k.Initialize(ctx, control.RepoOptions{}) + err := k.Initialize(ctx, repository.Options{}) assert.Error(t, err, clues.ToCore(err)) } @@ -111,7 +111,7 @@ func (suite *WrapperIntegrationSuite) TestConnectWithoutInitErrors() { st := tester.NewPrefixedS3Storage(t) k := NewConn(st) - err := k.Connect(ctx, control.RepoOptions{}) + err := k.Connect(ctx, repository.Options{}) assert.Error(t, err, clues.ToCore(err)) } @@ -358,7 +358,7 @@ func (suite *WrapperIntegrationSuite) TestConfigDefaultsSetOnInitAndNotOnConnect err = k.Close(ctx) 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)) defer func() { @@ -386,7 +386,7 @@ func (suite *WrapperIntegrationSuite) TestInitAndConnWithTempDirectory() { require.NoError(t, err, clues.ToCore(err)) // Re-open with Connect. - err = k.Connect(ctx, control.RepoOptions{}) + err = k.Connect(ctx, repository.Options{}) require.NoError(t, err, clues.ToCore(err)) err = k.Close(ctx) @@ -397,7 +397,7 @@ func (suite *WrapperIntegrationSuite) TestSetUserAndHost() { ctx, flush := tester.NewContext() defer flush() - opts := control.RepoOptions{ + opts := repository.Options{ User: "foo", Host: "bar", } diff --git a/src/internal/kopia/model_store_test.go b/src/internal/kopia/model_store_test.go index 4922dbe95..b5bf76bcd 100644 --- a/src/internal/kopia/model_store_test.go +++ b/src/internal/kopia/model_store_test.go @@ -17,7 +17,7 @@ import ( "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/tester" "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 { @@ -804,7 +804,7 @@ func openConnAndModelStore( st := tester.NewPrefixedS3Storage(t) c := NewConn(st) - err := c.Initialize(ctx, control.RepoOptions{}) + err := c.Initialize(ctx, repository.Options{}) require.NoError(t, err, clues.ToCore(err)) defer func() { @@ -823,7 +823,7 @@ func reconnectToModelStore( ctx context.Context, //revive:disable-line:context-as-argument c *conn, ) *ModelStore { - err := c.Connect(ctx, control.RepoOptions{}) + err := c.Connect(ctx, repository.Options{}) require.NoError(t, err, clues.ToCore(err)) defer func() { diff --git a/src/internal/kopia/wrapper.go b/src/internal/kopia/wrapper.go index 293b8df02..ff7280d1b 100644 --- a/src/internal/kopia/wrapper.go +++ b/src/internal/kopia/wrapper.go @@ -7,15 +7,19 @@ import ( "github.com/alcionai/clues" "github.com/kopia/kopia/fs" "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/maintenance" "github.com/kopia/kopia/repo/manifest" "github.com/kopia/kopia/snapshot" "github.com/kopia/kopia/snapshot/policy" "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/diagnostics" + "github.com/alcionai/corso/src/internal/observe" "github.com/alcionai/corso/src/internal/stats" "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/logger" "github.com/alcionai/corso/src/pkg/path" @@ -515,3 +519,128 @@ func isErrEntryNotFound(err error) bool { return strings.Contains(err.Error(), "entry not found") && !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 +} diff --git a/src/internal/kopia/wrapper_test.go b/src/internal/kopia/wrapper_test.go index 1da2f1a84..89e5b134b 100644 --- a/src/internal/kopia/wrapper_test.go +++ b/src/internal/kopia/wrapper_test.go @@ -10,6 +10,7 @@ import ( "github.com/alcionai/clues" "github.com/google/uuid" "github.com/kopia/kopia/repo" + "github.com/kopia/kopia/repo/maintenance" "github.com/kopia/kopia/repo/manifest" "github.com/kopia/kopia/snapshot" "github.com/stretchr/testify/assert" @@ -23,6 +24,7 @@ import ( "github.com/alcionai/corso/src/internal/data/mock" "github.com/alcionai/corso/src/internal/tester" "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/logger" "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, ¬OwnedErr, 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 { tester.Suite diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_integration_test.go index 5fdda1f71..abe89e56e 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_integration_test.go @@ -41,6 +41,7 @@ import ( "github.com/alcionai/corso/src/pkg/backup" "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/path" "github.com/alcionai/corso/src/pkg/selectors" @@ -83,7 +84,7 @@ func prepNewTestBackupOp( k = kopia.NewConn(st) ) - err := k.Initialize(ctx, control.RepoOptions{}) + err := k.Initialize(ctx, repository.Options{}) require.NoError(t, err, clues.ToCore(err)) // kopiaRef comes with a count of 1 and Wrapper bumps it again so safe diff --git a/src/internal/operations/restore_test.go b/src/internal/operations/restore_test.go index 5eb06dabb..c221fe4dc 100644 --- a/src/internal/operations/restore_test.go +++ b/src/internal/operations/restore_test.go @@ -28,6 +28,7 @@ import ( "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/pkg/account" "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/store" ) @@ -175,7 +176,7 @@ func (suite *RestoreOpIntegrationSuite) SetupSuite() { suite.acct = tester.NewM365Account(t) - err := k.Initialize(ctx, control.RepoOptions{}) + err := k.Initialize(ctx, repository.Options{}) require.NoError(t, err, clues.ToCore(err)) suite.kopiaCloser = func(ctx context.Context) { diff --git a/src/internal/streamstore/collectables_test.go b/src/internal/streamstore/collectables_test.go index 640aa6406..6cefb804a 100644 --- a/src/internal/streamstore/collectables_test.go +++ b/src/internal/streamstore/collectables_test.go @@ -12,7 +12,7 @@ import ( "github.com/alcionai/corso/src/internal/kopia" "github.com/alcionai/corso/src/internal/tester" "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/path" ) @@ -42,7 +42,7 @@ func (suite *StreamStoreIntgSuite) SetupSubTest() { st := tester.NewPrefixedS3Storage(t) 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) } diff --git a/src/pkg/control/options.go b/src/pkg/control/options.go index 94b2e316c..d805f8bf3 100644 --- a/src/pkg/control/options.go +++ b/src/pkg/control/options.go @@ -2,18 +2,19 @@ package control import ( "github.com/alcionai/corso/src/internal/common" + "github.com/alcionai/corso/src/pkg/control/repository" ) // Options holds the optional configurations for a process type Options struct { - Collision CollisionPolicy `json:"-"` - DisableMetrics bool `json:"disableMetrics"` - FailureHandling FailureBehavior `json:"failureHandling"` - RestorePermissions bool `json:"restorePermissions"` - SkipReduce bool `json:"skipReduce"` - ToggleFeatures Toggles `json:"toggleFeatures"` - Parallelism Parallelism `json:"parallelism"` - Repo RepoOptions `json:"repo"` + Collision CollisionPolicy `json:"-"` + DisableMetrics bool `json:"disableMetrics"` + FailureHandling FailureBehavior `json:"failureHandling"` + RestorePermissions bool `json:"restorePermissions"` + SkipReduce bool `json:"skipReduce"` + ToggleFeatures Toggles `json:"toggleFeatures"` + Parallelism Parallelism `json:"parallelism"` + Repo repository.Options `json:"repo"` } type FailureBehavior string @@ -34,12 +35,6 @@ const ( 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. func Defaults() Options { return Options{ diff --git a/src/pkg/control/repository/maintenancesafety_string.go b/src/pkg/control/repository/maintenancesafety_string.go new file mode 100644 index 000000000..789bd918a --- /dev/null +++ b/src/pkg/control/repository/maintenancesafety_string.go @@ -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]] +} diff --git a/src/pkg/control/repository/maintenancetype_string.go b/src/pkg/control/repository/maintenancetype_string.go new file mode 100644 index 000000000..fea525c93 --- /dev/null +++ b/src/pkg/control/repository/maintenancetype_string.go @@ -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]] +} diff --git a/src/pkg/control/repository/repo.go b/src/pkg/control/repository/repo.go new file mode 100644 index 000000000..6153b7422 --- /dev/null +++ b/src/pkg/control/repository/repo.go @@ -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 +) diff --git a/src/pkg/repository/repository_unexported_test.go b/src/pkg/repository/repository_unexported_test.go index 6d05a7b48..92fbd86d5 100644 --- a/src/pkg/repository/repository_unexported_test.go +++ b/src/pkg/repository/repository_unexported_test.go @@ -20,7 +20,7 @@ import ( "github.com/alcionai/corso/src/internal/version" "github.com/alcionai/corso/src/pkg/backup" "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/path" "github.com/alcionai/corso/src/pkg/selectors" @@ -237,10 +237,10 @@ func (suite *RepositoryModelIntgSuite) SetupSuite() { require.NotNil(t, k) - err = k.Initialize(ctx, control.RepoOptions{}) + err = k.Initialize(ctx, rep.Options{}) 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)) suite.kopiaCloser = func(ctx context.Context) { @@ -287,8 +287,8 @@ func (suite *RepositoryModelIntgSuite) TestGetRepositoryModel() { k = kopia.NewConn(s) ) - require.NoError(t, k.Initialize(ctx, control.RepoOptions{})) - require.NoError(t, k.Connect(ctx, control.RepoOptions{})) + require.NoError(t, k.Initialize(ctx, rep.Options{})) + require.NoError(t, k.Connect(ctx, rep.Options{})) defer k.Close(ctx)