diff --git a/src/internal/events/events.go b/src/internal/events/events.go index ce2ec5f18..414dd5a7f 100644 --- a/src/internal/events/events.go +++ b/src/internal/events/events.go @@ -37,6 +37,7 @@ const ( ItemsRead = "items-read" ItemsWritten = "items-written" Resources = "resources" + RestoreID = "restore-id" Service = "service" StartTime = "start-time" Status = "status" diff --git a/src/internal/kopia/model_store.go b/src/internal/kopia/model_store.go index ca6a9907a..6f704a560 100644 --- a/src/internal/kopia/model_store.go +++ b/src/internal/kopia/model_store.go @@ -107,7 +107,7 @@ func putInner( } base := m.Base() - if create { + if create && len(base.ID) == 0 { base.ID = model.StableID(uuid.NewString()) } diff --git a/src/internal/kopia/model_store_test.go b/src/internal/kopia/model_store_test.go index 6dfda305a..5135b9616 100644 --- a/src/internal/kopia/model_store_test.go +++ b/src/internal/kopia/model_store_test.go @@ -260,6 +260,54 @@ func (suite *ModelStoreIntegrationSuite) TestPutGet() { } } +func (suite *ModelStoreIntegrationSuite) TestPutGet_PreSetID() { + mdl := model.BackupOpSchema + table := []struct { + name string + baseID string + expect assert.ComparisonAssertionFunc + }{ + { + name: "genreate new id", + baseID: "", + expect: assert.NotEqual, + }, + { + name: "use provided id", + baseID: uuid.NewString(), + expect: assert.Equal, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + foo := &fooModel{ + BaseModel: model.BaseModel{ID: model.StableID(test.baseID)}, + Bar: uuid.NewString(), + } + + // Avoid some silly test errors from comparing nil to empty map. + foo.Tags = map[string]string{} + + err := suite.m.Put(suite.ctx, mdl, foo) + require.NoError(t, err) + + test.expect(t, model.StableID(test.baseID), foo.ID) + require.NotEmpty(t, foo.ModelStoreID) + require.NotEmpty(t, foo.ID) + + returned := &fooModel{} + err = suite.m.Get(suite.ctx, mdl, foo.ID, returned) + require.NoError(t, err) + assert.Equal(t, foo, returned) + + err = suite.m.GetWithModelStoreID(suite.ctx, mdl, foo.ModelStoreID, returned) + require.NoError(t, err) + assert.Equal(t, foo, returned) + }) + } +} + func (suite *ModelStoreIntegrationSuite) TestPutGet_WithTags() { t := suite.T() theModelType := model.BackupOpSchema diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 448198597..4da66d6b7 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/google/uuid" multierror "github.com/hashicorp/go-multierror" "github.com/pkg/errors" @@ -87,7 +88,8 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { backupDetails *details.Details startTime = time.Now() ) - // TODO: persist initial state of backupOperation in modelstore + + op.Results.BackupID = model.StableID(uuid.NewString()) op.bus.Event( ctx, @@ -95,7 +97,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { map[string]any{ events.StartTime: startTime, events.Service: op.Selectors.Service.String(), - // TODO: initial backup ID, + events.BackupID: op.Results.BackupID, }, ) @@ -195,6 +197,7 @@ func (op *BackupOperation) createBackupModels( b := backup.New( snapID, string(backupDetails.ModelStoreID), op.Status.String(), + op.Results.BackupID, op.Selectors, op.Results.ReadWrites, op.Results.StartAndEndTime, @@ -205,20 +208,18 @@ func (op *BackupOperation) createBackupModels( return errors.Wrap(err, "creating backup model") } - op.Results.BackupID = b.ID - op.bus.Event( ctx, events.BackupEnd, map[string]any{ events.BackupID: b.ID, - events.Service: op.Selectors.Service.String(), - events.Status: op.Status, - events.StartTime: op.Results.StartedAt, - events.EndTime: op.Results.CompletedAt, - events.Duration: op.Results.CompletedAt.Sub(op.Results.StartedAt), events.DataStored: op.Results.BytesUploaded, + events.Duration: op.Results.CompletedAt.Sub(op.Results.StartedAt), + events.EndTime: op.Results.CompletedAt, events.Resources: op.Results.ResourceOwners, + events.Service: op.Selectors.Service.String(), + events.StartTime: op.Results.StartedAt, + events.Status: op.Status, // TODO: events.ExchangeDataObserved: , }, ) diff --git a/src/internal/operations/backup_test.go b/src/internal/operations/backup_test.go index 70092b833..c04f72f5c 100644 --- a/src/internal/operations/backup_test.go +++ b/src/internal/operations/backup_test.go @@ -227,6 +227,9 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run() { assert.Zero(t, bo.Results.WriteErrors) assert.Equal(t, 1, mb.TimesCalled[events.BackupStart], "backup-start events") assert.Equal(t, 1, mb.TimesCalled[events.BackupEnd], "backup-end events") + assert.Equal(t, + mb.CalledWith[events.BackupStart][0][events.BackupID], + bo.Results.BackupID, "backupID pre-declaration") }) } } @@ -287,4 +290,7 @@ func (suite *BackupOpIntegrationSuite) TestBackupOneDrive_Run() { assert.NoError(t, bo.Results.WriteErrors) assert.Equal(t, 1, mb.TimesCalled[events.BackupStart], "backup-start events") assert.Equal(t, 1, mb.TimesCalled[events.BackupEnd], "backup-end events") + assert.Equal(t, + mb.CalledWith[events.BackupStart][0][events.BackupID], + bo.Results.BackupID, "backupID pre-declaration") } diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index fc4281b6d..173e6073c 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/google/uuid" multierror "github.com/hashicorp/go-multierror" "github.com/pkg/errors" @@ -82,6 +83,9 @@ type restoreStats struct { resourceCount int started bool readErr, writeErr error + + // a transient value only used to pair up start-end events. + restoreID string } // Run begins a synchronous restore operation. @@ -91,8 +95,8 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) { // persist operation results to the model store on exit opStats := restoreStats{ bytesRead: &stats.ByteCounter{}, + restoreID: uuid.NewString(), } - // TODO: persist results? defer func() { err = op.persistResults(ctx, startTime, &opStats) @@ -110,7 +114,6 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) { return err } - // TODO: persist initial state of restoreOperation in modelstore op.bus.Event( ctx, events.RestoreStart, @@ -118,6 +121,7 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) { events.StartTime: startTime, events.BackupID: op.BackupID, events.BackupCreateTime: b.CreationTime, + events.RestoreID: opStats.restoreID, // TODO: restore options, }, ) @@ -237,15 +241,17 @@ func (op *RestoreOperation) persistResults( map[string]any{ // TODO: RestoreID events.BackupID: op.BackupID, - events.Service: op.Selectors.Service.String(), - events.Status: op.Status, - events.StartTime: op.Results.StartedAt, - events.EndTime: op.Results.CompletedAt, + events.DataRetrieved: op.Results.BytesRead, events.Duration: op.Results.CompletedAt.Sub(op.Results.StartedAt), + events.EndTime: op.Results.CompletedAt, events.ItemsRead: op.Results.ItemsRead, events.ItemsWritten: op.Results.ItemsWritten, events.Resources: op.Results.ResourceOwners, - events.DataRetrieved: op.Results.BytesRead, + events.RestoreID: opStats.restoreID, + events.Service: op.Selectors.Service.String(), + events.StartTime: op.Results.StartedAt, + events.Status: op.Status, + // TODO: events.ExchangeDataObserved: , }, ) diff --git a/src/pkg/backup/backup.go b/src/pkg/backup/backup.go index b60c35b4e..b75d7e1a2 100644 --- a/src/pkg/backup/backup.go +++ b/src/pkg/backup/backup.go @@ -42,11 +42,15 @@ var _ print.Printable = &Backup{} func New( snapshotID, detailsID, status string, + id model.StableID, selector selectors.Selector, rw stats.ReadWrites, se stats.StartAndEndTime, ) *Backup { return &Backup{ + BaseModel: model.BaseModel{ + ID: id, + }, CreationTime: time.Now(), SnapshotID: snapshotID, DetailsID: detailsID,