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.
type BackupOperation struct {
operation
Version string
Results BackupResults `json:"results"`
Targets []string `json:"selectors"` // todo: replace with Selectors
Version string `json:"version"`
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.
func NewBackupOperation(
ctx context.Context,
opts OperationOpts,
opts Options,
kw *kopia.KopiaWrapper,
acct account.Account,
targets []string,
) (BackupOperation, error) {
op := BackupOperation{
operation: newOperation(opts, kw),
Targets: targets,
Version: "v0",
account: acct,
Targets: targets,
}
if err := op.validate(); err != nil {
return BackupOperation{}, err

View File

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

View File

@ -4,6 +4,7 @@ import (
"time"
"github.com/google/uuid"
multierror "github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/alcionai/corso/internal/kopia"
@ -18,35 +19,38 @@ const (
Failed
)
// --------------------------------------------------------------------------------
// Operation Core
// --------------------------------------------------------------------------------
// An operation tracks the in-progress workload of some long-running process.
// Specific processes (eg: backups, restores, etc) are expected to wrap operation
// with process specific details.
type operation struct {
ID uuid.UUID // system generated identifier
CreatedAt time.Time // datetime of the operation's creation
ID uuid.UUID `json:"id"` // system generated identifier
CreatedAt time.Time `json:"createdAt"` // datetime of the operation's creation
Options Options `json:"options"`
Status opStatus `json:"status"`
options OperationOpts
kopia *kopia.KopiaWrapper
Status opStatus
Errors []error
kopia *kopia.KopiaWrapper
}
// OperationOpts configure some parameters of the operation
type OperationOpts struct {
// Options configure some parameters of the operation
type Options struct {
// todo: collision handling
// todo: fast fail vs best attempt
}
func newOperation(
opts OperationOpts,
opts Options,
kw *kopia.KopiaWrapper,
) operation {
return operation{
ID: uuid.New(),
CreatedAt: time.Now(),
options: opts,
Options: opts,
kopia: kw,
Status: InProgress,
Errors: []error{},
}
}
@ -56,3 +60,22 @@ func (op operation) validate() error {
}
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() {
t := suite.T()
op := newOperation(OperationOpts{}, nil)
assert.NotNil(t, op.Errors)
op := newOperation(Options{}, nil)
assert.NotNil(t, op.ID)
}
@ -36,7 +35,7 @@ func (suite *OperationSuite) TestOperation_Validate() {
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
op := newOperation(OperationOpts{}, test.kw)
op := newOperation(Options{}, test.kw)
test.errCheck(t, op.validate())
})
}

View File

@ -13,18 +13,25 @@ import (
// RestoreOperation wraps an operation with restore-specific props.
type RestoreOperation struct {
operation
Version string
restorePointID string
account account.Account
RestorePointID string `json:"restorePointID"`
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.
func NewRestoreOperation(
ctx context.Context,
opts OperationOpts,
opts Options,
kw *kopia.KopiaWrapper,
acct account.Account,
restorePointID string,
@ -32,10 +39,10 @@ func NewRestoreOperation(
) (RestoreOperation, error) {
op := RestoreOperation{
operation: newOperation(opts, kw),
RestorePointID: restorePointID,
Targets: targets,
Version: "v0",
account: acct,
restorePointID: restorePointID,
Targets: targets,
}
if err := op.validate(); err != nil {
return RestoreOperation{}, err
@ -51,7 +58,7 @@ func (op RestoreOperation) validate() error {
// Run begins a synchronous restore operation.
// todo (keepers): return stats block in first param.
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 {
return errors.Wrap(err, "retrieving service data")
}

View File

@ -37,20 +37,20 @@ func (suite *RestoreOpIntegrationSuite) TestNewRestoreOperation() {
table := []struct {
name string
opts operations.OperationOpts
opts operations.Options
kw *kopia.KopiaWrapper
acct account.Account
targets []string
errCheck assert.ErrorAssertionFunc
}{
{"good", operations.OperationOpts{}, kw, acct, nil, assert.NoError},
{"missing kopia", operations.OperationOpts{}, nil, acct, nil, assert.Error},
{"good", operations.Options{}, kw, acct, nil, assert.NoError},
{"missing kopia", operations.Options{}, nil, acct, nil, assert.Error},
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
_, err := operations.NewRestoreOperation(
context.Background(),
operations.OperationOpts{},
operations.Options{},
test.kw,
test.acct,
"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) {
return operations.NewBackupOperation(
ctx,
operations.OperationOpts{},
operations.Options{},
r.dataLayer,
r.Account,
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) {
return operations.NewRestoreOperation(
ctx,
operations.OperationOpts{},
operations.Options{},
r.dataLayer,
r.Account,
restorePointID,