aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bulk.go3
-rw-r--r--errors.go4
-rw-r--r--internal/errext/errext.go26
-rw-r--r--largeobject.go6
-rw-r--r--object.go26
-rw-r--r--request.go2
-rw-r--r--tests/bulk_upload_test.go5
7 files changed, 54 insertions, 18 deletions
diff --git a/bulk.go b/bulk.go
index 431fa76..d8b96ca 100644
--- a/bulk.go
+++ b/bulk.go
@@ -28,6 +28,7 @@ import (
"strings"
"github.com/majewsky/schwift/capabilities"
+ "github.com/majewsky/schwift/internal/errext"
)
// BulkUploadFormat enumerates possible archive formats for Container.BulkUpload().
@@ -214,7 +215,7 @@ func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, o
numNotFound++
return nil
}
- if statusErr, ok := err.(UnexpectedStatusCodeError); ok {
+ if statusErr, ok := errext.As[UnexpectedStatusCodeError](err); ok {
errs = append(errs, BulkObjectError{
ContainerName: containerName,
ObjectName: objectName,
diff --git a/errors.go b/errors.go
index 06fa639..a79dd60 100644
--- a/errors.go
+++ b/errors.go
@@ -24,6 +24,8 @@ import (
"net/http"
"strconv"
"strings"
+
+ "github.com/majewsky/schwift/internal/errext"
)
var (
@@ -148,7 +150,7 @@ func (e BulkError) Error() string {
//
// It is safe to pass a nil error, in which case Is() always returns false.
func Is(err error, code int) bool {
- if e, ok := err.(UnexpectedStatusCodeError); ok {
+ if e, ok := errext.As[UnexpectedStatusCodeError](err); ok {
return e.ActualResponse.StatusCode == code
}
return false
diff --git a/internal/errext/errext.go b/internal/errext/errext.go
new file mode 100644
index 0000000..3655ba7
--- /dev/null
+++ b/internal/errext/errext.go
@@ -0,0 +1,26 @@
+package errext
+
+import "errors"
+
+// vendored from https://github.com/sapcc/go-bits/blob/master/errext/errext.go (also licensed Apache 2.0) to prevent go.mod go bump to 1.21
+
+// As is a variant of errors.As() that leverages generics to present a nicer interface.
+//
+// //this code:
+// var perr os.PathError
+// if errors.As(err, &perr) {
+// handle(perr)
+// }
+// //can be rewritten as:
+// if perr, ok := errext.As[os.PathError](err); ok {
+// handle(perr)
+// }
+//
+// This is sometimes more verbose (like in this example), but allows to scope
+// the specific error variable to the condition's then-branch, and also looks
+// more idiomatic to developers already familiar with type casts.
+func As[T error](err error) (T, bool) {
+ var result T
+ ok := errors.As(err, &result)
+ return result, ok
+}
diff --git a/largeobject.go b/largeobject.go
index 83deddf..4def0d0 100644
--- a/largeobject.go
+++ b/largeobject.go
@@ -414,13 +414,13 @@ func (o *Object) AsNewLargeObject(sopts SegmentingOptions, topts *TruncateOption
//with the old segments
if topts != nil && topts.DeleteSegments {
lo, err := o.AsLargeObject()
- switch err {
- case nil:
+ switch {
+ case err == nil:
err := lo.Truncate(topts)
if err != nil {
return nil, err
}
- case ErrNotLarge:
+ case errors.Is(err, ErrNotLarge):
//not an error, continue down below
default:
return nil, err //unexpected error
diff --git a/object.go b/object.go
index 0322906..8e2becd 100644
--- a/object.go
+++ b/object.go
@@ -25,6 +25,7 @@ import (
"crypto/sha1" //nolint:gosec // Used by swift
"crypto/sha256"
"encoding/hex"
+ "errors"
"fmt"
"hash"
"io"
@@ -135,6 +136,7 @@ func (o *Object) fetchHeaders(opts *RequestOptions) (*ObjectHeaders, error) {
if err != nil {
return nil, err
}
+ defer resp.Body.Close()
headers := ObjectHeaders{headersFromHTTP(resp.Header)}
return &headers, headers.Validate()
@@ -147,7 +149,7 @@ func (o *Object) fetchHeaders(opts *RequestOptions) (*ObjectHeaders, error) {
//
// A successful POST request implies Invalidate() since it may change metadata.
func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error {
- _, err := Request{
+ resp, err := Request{
Method: "POST",
ContainerName: o.c.name,
ObjectName: o.name,
@@ -156,6 +158,7 @@ func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error {
}.Do(o.c.a.backend)
if err == nil {
o.Invalidate()
+ resp.Body.Close()
}
return err
}
@@ -245,10 +248,10 @@ func (o *Object) Upload(content io.Reader, opts *UploadOptions, ropts *RequestOp
//chance of an inconsistent state following an upload error
var err error
lo, err = o.AsLargeObject()
- switch err {
- case nil:
+ switch {
+ case err == nil:
//okay, delete segments at the end
- case ErrNotLarge:
+ case errors.Is(err, ErrNotLarge):
//okay, do not try to delete segments
lo = nil
default:
@@ -270,6 +273,7 @@ func (o *Object) Upload(content io.Reader, opts *UploadOptions, ropts *RequestOp
return err
}
o.Invalidate()
+ defer resp.Body.Close()
if hasher != nil {
expectedEtag := hex.EncodeToString(hasher.Sum(nil))
@@ -393,13 +397,13 @@ func (o *Object) Delete(opts *DeleteOptions, ropts *RequestOptions) error {
}
if exists {
lo, err := o.AsLargeObject()
- switch err {
- case nil:
+ switch {
+ case err == nil:
//is large object - delete segments and the object itself in one step
_, _, err := o.c.a.BulkDelete(append(lo.SegmentObjects(), o), nil, nil)
o.Invalidate()
return err
- case ErrNotLarge:
+ case errors.Is(err, ErrNotLarge):
//not a large object - use regular DELETE request
default:
//unexpected error
@@ -408,7 +412,7 @@ func (o *Object) Delete(opts *DeleteOptions, ropts *RequestOptions) error {
}
}
- _, err := Request{
+ resp, err := Request{
Method: "DELETE",
ContainerName: o.c.name,
ObjectName: o.name,
@@ -417,6 +421,7 @@ func (o *Object) Delete(opts *DeleteOptions, ropts *RequestOptions) error {
}.Do(o.c.a.backend)
if err == nil {
o.Invalidate()
+ resp.Body.Close()
}
return err
}
@@ -453,7 +458,7 @@ func (o *Object) Download(opts *RequestOptions) DownloadedObject {
ObjectName: o.name,
Options: opts,
ExpectStatusCodes: []int{http.StatusOK},
- }.Do(o.c.a.backend)
+ }.Do(o.c.a.backend) //nolint:bodyclose // body is returned and must be closed by the user
var body io.ReadCloser
if err == nil {
newHeaders := ObjectHeaders{headersFromHTTP(resp.Header)}
@@ -498,7 +503,7 @@ func (o *Object) CopyTo(target *Object, opts *CopyOptions, ropts *RequestOptions
}
}
- _, err := Request{
+ resp, err := Request{
Method: "COPY",
ContainerName: o.c.name,
ObjectName: o.name,
@@ -508,6 +513,7 @@ func (o *Object) CopyTo(target *Object, opts *CopyOptions, ropts *RequestOptions
}.Do(o.c.a.backend)
if err == nil {
target.Invalidate()
+ resp.Body.Close()
}
return err
}
diff --git a/request.go b/request.go
index 7b5da6d..01f052f 100644
--- a/request.go
+++ b/request.go
@@ -40,7 +40,7 @@ import (
type RequestOptions struct {
Headers Headers
Values url.Values
- Context context.Context
+ Context context.Context //nolint: containedctx // ignored for now to not break the API
}
func cloneRequestOptions(orig *RequestOptions, additional Headers) *RequestOptions {
diff --git a/tests/bulk_upload_test.go b/tests/bulk_upload_test.go
index d810cf5..0bdcb4e 100644
--- a/tests/bulk_upload_test.go
+++ b/tests/bulk_upload_test.go
@@ -25,6 +25,7 @@ import (
"testing"
"github.com/majewsky/schwift"
+ "github.com/majewsky/schwift/internal/errext"
)
func TestBulkUploadSuccess(t *testing.T) {
@@ -62,7 +63,7 @@ func TestBulkUploadArchiveError(t *testing.T) {
)
expectInt(t, n, 0)
expectError(t, err, "400 Bad Request: Invalid Tar File: truncated header")
- bulkErr := err.(schwift.BulkError) //nolint:errcheck
+ bulkErr, _ := errext.As[schwift.BulkError](err)
expectInt(t, bulkErr.StatusCode, 400)
expectString(t, bulkErr.OverallError, "Invalid Tar File: truncated header")
expectInt(t, len(bulkErr.ObjectErrors), 0)
@@ -87,7 +88,7 @@ func TestBulkUploadObjectError(t *testing.T) {
)
expectInt(t, n, 1)
expectError(t, err, "400 Bad Request (+1 object errors)")
- bulkErr := err.(schwift.BulkError) //nolint:errcheck
+ bulkErr, _ := errext.As[schwift.BulkError](err)
expectInt(t, len(bulkErr.ObjectErrors), 1)
expectString(t, bulkErr.ObjectErrors[0].ContainerName, c.Name())
expectInt(t, bulkErr.ObjectErrors[0].StatusCode, 400)