diff --git a/src/pkg/account/m365.go b/src/pkg/account/m365.go index a9c84fb37..b9f91b8aa 100644 --- a/src/pkg/account/m365.go +++ b/src/pkg/account/m365.go @@ -1,6 +1,7 @@ package account import ( + "github.com/alcionai/clues" "github.com/pkg/errors" "github.com/alcionai/corso/src/pkg/credentials" @@ -66,7 +67,7 @@ func (c M365Config) validate() error { for k, v := range check { if len(v) == 0 { - return errors.Wrap(errMissingRequired, k) + return clues.Stack(errMissingRequired, errors.New(k)) } } diff --git a/src/pkg/credentials/aws.go b/src/pkg/credentials/aws.go index 61c0673aa..9ab9d7c3e 100644 --- a/src/pkg/credentials/aws.go +++ b/src/pkg/credentials/aws.go @@ -3,6 +3,7 @@ package credentials import ( "os" + "github.com/alcionai/clues" "github.com/pkg/errors" ) @@ -48,7 +49,7 @@ func (c AWS) Validate() error { for k, v := range check { if len(v) == 0 { - return errors.Wrap(errMissingRequired, k) + return clues.Stack(errMissingRequired, errors.New(k)) } } diff --git a/src/pkg/credentials/corso.go b/src/pkg/credentials/corso.go index 297f2030a..fc22dc957 100644 --- a/src/pkg/credentials/corso.go +++ b/src/pkg/credentials/corso.go @@ -3,6 +3,7 @@ package credentials import ( "os" + "github.com/alcionai/clues" "github.com/pkg/errors" ) @@ -34,7 +35,7 @@ func (c Corso) Validate() error { for k, v := range check { if len(v) == 0 { - return errors.Wrap(errMissingRequired, k) + return clues.Stack(errMissingRequired, errors.New(k)) } } diff --git a/src/pkg/credentials/m365.go b/src/pkg/credentials/m365.go index 2d0943aa7..c2d0c5906 100644 --- a/src/pkg/credentials/m365.go +++ b/src/pkg/credentials/m365.go @@ -3,6 +3,7 @@ package credentials import ( "os" + "github.com/alcionai/clues" "github.com/pkg/errors" ) @@ -36,7 +37,7 @@ func (c M365) Validate() error { for k, v := range check { if len(v) == 0 { - return errors.Wrap(errMissingRequired, k) + return clues.Stack(errMissingRequired, errors.New(k)) } } diff --git a/src/pkg/path/onedrive.go b/src/pkg/path/onedrive.go index cf960933f..86d40c887 100644 --- a/src/pkg/path/onedrive.go +++ b/src/pkg/path/onedrive.go @@ -1,8 +1,6 @@ package path -import ( - "github.com/pkg/errors" -) +import "github.com/alcionai/clues" // drivePath is used to represent path components // of an item within the drive i.e. @@ -20,10 +18,9 @@ func ToOneDrivePath(p Path) (*DrivePath, error) { // Must be at least `drives//root:` if len(folders) < 3 { - return nil, errors.Errorf( - "folder path doesn't match expected format for OneDrive items: %s", - p.Folder(), - ) + return nil, clues. + New("folder path doesn't match expected format for OneDrive items"). + With("folders", p.Folder()) } return &DrivePath{DriveID: folders[1], Folders: folders[3:]}, nil diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go index e2e6d273e..b0c4456c4 100644 --- a/src/pkg/path/path.go +++ b/src/pkg/path/path.go @@ -56,11 +56,10 @@ import ( "fmt" "strings" + "github.com/alcionai/clues" "github.com/pkg/errors" ) -const templateErrPathParsing = "parsing resource path from %s" - const ( escapeCharacter = '\\' PathSeparator = '/' @@ -73,7 +72,10 @@ var charactersToEscape = map[rune]struct{}{ escapeCharacter: {}, } -var errMissingSegment = errors.New("missing required path element") +var ( + errMissingSegment = errors.New("missing required path element") + errParsingPath = errors.New("parsing resource path") +) // For now, adding generic functions to pull information from segments. // Resources that don't have the requested information should return an empty @@ -252,11 +254,11 @@ func (pb Builder) join(start, end int) string { func verifyInputValues(tenant, resourceOwner string) error { if len(tenant) == 0 { - return errors.Wrap(errMissingSegment, "tenant") + return clues.Stack(errMissingSegment, errors.New("tenant")) } if len(resourceOwner) == 0 { - return errors.Wrap(errMissingSegment, "resourceOwner") + return clues.Stack(errMissingSegment, errors.New("resourceOwner")) } return nil @@ -418,17 +420,17 @@ func FromDataLayerPath(p string, isItem bool) (Path, error) { p = TrimTrailingSlash(p) // If p was just the path separator then it will be empty now. if len(p) == 0 { - return nil, errors.Errorf("logically empty path given: %s", p) + return nil, clues.New("logically empty path given").With("path_string", p) } // Turn into a Builder to reuse code that ignores empty elements. pb, err := Builder{}.UnescapeAndAppend(Split(p)...) if err != nil { - return nil, errors.Wrapf(err, templateErrPathParsing, p) + return nil, clues.Stack(errParsingPath, err).With("path_string", p) } if len(pb.elements) < 5 { - return nil, errors.Errorf("path has too few segments: %s", p) + return nil, clues.New("path has too few segments").With("path_string", p) } service, category, err := validateServiceAndCategoryStrings( @@ -436,7 +438,7 @@ func FromDataLayerPath(p string, isItem bool) (Path, error) { pb.elements[3], ) if err != nil { - return nil, errors.Wrapf(err, templateErrPathParsing, p) + return nil, clues.Stack(errParsingPath, err).With("path_string", p) } return &dataLayerResourcePath{ @@ -519,8 +521,8 @@ func validateEscapedElement(element string) error { prevWasEscape = false if _, ok := charactersToEscape[c]; !ok { - return errors.Errorf( - "bad escape sequence in path: '%c%c'", escapeCharacter, c) + return clues.New("bad escape sequence in path"). + With("escape_sequence", fmt.Sprintf("'%c%c'", escapeCharacter, c)) } case false: @@ -530,7 +532,7 @@ func validateEscapedElement(element string) error { } if _, ok := charactersToEscape[c]; ok { - return errors.Errorf("unescaped '%c' in path", c) + return clues.New("unescaped character in path").With("character", c) } } } diff --git a/src/pkg/path/resource_path.go b/src/pkg/path/resource_path.go index 07f9f429c..57f41c6ff 100644 --- a/src/pkg/path/resource_path.go +++ b/src/pkg/path/resource_path.go @@ -1,8 +1,10 @@ package path import ( + "fmt" "strings" + "github.com/alcionai/clues" "github.com/pkg/errors" ) @@ -119,12 +121,12 @@ var serviceCategories = map[ServiceType]map[CategoryType]struct{}{ func validateServiceAndCategoryStrings(s, c string) (ServiceType, CategoryType, error) { service := toServiceType(s) if service == UnknownService { - return UnknownService, UnknownCategory, errors.Wrapf(ErrorUnknownService, "%q", s) + return UnknownService, UnknownCategory, clues.Stack(ErrorUnknownService).With("service", fmt.Sprintf("%q", s)) } category := ToCategoryType(c) if category == UnknownCategory { - return UnknownService, UnknownCategory, errors.Wrapf(ErrorUnknownCategory, "%q", c) + return UnknownService, UnknownCategory, clues.Stack(ErrorUnknownService).With("category", fmt.Sprintf("%q", c)) } if err := validateServiceAndCategory(service, category); err != nil { @@ -137,15 +139,12 @@ func validateServiceAndCategoryStrings(s, c string) (ServiceType, CategoryType, func validateServiceAndCategory(service ServiceType, category CategoryType) error { cats, ok := serviceCategories[service] if !ok { - return errors.New("unsupported service") + return clues.New("unsupported service").With("service", fmt.Sprintf("%q", service)) } if _, ok := cats[category]; !ok { - return errors.Errorf( - "unknown service/category combination %q/%q", - service, - category, - ) + return clues.New("unknown service/category combination"). + WithAll("service", fmt.Sprintf("%q", service), "category", fmt.Sprintf("%q", category)) } return nil @@ -234,7 +233,7 @@ func (rp dataLayerResourcePath) Item() string { func (rp dataLayerResourcePath) Dir() (Path, error) { if len(rp.elements) <= 4 { - return nil, errors.Errorf("unable to shorten path %q", rp) + return nil, clues.New("unable to shorten path").With("path", fmt.Sprintf("%q", rp)) } return &dataLayerResourcePath{ diff --git a/src/pkg/repository/repository.go b/src/pkg/repository/repository.go index 087b193bc..a8f1e2827 100644 --- a/src/pkg/repository/repository.go +++ b/src/pkg/repository/repository.go @@ -89,13 +89,17 @@ func Initialize( s storage.Storage, opts control.Options, ) (Repository, error) { - ctx = clues.AddAll(ctx, "acct_provider", acct.Provider, "storage_provider", s.Provider) + ctx = clues.AddAll( + ctx, + "acct_provider", acct.Provider.String(), + "acct_id", acct.ID(), // TODO: pii + "storage_provider", s.Provider.String()) kopiaRef := kopia.NewConn(s) if err := kopiaRef.Initialize(ctx); err != nil { // replace common internal errors so that sdk users can check results with errors.Is() if kopia.IsRepoAlreadyExistsError(err) { - return nil, ErrorRepoAlreadyExists + return nil, clues.Stack(ErrorRepoAlreadyExists).WithClues(ctx) } return nil, errors.Wrap(err, "initializing kopia") @@ -153,7 +157,11 @@ func Connect( s storage.Storage, opts control.Options, ) (Repository, error) { - ctx = clues.AddAll(ctx, "acct_provider", acct.Provider, "storage_provider", s.Provider) + ctx = clues.AddAll( + ctx, + "acct_provider", acct.Provider.String(), + "acct_id", acct.ID(), // TODO: pii + "storage_provider", s.Provider.String()) // Close/Reset the progress bar. This ensures callers don't have to worry about // their output getting clobbered (#1720) @@ -210,12 +218,12 @@ func Connect( func (r *repository) Close(ctx context.Context) error { if err := r.Bus.Close(); err != nil { - logger.Ctx(ctx).Debugw("closing the event bus", "err", err) + logger.Ctx(ctx).With("err", err).Debugw("closing the event bus", clues.In(ctx).Slice()...) } if r.dataLayer != nil { if err := r.dataLayer.Close(ctx); err != nil { - logger.Ctx(ctx).Debugw("closing Datalayer", "err", err) + logger.Ctx(ctx).With("err", err).Debugw("closing Datalayer", clues.In(ctx).Slice()...) } r.dataLayer = nil @@ -223,7 +231,7 @@ func (r *repository) Close(ctx context.Context) error { if r.modelStore != nil { if err := r.modelStore.Close(ctx); err != nil { - logger.Ctx(ctx).Debugw("closing modelStore", "err", err) + logger.Ctx(ctx).With("err", err).Debugw("closing modelStore", clues.In(ctx).Slice()...) } r.modelStore = nil diff --git a/src/pkg/services/m365/m365.go b/src/pkg/services/m365/m365.go index 984326b6d..19d37f0d5 100644 --- a/src/pkg/services/m365/m365.go +++ b/src/pkg/services/m365/m365.go @@ -3,6 +3,7 @@ package m365 import ( "context" + "github.com/alcionai/clues" "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/pkg/errors" @@ -20,10 +21,10 @@ type User struct { // Users returns a list of users in the specified M365 tenant // TODO: Implement paging support -func Users(ctx context.Context, m365Account account.Account) ([]*User, error) { - gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), m365Account, connector.Users) +func Users(ctx context.Context, acct account.Account) ([]*User, error) { + gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), acct, connector.Users) if err != nil { - return nil, errors.Wrap(err, "could not initialize M365 graph connection") + return nil, errors.Wrap(err, "initializing M365 graph connection") } users, err := discovery.Users(ctx, gc.Owners.Users()) @@ -36,7 +37,7 @@ func Users(ctx context.Context, m365Account account.Account) ([]*User, error) { for _, u := range users { pu, err := parseUser(u) if err != nil { - return nil, err + return nil, errors.Wrap(err, "parsing userable") } ret = append(ret, pu) @@ -45,8 +46,8 @@ func Users(ctx context.Context, m365Account account.Account) ([]*User, error) { return ret, nil } -func UserIDs(ctx context.Context, m365Account account.Account) ([]string, error) { - users, err := Users(ctx, m365Account) +func UserIDs(ctx context.Context, acct account.Account) ([]string, error) { + users, err := Users(ctx, acct) if err != nil { return nil, err } @@ -61,8 +62,8 @@ func UserIDs(ctx context.Context, m365Account account.Account) ([]string, error) // UserPNs retrieves all user principleNames in the tenant. Principle Names // can be used analogous userIDs in graph API queries. -func UserPNs(ctx context.Context, m365Account account.Account) ([]string, error) { - users, err := Users(ctx, m365Account) +func UserPNs(ctx context.Context, acct account.Account) ([]string, error) { + users, err := Users(ctx, acct) if err != nil { return nil, err } @@ -76,20 +77,20 @@ func UserPNs(ctx context.Context, m365Account account.Account) ([]string, error) } // SiteURLs returns a list of SharePoint site WebURLs in the specified M365 tenant -func SiteURLs(ctx context.Context, m365Account account.Account) ([]string, error) { - gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), m365Account, connector.Sites) +func SiteURLs(ctx context.Context, acct account.Account) ([]string, error) { + gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), acct, connector.Sites) if err != nil { - return nil, errors.Wrap(err, "could not initialize M365 graph connection") + return nil, errors.Wrap(err, "initializing M365 graph connection") } return gc.GetSiteWebURLs(), nil } // SiteURLs returns a list of SharePoint sites IDs in the specified M365 tenant -func SiteIDs(ctx context.Context, m365Account account.Account) ([]string, error) { - gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), m365Account, connector.Sites) +func SiteIDs(ctx context.Context, acct account.Account) ([]string, error) { + gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), acct, connector.Sites) if err != nil { - return nil, errors.Wrap(err, "could not initialize M365 graph connection") + return nil, errors.Wrap(err, "initializing graph connection") } return gc.GetSiteIDs(), nil @@ -98,7 +99,8 @@ func SiteIDs(ctx context.Context, m365Account account.Account) ([]string, error) // parseUser extracts information from `models.Userable` we care about func parseUser(item models.Userable) (*User, error) { if item.GetUserPrincipalName() == nil { - return nil, errors.Errorf("no principal name for User: %s", *item.GetId()) + return nil, clues.New("user missing principal name"). + With("user_id", *item.GetId()) // TODO: pii } u := &User{PrincipalName: *item.GetUserPrincipalName(), ID: *item.GetId()} diff --git a/src/pkg/storage/common.go b/src/pkg/storage/common.go index fd5bb24dd..e230e50fb 100644 --- a/src/pkg/storage/common.go +++ b/src/pkg/storage/common.go @@ -1,6 +1,7 @@ package storage import ( + "github.com/alcionai/clues" "github.com/pkg/errors" "github.com/alcionai/corso/src/pkg/credentials" @@ -45,7 +46,7 @@ func (s Storage) CommonConfig() (CommonConfig, error) { // ensures all required properties are present func (c CommonConfig) validate() error { if len(c.CorsoPassphrase) == 0 { - return errors.Wrap(errMissingRequired, credentials.CorsoPassphrase) + return clues.Stack(errMissingRequired, errors.New(credentials.CorsoPassphrase)) } // kopiaCfgFilePath is not required diff --git a/src/pkg/storage/s3.go b/src/pkg/storage/s3.go index 67711f421..78de67ce3 100644 --- a/src/pkg/storage/s3.go +++ b/src/pkg/storage/s3.go @@ -3,6 +3,7 @@ package storage import ( "strconv" + "github.com/alcionai/clues" "github.com/pkg/errors" "github.com/alcionai/corso/src/internal/common" @@ -81,7 +82,7 @@ func (c S3Config) validate() error { } for k, v := range check { if len(v) == 0 { - return errors.Wrap(errMissingRequired, k) + return clues.Stack(errMissingRequired, errors.New(k)) } }