Allow overriding the default username/hostname in kopia (#3223)
This allows setting the username/hostname kopia will use for maintenance. This information is recorded in the kopia config file during connect and reused during open If no values are given, kopia pulls the values from the OS --- #### 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:
parent
754e14d7a6
commit
6ac8c9f331
@ -18,6 +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/storage"
|
"github.com/alcionai/corso/src/pkg/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ func NewConn(s storage.Storage) *conn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *conn) Initialize(ctx context.Context) error {
|
func (w *conn) Initialize(ctx context.Context, opts control.RepoOptions) 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")
|
||||||
@ -92,6 +93,7 @@ func (w *conn) Initialize(ctx context.Context) error {
|
|||||||
|
|
||||||
err = w.commonConnect(
|
err = w.commonConnect(
|
||||||
ctx,
|
ctx,
|
||||||
|
opts,
|
||||||
cfg.KopiaCfgDir,
|
cfg.KopiaCfgDir,
|
||||||
bst,
|
bst,
|
||||||
cfg.CorsoPassphrase,
|
cfg.CorsoPassphrase,
|
||||||
@ -108,7 +110,7 @@ func (w *conn) Initialize(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *conn) Connect(ctx context.Context) error {
|
func (w *conn) Connect(ctx context.Context, opts control.RepoOptions) 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")
|
||||||
@ -122,6 +124,7 @@ func (w *conn) Connect(ctx context.Context) error {
|
|||||||
|
|
||||||
return w.commonConnect(
|
return w.commonConnect(
|
||||||
ctx,
|
ctx,
|
||||||
|
opts,
|
||||||
cfg.KopiaCfgDir,
|
cfg.KopiaCfgDir,
|
||||||
bst,
|
bst,
|
||||||
cfg.CorsoPassphrase,
|
cfg.CorsoPassphrase,
|
||||||
@ -131,16 +134,21 @@ func (w *conn) Connect(ctx context.Context) error {
|
|||||||
|
|
||||||
func (w *conn) commonConnect(
|
func (w *conn) commonConnect(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
opts control.RepoOptions,
|
||||||
configDir string,
|
configDir string,
|
||||||
bst blob.Storage,
|
bst blob.Storage,
|
||||||
password, compressor string,
|
password, compressor string,
|
||||||
) error {
|
) error {
|
||||||
var opts *repo.ConnectOptions
|
kopiaOpts := &repo.ConnectOptions{
|
||||||
|
ClientOptions: repo.ClientOptions{
|
||||||
|
Username: opts.User,
|
||||||
|
Hostname: opts.Host,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
if len(configDir) > 0 {
|
if len(configDir) > 0 {
|
||||||
opts = &repo.ConnectOptions{
|
kopiaOpts.CachingOptions = content.CachingOptions{
|
||||||
CachingOptions: content.CachingOptions{
|
CacheDirectory: configDir,
|
||||||
CacheDirectory: configDir,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
configDir = defaultKopiaConfigDir
|
configDir = defaultKopiaConfigDir
|
||||||
@ -154,7 +162,7 @@ func (w *conn) commonConnect(
|
|||||||
cfgFile,
|
cfgFile,
|
||||||
bst,
|
bst,
|
||||||
password,
|
password,
|
||||||
opts,
|
kopiaOpts,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return clues.Wrap(err, "connecting to repo").WithClues(ctx)
|
return clues.Wrap(err, "connecting to repo").WithClues(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,16 +14,18 @@ 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/storage"
|
"github.com/alcionai/corso/src/pkg/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
//revive:disable:context-as-argument
|
func openKopiaRepo(
|
||||||
func openKopiaRepo(t *testing.T, ctx context.Context) (*conn, error) {
|
t *testing.T,
|
||||||
//revive:enable:context-as-argument
|
ctx context.Context, //revive:disable-line:context-as-argument
|
||||||
|
) (*conn, error) {
|
||||||
st := tester.NewPrefixedS3Storage(t)
|
st := tester.NewPrefixedS3Storage(t)
|
||||||
|
|
||||||
k := NewConn(st)
|
k := NewConn(st)
|
||||||
if err := k.Initialize(ctx); err != nil {
|
if err := k.Initialize(ctx, control.RepoOptions{}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,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)
|
err := k.Initialize(ctx, control.RepoOptions{})
|
||||||
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)
|
err = k.Initialize(ctx, control.RepoOptions{})
|
||||||
assert.Error(t, err, clues.ToCore(err))
|
assert.Error(t, err, clues.ToCore(err))
|
||||||
assert.ErrorIs(t, err, ErrorRepoAlreadyExists)
|
assert.ErrorIs(t, err, ErrorRepoAlreadyExists)
|
||||||
}
|
}
|
||||||
@ -97,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)
|
err := k.Initialize(ctx, control.RepoOptions{})
|
||||||
assert.Error(t, err, clues.ToCore(err))
|
assert.Error(t, err, clues.ToCore(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,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)
|
err := k.Connect(ctx, control.RepoOptions{})
|
||||||
assert.Error(t, err, clues.ToCore(err))
|
assert.Error(t, err, clues.ToCore(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,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)
|
err = k.Connect(ctx, control.RepoOptions{})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -384,9 +386,63 @@ 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)
|
err = k.Connect(ctx, control.RepoOptions{})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = k.Close(ctx)
|
err = k.Close(ctx)
|
||||||
assert.NoError(t, err, clues.ToCore(err))
|
assert.NoError(t, err, clues.ToCore(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *WrapperIntegrationSuite) TestSetUserAndHost() {
|
||||||
|
ctx, flush := tester.NewContext()
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
opts := control.RepoOptions{
|
||||||
|
User: "foo",
|
||||||
|
Host: "bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
t := suite.T()
|
||||||
|
st := tester.NewPrefixedS3Storage(t)
|
||||||
|
k := NewConn(st)
|
||||||
|
|
||||||
|
err := k.Initialize(ctx, opts)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
kopiaOpts := k.ClientOptions()
|
||||||
|
require.Equal(t, opts.User, kopiaOpts.Username)
|
||||||
|
require.Equal(t, opts.Host, kopiaOpts.Hostname)
|
||||||
|
|
||||||
|
err = k.Close(ctx)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
// Re-open with Connect and a different user/hostname.
|
||||||
|
opts.User = "hello"
|
||||||
|
opts.Host = "world"
|
||||||
|
|
||||||
|
err = k.Connect(ctx, opts)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
kopiaOpts = k.ClientOptions()
|
||||||
|
require.Equal(t, opts.User, kopiaOpts.Username)
|
||||||
|
require.Equal(t, opts.Host, kopiaOpts.Hostname)
|
||||||
|
|
||||||
|
err = k.Close(ctx)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
// Make sure not setting the values uses the kopia defaults.
|
||||||
|
opts.User = ""
|
||||||
|
opts.Host = ""
|
||||||
|
|
||||||
|
err = k.Connect(ctx, opts)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
kopiaOpts = k.ClientOptions()
|
||||||
|
assert.NotEmpty(t, kopiaOpts.Username)
|
||||||
|
assert.NotEqual(t, "hello", kopiaOpts.Username)
|
||||||
|
assert.NotEmpty(t, kopiaOpts.Hostname)
|
||||||
|
assert.NotEqual(t, "world", kopiaOpts.Hostname)
|
||||||
|
|
||||||
|
err = k.Close(ctx)
|
||||||
|
assert.NoError(t, err, clues.ToCore(err))
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fooModel struct {
|
type fooModel struct {
|
||||||
@ -803,7 +804,7 @@ func openConnAndModelStore(
|
|||||||
st := tester.NewPrefixedS3Storage(t)
|
st := tester.NewPrefixedS3Storage(t)
|
||||||
c := NewConn(st)
|
c := NewConn(st)
|
||||||
|
|
||||||
err := c.Initialize(ctx)
|
err := c.Initialize(ctx, control.RepoOptions{})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -822,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)
|
err := c.Connect(ctx, control.RepoOptions{})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@ -83,7 +83,7 @@ func prepNewTestBackupOp(
|
|||||||
k = kopia.NewConn(st)
|
k = kopia.NewConn(st)
|
||||||
)
|
)
|
||||||
|
|
||||||
err := k.Initialize(ctx)
|
err := k.Initialize(ctx, control.RepoOptions{})
|
||||||
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
|
||||||
|
|||||||
@ -175,7 +175,7 @@ func (suite *RestoreOpIntegrationSuite) SetupSuite() {
|
|||||||
|
|
||||||
suite.acct = tester.NewM365Account(t)
|
suite.acct = tester.NewM365Account(t)
|
||||||
|
|
||||||
err := k.Initialize(ctx)
|
err := k.Initialize(ctx, control.RepoOptions{})
|
||||||
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) {
|
||||||
|
|||||||
@ -12,6 +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/fault"
|
"github.com/alcionai/corso/src/pkg/fault"
|
||||||
"github.com/alcionai/corso/src/pkg/path"
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
)
|
)
|
||||||
@ -41,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))
|
require.NoError(t, k.Initialize(ctx, control.RepoOptions{}))
|
||||||
|
|
||||||
suite.kcloser = func() { k.Close(ctx) }
|
suite.kcloser = func() { k.Close(ctx) }
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ type Options struct {
|
|||||||
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FailureBehavior string
|
type FailureBehavior string
|
||||||
@ -33,6 +34,12 @@ 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{
|
||||||
|
|||||||
@ -120,7 +120,7 @@ func Initialize(
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
kopiaRef := kopia.NewConn(s)
|
kopiaRef := kopia.NewConn(s)
|
||||||
if err := kopiaRef.Initialize(ctx); err != nil {
|
if err := kopiaRef.Initialize(ctx, opts.Repo); err != nil {
|
||||||
// replace common internal errors so that sdk users can check results with errors.Is()
|
// replace common internal errors so that sdk users can check results with errors.Is()
|
||||||
if errors.Is(err, kopia.ErrorRepoAlreadyExists) {
|
if errors.Is(err, kopia.ErrorRepoAlreadyExists) {
|
||||||
return nil, clues.Stack(ErrorRepoAlreadyExists, err).WithClues(ctx)
|
return nil, clues.Stack(ErrorRepoAlreadyExists, err).WithClues(ctx)
|
||||||
@ -202,7 +202,7 @@ func Connect(
|
|||||||
defer close(complete)
|
defer close(complete)
|
||||||
|
|
||||||
kopiaRef := kopia.NewConn(s)
|
kopiaRef := kopia.NewConn(s)
|
||||||
if err := kopiaRef.Connect(ctx); err != nil {
|
if err := kopiaRef.Connect(ctx, opts.Repo); err != nil {
|
||||||
return nil, clues.Wrap(err, "connecting kopia client")
|
return nil, clues.Wrap(err, "connecting kopia client")
|
||||||
}
|
}
|
||||||
// kopiaRef comes with a count of 1 and NewWrapper/NewModelStore bumps it again so safe
|
// kopiaRef comes with a count of 1 and NewWrapper/NewModelStore bumps it again so safe
|
||||||
|
|||||||
@ -20,6 +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"
|
||||||
"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"
|
||||||
@ -236,10 +237,10 @@ func (suite *RepositoryModelIntgSuite) SetupSuite() {
|
|||||||
|
|
||||||
require.NotNil(t, k)
|
require.NotNil(t, k)
|
||||||
|
|
||||||
err = k.Initialize(ctx)
|
err = k.Initialize(ctx, control.RepoOptions{})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = k.Connect(ctx)
|
err = k.Connect(ctx, control.RepoOptions{})
|
||||||
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) {
|
||||||
@ -286,8 +287,8 @@ func (suite *RepositoryModelIntgSuite) TestGetRepositoryModel() {
|
|||||||
k = kopia.NewConn(s)
|
k = kopia.NewConn(s)
|
||||||
)
|
)
|
||||||
|
|
||||||
require.NoError(t, k.Initialize(ctx))
|
require.NoError(t, k.Initialize(ctx, control.RepoOptions{}))
|
||||||
require.NoError(t, k.Connect(ctx))
|
require.NoError(t, k.Connect(ctx, control.RepoOptions{}))
|
||||||
|
|
||||||
defer k.Close(ctx)
|
defer k.Close(ctx)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user