Skip to content

Commit db3a5a4

Browse files
committed
s3 compat: return standard S3 error codes (NoSuchKey, et al.)
* add `s3.ErrInfo` to carry error, status, and (explicit) S3 error code - error must be non-nil; other two fields optional * refactor all `s3.WriteErr` call sites * return "NoSuchKey" on GET, HEAD, DELETE, and COPY (non-existent object) - add S3 error code constants (NoSuchKey, NoSuchBucket, NoSuchUpload) Fixes: #274 Signed-off-by: Alex Aizman <alex.aizman@gmail.com>
1 parent 9d1a7f9 commit db3a5a4

6 files changed

Lines changed: 183 additions & 111 deletions

File tree

ais/prxs3.go

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (p *proxy) s3Handler(w http.ResponseWriter, r *http.Request) {
5050
switch r.Method {
5151
case http.MethodHead:
5252
if len(apiItems) == 0 {
53-
s3.WriteErr(w, r, errS3Req, 0)
53+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Req})
5454
return
5555
}
5656
if len(apiItems) == 1 {
@@ -66,7 +66,7 @@ func (p *proxy) s3Handler(w http.ResponseWriter, r *http.Request) {
6666
// "list buckets for a given provider" comment there
6767
// perms: apc.AceListBuckets
6868
if err := p.access(r.Context(), r.Header, nil, apc.AceListBuckets); err != nil {
69-
s3.WriteErr(w, r, err, http.StatusForbidden)
69+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
7070
return
7171
}
7272
p.bckNamesFromBMD(w)
@@ -100,7 +100,7 @@ func (p *proxy) s3Handler(w http.ResponseWriter, r *http.Request) {
100100
p.getObjS3(w, r, apiItems, q, listMultipart)
101101
case http.MethodPut:
102102
if len(apiItems) == 0 {
103-
s3.WriteErr(w, r, errS3Req, 0)
103+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Req})
104104
return
105105
}
106106
if len(apiItems) == 1 {
@@ -123,18 +123,18 @@ func (p *proxy) s3Handler(w http.ResponseWriter, r *http.Request) {
123123
return
124124
}
125125
if len(apiItems) != 1 {
126-
s3.WriteErr(w, r, errS3Req, 0)
126+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Req})
127127
return
128128
}
129129
if _, multiple := q[s3.QparamMultiDelete]; !multiple {
130-
s3.WriteErr(w, r, errS3Req, 0)
130+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Req})
131131
return
132132
}
133133
// perms: apc.AceObjDELETE
134134
p.delMultipleObjs(w, r, apiItems[0])
135135
case http.MethodDelete:
136136
if len(apiItems) == 0 {
137-
s3.WriteErr(w, r, errS3Req, 0)
137+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Req})
138138
return
139139
}
140140
if len(apiItems) == 1 {
@@ -179,7 +179,7 @@ func (p *proxy) bckNamesFromBMD(w http.ResponseWriter) {
179179
// Create a new S3 bucket
180180
func (p *proxy) putBckS3(w http.ResponseWriter, r *http.Request, bucket string) {
181181
if err := p.access(r.Context(), r.Header, nil, apc.AceCreateBucket); err != nil {
182-
s3.WriteErr(w, r, err, http.StatusForbidden)
182+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
183183
return
184184
}
185185
msg := apc.ActMsg{Action: apc.ActCreateBck}
@@ -188,11 +188,12 @@ func (p *proxy) putBckS3(w http.ResponseWriter, r *http.Request, bucket string)
188188
}
189189
bck := meta.NewBck(bucket, apc.AIS, cmn.NsGlobal)
190190
if err := bck.Validate(); err != nil {
191-
s3.WriteErr(w, r, err, 0)
191+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
192192
return
193193
}
194194
if err := p.createBucket(&msg, bck, nil); err != nil {
195-
s3.WriteErr(w, r, err, crerrStatus(err))
195+
status := crerrStatus(err)
196+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: status})
196197
}
197198
}
198199

@@ -205,7 +206,7 @@ func (p *proxy) delBckS3(w http.ResponseWriter, r *http.Request, bucket string)
205206
return
206207
}
207208
if err := p.access(r.Context(), r.Header, bck, apc.AceDestroyBucket); err != nil {
208-
s3.WriteErr(w, r, err, http.StatusForbidden)
209+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
209210
return
210211
}
211212
msg := apc.ActMsg{Action: apc.ActDestroyBck}
@@ -219,35 +220,35 @@ func (p *proxy) delBckS3(w http.ResponseWriter, r *http.Request, bucket string)
219220
nlog.Infof("%s: %s already %q-ed, nothing to do", p, bck.String(), msg.Action)
220221
return
221222
}
222-
s3.WriteErr(w, r, err, ecode)
223+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: ecode})
223224
}
224225
}
225226

