add operation results structs (#253)

* add operation results structs

Operations, both backup and restore, need to hold the
results of their operation, and be able to marshal the struct
to json for output.
This commit is contained in:
Keepers 2022-06-29 13:56:13 -06:00 committed by GitHub
parent ed3844b714
commit abc8b44718
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 37 deletions

View File

@ -13,26 +13,34 @@ import (
// BackupOperation wraps an operation with backup-specific props. // BackupOperation wraps an operation with backup-specific props.
type BackupOperation struct { type BackupOperation struct {
operation operation
Version string
Results BackupResults `json:"results"`
Targets []string `json:"selectors"` // todo: replace with Selectors
Version string `json:"version"`
account account.Account account account.Account
}
Targets []string // something for targets/filter/source/app&users/etc // BackupResults aggregate the details of the result of the operation.
type BackupResults struct {
summary
metrics
// todo: RestorePoint RestorePoint
} }
// NewBackupOperation constructs and validates a backup operation. // NewBackupOperation constructs and validates a backup operation.
func NewBackupOperation( func NewBackupOperation(
ctx context.Context, ctx context.Context,
opts OperationOpts, opts Options,
kw *kopia.KopiaWrapper, kw *kopia.KopiaWrapper,
acct account.Account, acct account.Account,
targets []string, targets []string,
) (BackupOperation, error) { ) (BackupOperation, error) {
op := BackupOperation{ op := BackupOperation{
operation: newOperation(opts, kw), operation: newOperation(opts, kw),
Targets: targets,
Version: "v0", Version: "v0",
account: acct, account: acct,
Targets: targets,
} }
if err := op.validate(); err != nil { if err := op.validate(); err != nil {
return BackupOperation{}, err return BackupOperation{}, err

View File

@ -43,20 +43,20 @@ func (suite *BackupOpIntegrationSuite) TestNewBackupOperation() {
table := []struct { table := []struct {
name string name string
opts operations.OperationOpts opts operations.Options
kw *kopia.KopiaWrapper kw *kopia.KopiaWrapper
acct account.Account acct account.Account
targets []string targets []string
errCheck assert.ErrorAssertionFunc errCheck assert.ErrorAssertionFunc
}{ }{
{"good", operations.OperationOpts{}, kw, acct, nil, assert.NoError}, {"good", operations.Options{}, kw, acct, nil, assert.NoError},
{"missing kopia", operations.OperationOpts{}, nil, acct, nil, assert.Error}, {"missing kopia", operations.Options{}, nil, acct, nil, assert.Error},
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
_, err := operations.NewBackupOperation( _, err := operations.NewBackupOperation(
context.Background(), context.Background(),
operations.OperationOpts{}, operations.Options{},
test.kw, test.kw,
test.acct, test.acct,
nil) nil)

View File

@ -4,6 +4,7 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
multierror "github.com/hashicorp/go-multierror"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/alcionai/corso/internal/kopia" "github.com/alcionai/corso/internal/kopia"
@ -18,35 +19,38 @@ const (
Failed Failed
) )
// --------------------------------------------------------------------------------
// Operation Core
// --------------------------------------------------------------------------------
// An operation tracks the in-progress workload of some long-running process. // An operation tracks the in-progress workload of some long-running process.
// Specific processes (eg: backups, restores, etc) are expected to wrap operation // Specific processes (eg: backups, restores, etc) are expected to wrap operation
// with process specific details. // with process specific details.
type operation struct { type operation struct {
ID uuid.UUID // system generated identifier ID uuid.UUID `json:"id"` // system generated identifier
CreatedAt time.Time // datetime of the operation's creation CreatedAt time.Time `json:"createdAt"` // datetime of the operation's creation
Options Options `json:"options"`
Status opStatus `json:"status"`
options OperationOpts kopia *kopia.KopiaWrapper
kopia *kopia.KopiaWrapper
Status opStatus
Errors []error
} }
// OperationOpts configure some parameters of the operation // Options configure some parameters of the operation
type OperationOpts struct { type Options struct {
// todo: collision handling
// todo: fast fail vs best attempt
} }
func newOperation( func newOperation(
opts OperationOpts, opts Options,
kw *kopia.KopiaWrapper, kw *kopia.KopiaWrapper,
) operation { ) operation {
return operation{ return operation{
ID: uuid.New(), ID: uuid.New(),
CreatedAt: time.Now(), CreatedAt: time.Now(),
options: opts, Options: opts,
kopia: kw, kopia: kw,
Status: InProgress, Status: InProgress,
Errors: []error{},
} }
} }
@ -56,3 +60,22 @@ func (op operation) validate() error {
} }
return nil return nil
} }
// --------------------------------------------------------------------------------
// Results
// --------------------------------------------------------------------------------
// Summary tracks the total files touched and errors produced
// during an operation.
type summary struct {
ItemsRead int `json:"itemsRead,omitempty"`
ItemsWritten int `json:"itemsWritten,omitempty"`
ReadErrors multierror.Error `json:"readErrors,omitempty"`
WriteErrors multierror.Error `json:"writeErrors,omitempty"`
}
// Metrics tracks performance details such as timing, throughput, etc.
type metrics struct {
StartedAt time.Time `json:"startedAt"`
CompletedAt time.Time `json:"completedAt"`
}

View File

@ -19,8 +19,7 @@ func TestOperationSuite(t *testing.T) {
func (suite *OperationSuite) TestNewOperation() { func (suite *OperationSuite) TestNewOperation() {
t := suite.T() t := suite.T()
op := newOperation(OperationOpts{}, nil) op := newOperation(Options{}, nil)
assert.NotNil(t, op.Errors)
assert.NotNil(t, op.ID) assert.NotNil(t, op.ID)
} }
@ -36,7 +35,7 @@ func (suite *OperationSuite) TestOperation_Validate() {
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
op := newOperation(OperationOpts{}, test.kw) op := newOperation(Options{}, test.kw)
test.errCheck(t, op.validate()) test.errCheck(t, op.validate())
}) })
} }

View File

@ -13,18 +13,25 @@ import (
// RestoreOperation wraps an operation with restore-specific props. // RestoreOperation wraps an operation with restore-specific props.
type RestoreOperation struct { type RestoreOperation struct {
operation operation
Version string
restorePointID string RestorePointID string `json:"restorePointID"`
account account.Account Results RestoreResults `json:"results"`
Targets []string `json:"selectors"` // todo: replace with Selectors
Version string `json:"bersion"`
Targets []string // something for targets/filter/source/app&users/etc account account.Account
}
// RestoreResults aggregate the details of the results of the operation.
type RestoreResults struct {
summary
metrics
} }
// NewRestoreOperation constructs and validates a restore operation. // NewRestoreOperation constructs and validates a restore operation.
func NewRestoreOperation( func NewRestoreOperation(
ctx context.Context, ctx context.Context,
opts OperationOpts, opts Options,
kw *kopia.KopiaWrapper, kw *kopia.KopiaWrapper,
acct account.Account, acct account.Account,
restorePointID string, restorePointID string,
@ -32,10 +39,10 @@ func NewRestoreOperation(
) (RestoreOperation, error) { ) (RestoreOperation, error) {
op := RestoreOperation{ op := RestoreOperation{
operation: newOperation(opts, kw), operation: newOperation(opts, kw),
RestorePointID: restorePointID,
Targets: targets,
Version: "v0", Version: "v0",
account: acct, account: acct,
restorePointID: restorePointID,
Targets: targets,
} }
if err := op.validate(); err != nil { if err := op.validate(); err != nil {
return RestoreOperation{}, err return RestoreOperation{}, err
@ -51,7 +58,7 @@ func (op RestoreOperation) validate() error {
// Run begins a synchronous restore operation. // Run begins a synchronous restore operation.
// todo (keepers): return stats block in first param. // todo (keepers): return stats block in first param.
func (op *RestoreOperation) Run(ctx context.Context) error { func (op *RestoreOperation) Run(ctx context.Context) error {
dc, err := op.kopia.RestoreSingleItem(ctx, op.restorePointID, op.Targets) dc, err := op.kopia.RestoreSingleItem(ctx, op.RestorePointID, op.Targets)
if err != nil { if err != nil {
return errors.Wrap(err, "retrieving service data") return errors.Wrap(err, "retrieving service data")
} }

View File

@ -37,20 +37,20 @@ func (suite *RestoreOpIntegrationSuite) TestNewRestoreOperation() {
table := []struct { table := []struct {
name string name string
opts operations.OperationOpts opts operations.Options
kw *kopia.KopiaWrapper kw *kopia.KopiaWrapper
acct account.Account acct account.Account
targets []string targets []string
errCheck assert.ErrorAssertionFunc errCheck assert.ErrorAssertionFunc
}{ }{
{"good", operations.OperationOpts{}, kw, acct, nil, assert.NoError}, {"good", operations.Options{}, kw, acct, nil, assert.NoError},
{"missing kopia", operations.OperationOpts{}, nil, acct, nil, assert.Error}, {"missing kopia", operations.Options{}, nil, acct, nil, assert.Error},
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
_, err := operations.NewRestoreOperation( _, err := operations.NewRestoreOperation(
context.Background(), context.Background(),
operations.OperationOpts{}, operations.Options{},
test.kw, test.kw,
test.acct, test.acct,
"restore-point-id", "restore-point-id",

View File

@ -94,7 +94,7 @@ func (r *Repository) Close(ctx context.Context) error {
func (r Repository) NewBackup(ctx context.Context, targets []string) (operations.BackupOperation, error) { func (r Repository) NewBackup(ctx context.Context, targets []string) (operations.BackupOperation, error) {
return operations.NewBackupOperation( return operations.NewBackupOperation(
ctx, ctx,
operations.OperationOpts{}, operations.Options{},
r.dataLayer, r.dataLayer,
r.Account, r.Account,
targets) targets)
@ -104,7 +104,7 @@ func (r Repository) NewBackup(ctx context.Context, targets []string) (operations
func (r Repository) NewRestore(ctx context.Context, restorePointID string, targets []string) (operations.RestoreOperation, error) { func (r Repository) NewRestore(ctx context.Context, restorePointID string, targets []string) (operations.RestoreOperation, error) {
return operations.NewRestoreOperation( return operations.NewRestoreOperation(
ctx, ctx,
operations.OperationOpts{}, operations.Options{},
r.dataLayer, r.dataLayer,
r.Account, r.Account,
restorePointID, restorePointID,