diff --git a/autopilot/autopilot.go b/autopilot/autopilot.go index c53e4ec4c..eb08c9456 100644 --- a/autopilot/autopilot.go +++ b/autopilot/autopilot.go @@ -632,7 +632,7 @@ func (ap *Autopilot) isStopped() bool { func (ap *Autopilot) configHandlerGET(jc jape.Context) { autopilot, err := ap.bus.Autopilot(jc.Request.Context(), ap.id) - if err != nil && strings.Contains(err.Error(), api.ErrAutopilotNotFound.Error()) { + if utils.IsErr(err, api.ErrAutopilotNotFound) { jc.Error(errors.New("autopilot is not configured yet"), http.StatusNotFound) return } @@ -654,7 +654,7 @@ func (ap *Autopilot) configHandlerPUT(jc jape.Context) { // fetch the autopilot and update its config var contractSetChanged bool autopilot, err := ap.bus.Autopilot(jc.Request.Context(), ap.id) - if err != nil && strings.Contains(err.Error(), api.ErrAutopilotNotFound.Error()) { + if utils.IsErr(err, api.ErrAutopilotNotFound) { autopilot = api.Autopilot{ID: ap.id, Config: cfg} } else { if autopilot.Config.Contracts.Set != cfg.Contracts.Set { diff --git a/autopilot/contractor.go b/autopilot/contractor.go index d69f2a354..8dfc702f3 100644 --- a/autopilot/contractor.go +++ b/autopilot/contractor.go @@ -1004,7 +1004,7 @@ func (c *contractor) runRevisionBroadcast(ctx context.Context, w Worker, allCont ctx, cancel := context.WithTimeout(ctx, timeoutBroadcastRevision) err := w.RHPBroadcast(ctx, contract.ID) cancel() - if err != nil && strings.Contains(err.Error(), "transaction has a file contract with an outdated revision number") { + if utils.IsErr(err, errors.New("transaction has a file contract with an outdated revision number")) { continue // don't log - revision was already broadcasted } else if err != nil { c.logger.Warnw(fmt.Sprintf("failed to broadcast contract revision: %v", err), diff --git a/cmd/renterd/main.go b/cmd/renterd/main.go index 98e075d92..093747796 100644 --- a/cmd/renterd/main.go +++ b/cmd/renterd/main.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "errors" "flag" "fmt" "log" @@ -24,6 +25,7 @@ import ( "go.sia.tech/renterd/bus" "go.sia.tech/renterd/config" "go.sia.tech/renterd/internal/node" + "go.sia.tech/renterd/internal/utils" "go.sia.tech/renterd/s3" "go.sia.tech/renterd/stores" "go.sia.tech/renterd/worker" @@ -224,7 +226,7 @@ func parseEnvVar(s string, v interface{}) { func listenTCP(logger *zap.Logger, addr string) (net.Listener, error) { l, err := net.Listen("tcp", addr) - if err != nil && strings.Contains(err.Error(), "no such host") && strings.Contains(addr, "localhost") { + if utils.IsErr(err, errors.New("no such host")) && strings.Contains(addr, "localhost") { // fall back to 127.0.0.1 if 'localhost' doesn't work _, port, err := net.SplitHostPort(addr) if err != nil { diff --git a/s3/authentication.go b/s3/authentication.go index 215da52b7..67017356b 100644 --- a/s3/authentication.go +++ b/s3/authentication.go @@ -5,11 +5,11 @@ import ( "fmt" "io" "net/http" - "strings" "go.sia.tech/gofakes3" "go.sia.tech/gofakes3/signature" "go.sia.tech/renterd/api" + "go.sia.tech/renterd/internal/utils" ) var ( @@ -89,7 +89,7 @@ func newAuthenticatedBackend(b *s3) *authenticatedBackend { func (b *authenticatedBackend) applyBucketPolicy(ctx context.Context, bucketName string, p *permissions) error { bucket, err := b.backend.b.Bucket(ctx, bucketName) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { return gofakes3.BucketNotFound(bucketName) } else if err != nil { return gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) diff --git a/s3/backend.go b/s3/backend.go index 5261bd5f7..62f7a3e76 100644 --- a/s3/backend.go +++ b/s3/backend.go @@ -10,6 +10,7 @@ import ( "go.sia.tech/gofakes3" "go.sia.tech/renterd/api" + "go.sia.tech/renterd/internal/utils" "go.sia.tech/renterd/object" "go.uber.org/zap" ) @@ -109,7 +110,7 @@ func (s *s3) ListBucket(ctx context.Context, bucketName string, prefix *gofakes3 } var res api.ObjectsResponse res, err = s.b.Object(ctx, bucketName, path, opts) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { return nil, gofakes3.BucketNotFound(bucketName) } else if err != nil { return nil, gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -129,7 +130,7 @@ func (s *s3) ListBucket(ctx context.Context, bucketName string, prefix *gofakes3 var res api.ObjectsListResponse res, err = s.b.ListObjects(ctx, bucketName, opts) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { return nil, gofakes3.BucketNotFound(bucketName) } else if err != nil { return nil, gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -169,7 +170,7 @@ func (s *s3) ListBucket(ctx context.Context, bucketName string, prefix *gofakes3 // gofakes3.ErrBucketAlreadyExists MUST be returned. func (s *s3) CreateBucket(ctx context.Context, name string) error { err := s.b.CreateBucket(ctx, name, api.CreateBucketOptions{}) - if err != nil && strings.Contains(err.Error(), api.ErrBucketExists.Error()) { + if utils.IsErr(err, api.ErrBucketExists) { return gofakes3.ErrBucketAlreadyExists } else if err != nil { return gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -183,7 +184,7 @@ func (s *s3) CreateBucket(ctx context.Context, name string) error { // TODO: backend could be improved to allow for checking specific dir in root. func (s *s3) BucketExists(ctx context.Context, name string) (bool, error) { _, err := s.b.Bucket(ctx, name) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { return false, nil } else if err != nil { return false, gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -203,9 +204,9 @@ func (s *s3) BucketExists(ctx context.Context, name string) (bool, error) { // atomically checking whether a bucket is empty. func (s *s3) DeleteBucket(ctx context.Context, name string) error { err := s.b.DeleteBucket(ctx, name) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotEmpty.Error()) { + if utils.IsErr(err, api.ErrBucketNotEmpty) { return gofakes3.ErrBucketNotEmpty - } else if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + } else if utils.IsErr(err, api.ErrBucketNotFound) { return gofakes3.BucketNotFound(name) } else if err != nil { return gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -243,9 +244,9 @@ func (s *s3) GetObject(ctx context.Context, bucketName, objectName string, range } res, err := s.w.GetObject(ctx, bucketName, objectName, opts) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { return nil, gofakes3.BucketNotFound(bucketName) - } else if err != nil && strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + } else if utils.IsErr(err, api.ErrObjectNotFound) { return nil, gofakes3.KeyNotFound(objectName) } else if err != nil { return nil, gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -298,7 +299,7 @@ func (s *s3) HeadObject(ctx context.Context, bucketName, objectName string) (*go res, err := s.w.HeadObject(ctx, bucketName, objectName, api.HeadObjectOptions{ IgnoreDelim: true, }) - if err != nil && strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + if utils.IsErr(err, api.ErrObjectNotFound) { return nil, gofakes3.KeyNotFound(objectName) } else if err != nil { return nil, gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -346,9 +347,9 @@ func (s *s3) HeadObject(ctx context.Context, bucketName, objectName string) (*go // isn't a null version, Amazon S3 does not remove any objects. func (s *s3) DeleteObject(ctx context.Context, bucketName, objectName string) (gofakes3.ObjectDeleteResult, error) { err := s.b.DeleteObject(ctx, bucketName, objectName, api.DeleteObjectOptions{}) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { return gofakes3.ObjectDeleteResult{}, gofakes3.BucketNotFound(bucketName) - } else if err != nil && !strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + } else if utils.IsErr(err, api.ErrObjectNotFound) { return gofakes3.ObjectDeleteResult{}, gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) } @@ -371,7 +372,7 @@ func (s *s3) PutObject(ctx context.Context, bucketName, key string, meta map[str } ur, err := s.w.UploadObject(ctx, input, bucketName, key, opts) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { return gofakes3.PutObjectResult{}, gofakes3.BucketNotFound(bucketName) } else if err != nil { return gofakes3.PutObjectResult{}, gofakes3.ErrorMessage(gofakes3.ErrInternal, err.Error()) @@ -387,7 +388,7 @@ func (s *s3) DeleteMulti(ctx context.Context, bucketName string, objects ...stri var res gofakes3.MultiDeleteResult for _, objectName := range objects { err := s.b.DeleteObject(ctx, bucketName, objectName, api.DeleteObjectOptions{}) - if err != nil && !strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + if err != nil && !utils.IsErr(err, api.ErrObjectNotFound) { res.Error = append(res.Error, gofakes3.ErrorResult{ Key: objectName, Code: gofakes3.ErrInternal, diff --git a/stores/migrations.go b/stores/migrations.go index 6395b52bb..b0304090e 100644 --- a/stores/migrations.go +++ b/stores/migrations.go @@ -3,9 +3,9 @@ package stores import ( "errors" "fmt" - "strings" "github.com/go-gormigrate/gormigrate/v2" + "go.sia.tech/renterd/internal/utils" "go.uber.org/zap" "gorm.io/gorm" ) @@ -32,7 +32,7 @@ func performMigrations(db *gorm.DB, logger *zap.SugaredLogger) error { ID: "00002_prune_slabs_trigger", Migrate: func(tx *gorm.DB) error { err := performMigration(tx, dbIdentifier, "00002_prune_slabs_trigger", logger) - if err != nil && strings.Contains(err.Error(), errMySQLNoSuperPrivilege.Error()) { + if utils.IsErr(err, errMySQLNoSuperPrivilege) { logger.Warn("migration 00002_prune_slabs_trigger requires the user to have the SUPER privilege to register triggers") } return err diff --git a/worker/download.go b/worker/download.go index 3a58bbc98..6f070acbd 100644 --- a/worker/download.go +++ b/worker/download.go @@ -7,13 +7,13 @@ import ( "fmt" "io" "math" - "strings" "sync" "time" rhpv2 "go.sia.tech/core/rhp/v2" "go.sia.tech/core/types" "go.sia.tech/renterd/api" + "go.sia.tech/renterd/internal/utils" "go.sia.tech/renterd/object" "go.sia.tech/renterd/stats" "go.uber.org/zap" @@ -454,7 +454,7 @@ func (mgr *downloadManager) numDownloaders() int { // in the partial slab buffer. func (mgr *downloadManager) fetchPartialSlab(ctx context.Context, key object.EncryptionKey, offset, length uint32) ([]byte, *object.Slab, error) { data, err := mgr.os.FetchPartialSlab(ctx, key, offset, length) - if err != nil && strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + if utils.IsErr(err, api.ErrObjectNotFound) { // Check if slab was already uploaded. slab, err := mgr.os.Slab(ctx, key) if err != nil { diff --git a/worker/worker.go b/worker/worker.go index dab8ef30a..99323b501 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -878,7 +878,7 @@ func (w *worker) objectsHandlerHEAD(jc jape.Context) { IgnoreDelim: ignoreDelim, OnlyMetadata: true, }) - if err != nil && strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + if utils.IsErr(err, api.ErrObjectNotFound) { jc.Error(err, http.StatusNotFound) return } else if err != nil { @@ -951,7 +951,7 @@ func (w *worker) objectsHandlerGET(jc jape.Context) { path := jc.PathParam("path") res, err := w.bus.Object(ctx, bucket, path, opts) - if err != nil && strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + if utils.IsErr(err, api.ErrObjectNotFound) { jc.Error(err, http.StatusNotFound) return } else if jc.Check("couldn't get object or entries", err) != nil { @@ -1041,7 +1041,7 @@ func (w *worker) objectsHandlerPUT(jc jape.Context) { // return early if the bucket does not exist _, err = w.bus.Bucket(ctx, bucket) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { jc.Error(fmt.Errorf("bucket '%s' not found; %w", bucket, err), http.StatusNotFound) return } @@ -1160,7 +1160,7 @@ func (w *worker) multipartUploadHandlerPUT(jc jape.Context) { // return early if the bucket does not exist _, err = w.bus.Bucket(ctx, bucket) - if err != nil && strings.Contains(err.Error(), api.ErrBucketNotFound.Error()) { + if utils.IsErr(err, api.ErrBucketNotFound) { jc.Error(fmt.Errorf("bucket '%s' not found; %w", bucket, err), http.StatusNotFound) return } @@ -1263,7 +1263,7 @@ func (w *worker) objectsHandlerDELETE(jc jape.Context) { return } err := w.bus.DeleteObject(jc.Request.Context(), bucket, jc.PathParam("path"), api.DeleteObjectOptions{Batch: batch}) - if err != nil && strings.Contains(err.Error(), api.ErrObjectNotFound.Error()) { + if utils.IsErr(err, api.ErrObjectNotFound) { jc.Error(err, http.StatusNotFound) return } @@ -1563,5 +1563,5 @@ func isErrHostUnreachable(err error) bool { } func isErrDuplicateTransactionSet(err error) bool { - return err != nil && strings.Contains(err.Error(), modules.ErrDuplicateTransactionSet.Error()) + return utils.IsErr(err, modules.ErrDuplicateTransactionSet) }