226227
// +gen:endpoint POST /s3/{bucket-name}/{object-name} [s3.QparamMptUploads=string,s3.QparamMptUploadID=string]
227228
// Handle S3 multipart upload operations
228229
func (p *proxy) handleMptUpload(w http.ResponseWriter, r *http.Request, items []string) {
229230
if len(items) < 2 {
230-
s3.WriteErr(w, r, errS3BckObj, 0)
231+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3BckObj})
231232
return
232233
}
233234
bck := p.initByNameOnly(w, r, items[0] /*bucket*/)
234235
if bck == nil {
235236
return
236237
}
237238
if err := p.access(r.Context(), r.Header, bck, apc.AcePUT); err != nil {
238-
s3.WriteErr(w, r, err, http.StatusForbidden)
239+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
239240
return
240241
}
241242
objName := s3.ObjName(items)
242243
if err := cos.ValidOname(objName); err != nil {
243-
s3.WriteErr(w, r, err, 0)
244+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
244245
return
245246
}
246247

247248
smap := p.owner.smap.get()
248249
tsi, netPub, err := smap.HrwMultiHome(bck.MakeUname(objName))
249250
if err != nil {
250-
s3.WriteErr(w, r, err, 0)
251+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
251252
return
252253
}
253254
started := time.Now()
@@ -265,13 +266,13 @@ func (p *proxy) delMultipleObjs(w http.ResponseWriter, r *http.Request, bucket s
265266
return
266267
}
267268
if err := p.access(r.Context(), r.Header, bck, apc.AceObjDELETE); err != nil {
268-
s3.WriteErr(w, r, err, http.StatusForbidden)
269+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
269270
return
270271
}
271272
decoder := xml.NewDecoder(r.Body)
272273
lst := &s3.Delete{}
273274
if err := decoder.Decode(lst); err != nil {
274-
s3.WriteErr(w, r, err, 0)
275+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
275276
return
276277
}
277278
if len(lst.Object) == 0 {
@@ -298,11 +299,11 @@ func (p *proxy) delMultipleObjs(w http.ResponseWriter, r *http.Request, bucket s
298299
query.Set(apc.QparamProvider, apc.AIS)
299300
if err := jsoniter.Unmarshal(bt, &msg2); err != nil {
300301
err = fmt.Errorf(cmn.FmtErrUnmarshal, p, "list-range action message", cos.BHead(bt), err)
301-
s3.WriteErr(w, r, err, 0)
302+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
302303
return
303304
}
304305
if _, err := p.bcastBckAction(http.MethodDelete, bucket, &msg2, query); err != nil {
305-
s3.WriteErr(w, r, err, 0)
306+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
306307
}
307308
// TODO: The client wants the response containing two lists:
308309
// - Successfully deleted objects
@@ -330,7 +331,7 @@ func (p *proxy) headBckS3(w http.ResponseWriter, r *http.Request, bucket string)
330331
return
331332
}
332333
if err := p.access(r.Context(), r.Header, bck, apc.AceBckHEAD); err != nil {
333-
s3.WriteErr(w, r, err, http.StatusForbidden)
334+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
334335
return
335336
}
336337
// From https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html:
@@ -354,7 +355,7 @@ func (p *proxy) listObjectsS3(w http.ResponseWriter, r *http.Request, bucket str
354355
return
355356
}
356357
if err := p.access(r.Context(), r.Header, bck, apc.AceObjLIST); err != nil {
357-
s3.WriteErr(w, r, err, http.StatusForbidden)
358+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
358359
return
359360
}
360361
amsg := &apc.ActMsg{Action: apc.ActList}
@@ -389,7 +390,7 @@ func (p *proxy) listObjectsS3(w http.ResponseWriter, r *http.Request, bucket str
389390
nlog.Infoln("lsoS3", bck.Cname(""), len(lst.Entries), err)
390391
}
391392
if err != nil {
392-
s3.WriteErr(w, r, err, 0)
393+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
393394
return
394395
}
395396

@@ -466,7 +467,7 @@ func (p *proxy) copyObjS3(w http.ResponseWriter, r *http.Request, items []string
466467
src = strings.Trim(src, "/")
467468
parts := strings.SplitN(src, "/", 2)
468469
if len(parts) < 2 {
469-
s3.WriteErr(w, r, errS3Obj, 0)
470+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Obj})
470471
return
471472
}
472473
// src
@@ -475,7 +476,7 @@ func (p *proxy) copyObjS3(w http.ResponseWriter, r *http.Request, items []string
475476
return
476477
}
477478
if err := p.access(r.Context(), r.Header, bckSrc, apc.AceGET); err != nil {
478-
s3.WriteErr(w, r, err, http.StatusForbidden)
479+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
479480
return
480481
}
481482
// dst
@@ -484,15 +485,15 @@ func (p *proxy) copyObjS3(w http.ResponseWriter, r *http.Request, items []string
484485
return
485486
}
486487
if err := bckDst.Allow(apc.AcePUT); err != nil {
487-
s3.WriteErr(w, r, err, http.StatusForbidden)
488+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
488489
return
489490
}
490491

491492
objName := strings.Trim(parts[1], "/")
492493
smap := p.owner.smap.get()
493494
tsi, err := smap.HrwName2T(bckSrc.MakeUname(objName))
494495
if err != nil {
495-
s3.WriteErr(w, r, err, 0)
496+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
496497
return
497498
}
498499
if cmn.Rom.V(5, cos.ModS3) {
@@ -511,23 +512,23 @@ func (p *proxy) directPutObjS3(w http.ResponseWriter, r *http.Request, items []s
511512
return
512513
}
513514
if err := p.access(r.Context(), r.Header, bck, apc.AcePUT); err != nil {
514-
s3.WriteErr(w, r, err, http.StatusForbidden)
515+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
515516
return
516517
}
517518
if len(items) < 2 {
518-
s3.WriteErr(w, r, errS3Obj, 0)
519+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Obj})
519520
return
520521
}
521522
objName := s3.ObjName(items)
522523
if err := cos.ValidOname(objName); err != nil {
523-
s3.WriteErr(w, r, err, 0)
524+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
524525
return
525526
}
526527

527528
smap := p.owner.smap.get()
528529
tsi, netPub, err := smap.HrwMultiHome(bck.MakeUname(objName))
529530
if err != nil {
530-
s3.WriteErr(w, r, err, 0)
531+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
531532
return
532533
}
533534
if cmn.Rom.V(5, cos.ModS3) {
@@ -547,27 +548,27 @@ func (p *proxy) getObjS3(w http.ResponseWriter, r *http.Request, items []string,
547548
return
548549
}
549550
if err := p.access(r.Context(), r.Header, bck, apc.AceGET); err != nil {
550-
s3.WriteErr(w, r, err, http.StatusForbidden)
551+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
551552
return
552553
}
553554
if listMultipart {
554555
p.listMultipart(w, r, bck, q)
555556
return
556557
}
557558
if len(items) < 2 {
558-
s3.WriteErr(w, r, errS3Obj, 0)
559+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3Obj})
559560
return
560561
}
561562
objName := s3.ObjName(items)
562563
if err := cos.ValidOname(objName); err != nil {
563-
s3.WriteErr(w, r, err, 0)
564+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
564565
return
565566
}
566567

567568
smap := p.owner.smap.get()
568569
tsi, netPub, err := smap.HrwMultiHome(bck.MakeUname(objName))
569570
if err != nil {
570-
s3.WriteErr(w, r, err, 0)
571+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
571572
return
572573
}
573574
if cmn.Rom.V(5, cos.ModS3) {
@@ -585,7 +586,7 @@ func (p *proxy) listMultipart(w http.ResponseWriter, r *http.Request, bck *meta.
585586
if smap.CountActiveTs() == 1 {
586587
si, err := smap.HrwName2T(bck.MakeUname(""))
587588
if err != nil {
588-
s3.WriteErr(w, r, err, 0)
589+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
589590
return
590591
}
591592
started := time.Now()
@@ -630,26 +631,26 @@ func (p *proxy) listMultipart(w http.ResponseWriter, r *http.Request, bck *meta.
630631
// Retrieve S3 object metadata and headers
631632
func (p *proxy) headObjS3(w http.ResponseWriter, r *http.Request, items []string) {
632633
if len(items) < 2 {
633-
s3.WriteErr(w, r, errS3BckObj, 0)
634+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3BckObj})
634635
return
635636
}
636637
bck := p.initByNameOnly(w, r, items[0] /*bucket*/)
637638
if bck == nil {
638639
return
639640
}
640641
if err := p.access(r.Context(), r.Header, bck, apc.AceObjHEAD); err != nil {
641-
s3.WriteErr(w, r, err, http.StatusForbidden)
642+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
642643
return
643644
}
644645
objName := s3.ObjName(items)
645646
if err := cos.ValidOname(objName); err != nil {
646-
s3.WriteErr(w, r, err, 0)
647+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
647648
return
648649
}
649650
smap := p.owner.smap.get()
650651
tsi, err := smap.HrwName2T(bck.MakeUname(objName))
651652
if err != nil {
652-
s3.WriteErr(w, r, err, http.StatusInternalServerError)
653+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusInternalServerError})
653654
return
654655
}
655656
if cmn.Rom.V(5, cos.ModS3) {
@@ -669,27 +670,27 @@ func (p *proxy) headObjS3(w http.ResponseWriter, r *http.Request, items []string
669670
// Delete an S3 object
670671
func (p *proxy) delObjS3(w http.ResponseWriter, r *http.Request, items []string) {
671672
if len(items) < 2 {
672-
s3.WriteErr(w, r, errS3BckObj, 0)
673+
s3.WriteErr(w, r, s3.ErrInfo{Err: errS3BckObj})
673674
return
674675
}
675676
bck := p.initByNameOnly(w, r, items[0] /*bucket*/)
676677
if bck == nil {
677678
return
678679
}
679680
if err := p.access(r.Context(), r.Header, bck, apc.AceObjDELETE); err != nil {
680-
s3.WriteErr(w, r, err, http.StatusForbidden)
681+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: http.StatusForbidden})
681682
return
682683
}
683684
objName := s3.ObjName(items)
684685
if err := cos.ValidOname(objName); err != nil {
685-
s3.WriteErr(w, r, err, 0)
686+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
686687
return
687688
}
688689

689690
smap := p.owner.smap.get()
690691
tsi, err := smap.HrwName2T(bck.MakeUname(objName))
691692
if err != nil {
692-
s3.WriteErr(w, r, err, 0)
693+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
693694
return
694695
}
695696
if cmn.Rom.V(5, cos.ModS3) {
@@ -718,7 +719,7 @@ func (p *proxy) getBckVersioningS3(w http.ResponseWriter, r *http.Request, bucke
718719
// GET /s3/<bucket-name>?lifecycle|cors|policy|acl
719720
func (p *proxy) unsupported(w http.ResponseWriter, r *http.Request, bucket string) {
720721
if _, ecode, err := meta.InitByNameOnly(bucket, p.owner.bmd); err != nil {
721-
s3.WriteErr(w, r, err, ecode)
722+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: ecode})
722723
return
723724
}
724725
w.WriteHeader(http.StatusNotImplemented)
@@ -739,7 +740,7 @@ func (p *proxy) putBckVersioningS3(w http.ResponseWriter, r *http.Request, bucke
739740
decoder := xml.NewDecoder(r.Body)
740741
vconf := &s3.VersioningConfiguration{}
741742
if err := decoder.Decode(vconf); err != nil {
742-
s3.WriteErr(w, r, err, 0)
743+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
743744
return
744745
}
745746
enabled := vconf.Enabled()
@@ -749,11 +750,11 @@ func (p *proxy) putBckVersioningS3(w http.ResponseWriter, r *http.Request, bucke
749750
// make and validate new props
750751
nprops, err := p.makeNewBckProps(bck, &propsToUpdate)
751752
if err != nil {
752-
s3.WriteErr(w, r, err, 0)
753+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
753754
return
754755
}
755756
if _, err := p.setBprops(msg, bck, nprops); err != nil {
756-
s3.WriteErr(w, r, err, 0)
757+
s3.WriteErr(w, r, s3.ErrInfo{Err: err})
757758
}
758759
}
759760

@@ -764,7 +765,7 @@ func (p *proxy) putBckVersioningS3(w http.ResponseWriter, r *http.Request, bucke
764765
func (p *proxy) initByNameOnly(w http.ResponseWriter, r *http.Request, bucket string) *meta.Bck {
765766
bck, ecode, err := meta.InitByNameOnly(bucket, p.owner.bmd)
766767
if err != nil {
767-
s3.WriteErr(w, r, err, ecode)
768+
s3.WriteErr(w, r, s3.ErrInfo{Err: err, Status: ecode})
768769
return nil
769770
}
770771
debug.Assert(bck != nil)

0 commit comments

Comments
 (0)