[TrueCloudLab#5] Request metrics per user

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2022-12-23 15:28:30 +03:00 committed by Alex Vanin
parent 86e881694d
commit fc5c09c084
5 changed files with 297 additions and 78 deletions

View file

@ -42,6 +42,10 @@ func transformToS3Error(err error) error {
return errors.GetAPIError(errors.ErrInternalError) return errors.GetAPIError(errors.ErrInternalError)
} }
func (h *handler) ResolveBucket(ctx context.Context, bucket string) (*data.BucketInfo, error) {
return h.obj.GetBucketInfo(ctx, bucket)
}
func (h *handler) getBucketAndCheckOwner(r *http.Request, bucket string, header ...string) (*data.BucketInfo, error) { func (h *handler) getBucketAndCheckOwner(r *http.Request, bucket string, header ...string) (*data.BucketInfo, error) {
bktInfo, err := h.obj.GetBucketInfo(r.Context(), bucket) bktInfo, err := h.obj.GetBucketInfo(r.Context(), bucket)
if err != nil { if err != nil {

View file

@ -1,4 +1,4 @@
package metrics package api
import ( import (
"io" "io"
@ -8,9 +8,70 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
"github.com/TrueCloudLab/frostfs-sdk-go/bearer"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
type RequestType int
const (
HEADRequest RequestType = iota
PUTRequest RequestType = iota
LISTRequest RequestType = iota
GETRequest RequestType = iota
DELETERequest RequestType = iota
)
func (t RequestType) String() string {
switch t {
case 0:
return "HEAD"
case 1:
return "PUT"
case 2:
return "LIST"
case 3:
return "GET"
case 4:
return "DELETE"
default:
return "Unknown"
}
}
func RequestTypeFromAPI(api string) RequestType {
switch api {
case "headobject", "headbucket":
return HEADRequest
case "createmultipartupload", "uploadpartcopy", "uploadpart", "completemutipartupload",
"putobjectacl", "putobjecttagging", "copyobject", "putobjectretention", "putobjectlegalhold",
"putobject", "putbucketcors", "putbucketacl", "putbucketlifecycle", "putbucketencryption",
"putbucketpolicy", "putbucketobjectlockconfig", "putbuckettagging", "putbucketversioning",
"putbucketnotification", "createbucket", "postobject":
return PUTRequest
case "listmultipartuploads", "listobjectsv2M", "listobjectsv2", "listbucketversions",
"listobjectsv1", "listbuckets":
return LISTRequest
case "getobjectacl", "getobjecttagging", "getobjectretention", "getobjectlegalhold",
"getobjectattributes", "getobject", "getbucketlocation", "getbucketpolicy",
"getbucketlifecycle", "getbucketencryption", "getbucketcors", "getbucketacl",
"getbucketwebsite", "getbucketaccelerate", "getbucketrequestpayment", "getbucketlogging",
"getbucketreplication", "getbuckettagging", "selectobjectcontent",
"getbucketobjectlockconfiguration", "getbucketversioning", "getbucketnotification",
"listenbucketnotification":
return GETRequest
case "abortmultipartupload", "deleteobjecttagging", "deleteobject", "deletebucketcors",
"deletebucketwebsite", "deletebuckettagging", "deletemultipleobjects", "deletebucketpolicy",
"deletebucketlifecycle", "deletebucketencryption", "deletebucket":
return DELETERequest
default:
return RequestType(-1)
}
}
type OperationList [5]int
type ( type (
// HTTPAPIStats holds statistics information about // HTTPAPIStats holds statistics information about
// the API given in the requests. // the API given in the requests.
@ -19,9 +80,25 @@ type (
sync.RWMutex sync.RWMutex
} }
UsersAPIStats struct {
users map[string]*userAPIStats
sync.RWMutex
}
bucketKey struct {
name string
cid string
}
userAPIStats struct {
buckets map[bucketKey]OperationList
user string
}
// HTTPStats holds statistics information about // HTTPStats holds statistics information about
// HTTP requests made by all clients. // HTTP requests made by all clients.
HTTPStats struct { HTTPStats struct {
usersS3Requests UsersAPIStats
currentS3Requests HTTPAPIStats currentS3Requests HTTPAPIStats
totalS3Requests HTTPAPIStats totalS3Requests HTTPAPIStats
totalS3Errors HTTPAPIStats totalS3Errors HTTPAPIStats
@ -101,6 +178,21 @@ func collectHTTPMetrics(ch chan<- prometheus.Metric) {
api, api,
) )
} }
for _, value := range httpStatsMetric.usersS3Requests.DumpMetrics() {
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
prometheus.BuildFQName("frostfs_s3", "user_requests", "count"),
"",
[]string{"user", "bucket", "cid", "operation"}, nil),
prometheus.CounterValue,
float64(value.Requests),
value.User,
value.Bucket,
value.ContainerID,
value.Operation,
)
}
} }
// APIStats wraps http handler for api with basic statistics collection. // APIStats wraps http handler for api with basic statistics collection.
@ -169,6 +261,66 @@ func (stats *HTTPAPIStats) Load() map[string]int {
return apiStats return apiStats
} }
func (u *UsersAPIStats) Update(user, bucket, cnrID string, reqType RequestType) {
u.Lock()
defer u.Unlock()
usersStat := u.users[user]
if usersStat == nil {
if u.users == nil {
u.users = make(map[string]*userAPIStats)
}
usersStat = &userAPIStats{
buckets: make(map[bucketKey]OperationList, 1),
user: user,
}
u.users[user] = usersStat
}
key := bucketKey{
name: bucket,
cid: cnrID,
}
bucketStat := usersStat.buckets[key]
bucketStat[reqType] += 1
usersStat.buckets[key] = bucketStat
}
type UserMetricsInfo struct {
User string
Bucket string
ContainerID string
Operation string
Requests int
}
func (u *UsersAPIStats) DumpMetrics() []UserMetricsInfo {
u.Lock()
defer u.Unlock()
result := make([]UserMetricsInfo, 0, len(u.users))
for user, userStat := range u.users {
for key, operations := range userStat.buckets {
for op, val := range operations {
if val != 0 {
result = append(result, UserMetricsInfo{
User: user,
Bucket: key.name,
ContainerID: key.cid,
Operation: RequestType(op).String(),
Requests: val,
})
}
}
}
}
u.users = make(map[string]*userAPIStats)
return result
}
func (st *HTTPStats) getInputBytes() uint64 { func (st *HTTPStats) getInputBytes() uint64 {
return atomic.LoadUint64(&st.totalInputBytes) return atomic.LoadUint64(&st.totalInputBytes)
} }
@ -178,26 +330,36 @@ func (st *HTTPStats) getOutputBytes() uint64 {
} }
// Update statistics from http request and response data. // Update statistics from http request and response data.
func (st *HTTPStats) updateStats(api string, w http.ResponseWriter, r *http.Request, durationSecs float64) { func (st *HTTPStats) updateStats(apiOperation string, w http.ResponseWriter, r *http.Request, durationSecs float64) {
var code int var code int
if res, ok := w.(*responseWrapper); ok { if res, ok := w.(*responseWrapper); ok {
code = res.statusCode code = res.statusCode
} }
user := "anon"
if bd, ok := r.Context().Value(BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
user = bearer.ResolveIssuer(*bd.Gate.BearerToken).String()
}
reqInfo := GetReqInfo(r.Context())
cnrID := GetCID(r.Context())
st.usersS3Requests.Update(user, reqInfo.BucketName, cnrID, RequestTypeFromAPI(apiOperation))
// A successful request has a 2xx response code // A successful request has a 2xx response code
successReq := code >= http.StatusOK && code < http.StatusMultipleChoices successReq := code >= http.StatusOK && code < http.StatusMultipleChoices
if !strings.HasSuffix(r.URL.Path, systemPath) { if !strings.HasSuffix(r.URL.Path, systemPath) {
st.totalS3Requests.Inc(api) st.totalS3Requests.Inc(apiOperation)
if !successReq && code != 0 { if !successReq && code != 0 {
st.totalS3Errors.Inc(api) st.totalS3Errors.Inc(apiOperation)
} }
} }
if r.Method == http.MethodGet { if r.Method == http.MethodGet {
// Increment the prometheus http request response histogram with appropriate label // Increment the prometheus http request response histogram with appropriate label
httpRequestsDuration.With(prometheus.Labels{"api": api}).Observe(durationSecs) httpRequestsDuration.With(prometheus.Labels{"api": apiOperation}).Observe(durationSecs)
} }
} }

View file

@ -1,4 +1,4 @@
package metrics package api
import ( import (
"github.com/TrueCloudLab/frostfs-s3-gw/internal/version" "github.com/TrueCloudLab/frostfs-s3-gw/internal/version"

View file

@ -2,6 +2,7 @@ package api
import ( import (
"context" "context"
cid "github.com/TrueCloudLab/frostfs-sdk-go/container/id"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -42,10 +43,13 @@ type (
} }
) )
// Key used for Get/SetReqInfo. // Key used for Get/Set context values.
type contextKeyType string type contextKeyType string
const ctxRequestInfo = contextKeyType("FrostFS-S3-GW") const (
ctxRequestInfo = contextKeyType("FrostFS-S3-GW")
ctxCID = contextKeyType("FrostFS-S3-GW-CID")
)
var ( var (
// De-facto standard header keys. // De-facto standard header keys.
@ -202,3 +206,21 @@ func GetReqInfo(ctx context.Context) *ReqInfo {
} }
return &ReqInfo{} return &ReqInfo{}
} }
// SetCID sets CID in the context.
func SetCID(ctx context.Context, id cid.ID) context.Context {
if ctx == nil {
return nil
}
return context.WithValue(ctx, ctxCID, id.EncodeToString())
}
// GetCID returns CID if set.
func GetCID(ctx context.Context) string {
if ctx == nil {
return ""
} else if id, ok := ctx.Value(ctxCID).(string); ok {
return id
}
return ""
}

View file

@ -6,7 +6,7 @@ import (
"sync" "sync"
"github.com/TrueCloudLab/frostfs-s3-gw/api/auth" "github.com/TrueCloudLab/frostfs-s3-gw/api/auth"
"github.com/TrueCloudLab/frostfs-s3-gw/api/metrics" "github.com/TrueCloudLab/frostfs-s3-gw/api/data"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"go.uber.org/zap" "go.uber.org/zap"
@ -82,6 +82,8 @@ type (
AbortMultipartUploadHandler(http.ResponseWriter, *http.Request) AbortMultipartUploadHandler(http.ResponseWriter, *http.Request)
ListPartsHandler(w http.ResponseWriter, r *http.Request) ListPartsHandler(w http.ResponseWriter, r *http.Request)
ListMultipartUploadsHandler(http.ResponseWriter, *http.Request) ListMultipartUploadsHandler(http.ResponseWriter, *http.Request)
ResolveBucket(ctx context.Context, bucket string) (*data.BucketInfo, error)
} }
// mimeType represents various MIME types used in API responses. // mimeType represents various MIME types used in API responses.
@ -145,6 +147,32 @@ func appendCORS(handler Handler) mux.MiddlewareFunc {
} }
} }
func resolveBucket(log *zap.Logger, resolveBucket func(ctx context.Context, bucket string) (*data.BucketInfo, error)) mux.MiddlewareFunc {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqInfo := GetReqInfo(r.Context())
if reqInfo.BucketName != "" {
bktInfo, err := resolveBucket(r.Context(), reqInfo.BucketName)
if err != nil {
code := WriteErrorResponse(w, reqInfo, err)
log.Error("failed to resolve bucket", zap.Int("status", code),
zap.String("request_id", reqInfo.RequestID), zap.String("method", reqInfo.API),
zap.String("bucket", reqInfo.BucketName), zap.String("object", reqInfo.ObjectName),
zap.Error(err))
return
}
// todo: (@KirillovDenis) consider save bktInfo into ReqInfo
// (in order to optimize resolving bucket in further handlers)
r = r.WithContext(SetCID(r.Context(), bktInfo.CID))
}
h.ServeHTTP(w, r)
})
}
}
func logErrorResponse(l *zap.Logger) mux.MiddlewareFunc { func logErrorResponse(l *zap.Logger) mux.MiddlewareFunc {
return func(h http.Handler) http.Handler { return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -192,6 +220,9 @@ func Attach(r *mux.Router, domains []string, m MaxClients, h Handler, center aut
// -- prepare request // -- prepare request
setRequestID, setRequestID,
// -- resolve bucket to set cid in context
resolveBucket(log, h.ResolveBucket),
// -- logging error requests // -- logging error requests
logErrorResponse(log), logErrorResponse(log),
) )
@ -213,277 +244,277 @@ func Attach(r *mux.Router, domains []string, m MaxClients, h Handler, center aut
// -- append CORS headers to a response for // -- append CORS headers to a response for
appendCORS(h), appendCORS(h),
) )
bucket.Methods(http.MethodOptions).HandlerFunc(m.Handle(metrics.APIStats("preflight", h.Preflight))).Name("Options") bucket.Methods(http.MethodOptions).HandlerFunc(m.Handle(APIStats("preflight", h.Preflight))).Name("Options")
bucket.Methods(http.MethodHead).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodHead).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("headobject", h.HeadObjectHandler))).Name("HeadObject") m.Handle(APIStats("headobject", h.HeadObjectHandler))).Name("HeadObject")
// CopyObjectPart // CopyObjectPart
bucket.Methods(http.MethodPut).Path("/{object:.+}").Headers(hdrAmzCopySource, "").HandlerFunc(m.Handle(metrics.APIStats("uploadpartcopy", h.UploadPartCopy))).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}"). bucket.Methods(http.MethodPut).Path("/{object:.+}").Headers(hdrAmzCopySource, "").HandlerFunc(m.Handle(APIStats("uploadpartcopy", h.UploadPartCopy))).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").
Name("UploadPartCopy") Name("UploadPartCopy")
// PutObjectPart // PutObjectPart
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("uploadpart", h.UploadPartHandler))).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}"). m.Handle(APIStats("uploadpart", h.UploadPartHandler))).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").
Name("UploadPart") Name("UploadPart")
// ListParts // ListParts
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("listobjectparts", h.ListPartsHandler))).Queries("uploadId", "{uploadId:.*}"). m.Handle(APIStats("listobjectparts", h.ListPartsHandler))).Queries("uploadId", "{uploadId:.*}").
Name("ListObjectParts") Name("ListObjectParts")
// CompleteMultipartUpload // CompleteMultipartUpload
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("completemutipartupload", h.CompleteMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}"). m.Handle(APIStats("completemutipartupload", h.CompleteMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}").
Name("CompleteMultipartUpload") Name("CompleteMultipartUpload")
// CreateMultipartUpload // CreateMultipartUpload
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("createmultipartupload", h.CreateMultipartUploadHandler))).Queries("uploads", ""). m.Handle(APIStats("createmultipartupload", h.CreateMultipartUploadHandler))).Queries("uploads", "").
Name("CreateMultipartUpload") Name("CreateMultipartUpload")
// AbortMultipartUpload // AbortMultipartUpload
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("abortmultipartupload", h.AbortMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}"). m.Handle(APIStats("abortmultipartupload", h.AbortMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}").
Name("AbortMultipartUpload") Name("AbortMultipartUpload")
// ListMultipartUploads // ListMultipartUploads
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("listmultipartuploads", h.ListMultipartUploadsHandler))).Queries("uploads", ""). m.Handle(APIStats("listmultipartuploads", h.ListMultipartUploadsHandler))).Queries("uploads", "").
Name("ListMultipartUploads") Name("ListMultipartUploads")
// GetObjectACL -- this is a dummy call. // GetObjectACL -- this is a dummy call.
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("getobjectacl", h.GetObjectACLHandler))).Queries("acl", ""). m.Handle(APIStats("getobjectacl", h.GetObjectACLHandler))).Queries("acl", "").
Name("GetObjectACL") Name("GetObjectACL")
// PutObjectACL -- this is a dummy call. // PutObjectACL -- this is a dummy call.
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("putobjectacl", h.PutObjectACLHandler))).Queries("acl", ""). m.Handle(APIStats("putobjectacl", h.PutObjectACLHandler))).Queries("acl", "").
Name("PutObjectACL") Name("PutObjectACL")
// GetObjectTagging // GetObjectTagging
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("getobjecttagging", h.GetObjectTaggingHandler))).Queries("tagging", ""). m.Handle(APIStats("getobjecttagging", h.GetObjectTaggingHandler))).Queries("tagging", "").
Name("GetObjectTagging") Name("GetObjectTagging")
// PutObjectTagging // PutObjectTagging
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("putobjecttagging", h.PutObjectTaggingHandler))).Queries("tagging", ""). m.Handle(APIStats("putobjecttagging", h.PutObjectTaggingHandler))).Queries("tagging", "").
Name("PutObjectTagging") Name("PutObjectTagging")
// DeleteObjectTagging // DeleteObjectTagging
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("deleteobjecttagging", h.DeleteObjectTaggingHandler))).Queries("tagging", ""). m.Handle(APIStats("deleteobjecttagging", h.DeleteObjectTaggingHandler))).Queries("tagging", "").
Name("DeleteObjectTagging") Name("DeleteObjectTagging")
// SelectObjectContent // SelectObjectContent
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("selectobjectcontent", h.SelectObjectContentHandler))).Queries("select", "").Queries("select-type", "2"). m.Handle(APIStats("selectobjectcontent", h.SelectObjectContentHandler))).Queries("select", "").Queries("select-type", "2").
Name("SelectObjectContent") Name("SelectObjectContent")
// GetObjectRetention // GetObjectRetention
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("getobjectretention", h.GetObjectRetentionHandler))).Queries("retention", ""). m.Handle(APIStats("getobjectretention", h.GetObjectRetentionHandler))).Queries("retention", "").
Name("GetObjectRetention") Name("GetObjectRetention")
// GetObjectLegalHold // GetObjectLegalHold
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("getobjectlegalhold", h.GetObjectLegalHoldHandler))).Queries("legal-hold", ""). m.Handle(APIStats("getobjectlegalhold", h.GetObjectLegalHoldHandler))).Queries("legal-hold", "").
Name("GetObjectLegalHold") Name("GetObjectLegalHold")
// GetObjectAttributes // GetObjectAttributes
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("getobjectattributes", h.GetObjectAttributesHandler))).Queries("attributes", ""). m.Handle(APIStats("getobjectattributes", h.GetObjectAttributesHandler))).Queries("attributes", "").
Name("GetObjectAttributes") Name("GetObjectAttributes")
// GetObject // GetObject
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("getobject", h.GetObjectHandler))). m.Handle(APIStats("getobject", h.GetObjectHandler))).
Name("GetObject") Name("GetObject")
// CopyObject // CopyObject
bucket.Methods(http.MethodPut).Path("/{object:.+}").Headers(hdrAmzCopySource, "").HandlerFunc(m.Handle(metrics.APIStats("copyobject", h.CopyObjectHandler))). bucket.Methods(http.MethodPut).Path("/{object:.+}").Headers(hdrAmzCopySource, "").HandlerFunc(m.Handle(APIStats("copyobject", h.CopyObjectHandler))).
Name("CopyObject") Name("CopyObject")
// PutObjectRetention // PutObjectRetention
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("putobjectretention", h.PutObjectRetentionHandler))).Queries("retention", ""). m.Handle(APIStats("putobjectretention", h.PutObjectRetentionHandler))).Queries("retention", "").
Name("PutObjectRetention") Name("PutObjectRetention")
// PutObjectLegalHold // PutObjectLegalHold
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("putobjectlegalhold", h.PutObjectLegalHoldHandler))).Queries("legal-hold", ""). m.Handle(APIStats("putobjectlegalhold", h.PutObjectLegalHoldHandler))).Queries("legal-hold", "").
Name("PutObjectLegalHold") Name("PutObjectLegalHold")
// PutObject // PutObject
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("putobject", h.PutObjectHandler))). m.Handle(APIStats("putobject", h.PutObjectHandler))).
Name("PutObject") Name("PutObject")
// DeleteObject // DeleteObject
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc( bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
m.Handle(metrics.APIStats("deleteobject", h.DeleteObjectHandler))). m.Handle(APIStats("deleteobject", h.DeleteObjectHandler))).
Name("DeleteObject") Name("DeleteObject")
// Bucket operations // Bucket operations
// GetBucketLocation // GetBucketLocation
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketlocation", h.GetBucketLocationHandler))).Queries("location", ""). m.Handle(APIStats("getbucketlocation", h.GetBucketLocationHandler))).Queries("location", "").
Name("GetBucketLocation") Name("GetBucketLocation")
// GetBucketPolicy // GetBucketPolicy
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketpolicy", h.GetBucketPolicyHandler))).Queries("policy", ""). m.Handle(APIStats("getbucketpolicy", h.GetBucketPolicyHandler))).Queries("policy", "").
Name("GetBucketPolicy") Name("GetBucketPolicy")
// GetBucketLifecycle // GetBucketLifecycle
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketlifecycle", h.GetBucketLifecycleHandler))).Queries("lifecycle", ""). m.Handle(APIStats("getbucketlifecycle", h.GetBucketLifecycleHandler))).Queries("lifecycle", "").
Name("GetBucketLifecycle") Name("GetBucketLifecycle")
// GetBucketEncryption // GetBucketEncryption
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketencryption", h.GetBucketEncryptionHandler))).Queries("encryption", ""). m.Handle(APIStats("getbucketencryption", h.GetBucketEncryptionHandler))).Queries("encryption", "").
Name("GetBucketEncryption") Name("GetBucketEncryption")
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketcors", h.GetBucketCorsHandler))).Queries("cors", ""). m.Handle(APIStats("getbucketcors", h.GetBucketCorsHandler))).Queries("cors", "").
Name("GetBucketCors") Name("GetBucketCors")
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketcors", h.PutBucketCorsHandler))).Queries("cors", ""). m.Handle(APIStats("putbucketcors", h.PutBucketCorsHandler))).Queries("cors", "").
Name("PutBucketCors") Name("PutBucketCors")
bucket.Methods(http.MethodDelete).HandlerFunc( bucket.Methods(http.MethodDelete).HandlerFunc(
m.Handle(metrics.APIStats("deletebucketcors", h.DeleteBucketCorsHandler))).Queries("cors", ""). m.Handle(APIStats("deletebucketcors", h.DeleteBucketCorsHandler))).Queries("cors", "").
Name("DeleteBucketCors") Name("DeleteBucketCors")
// Dummy Bucket Calls // Dummy Bucket Calls
// GetBucketACL -- this is a dummy call. // GetBucketACL -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketacl", h.GetBucketACLHandler))).Queries("acl", ""). m.Handle(APIStats("getbucketacl", h.GetBucketACLHandler))).Queries("acl", "").
Name("GetBucketACL") Name("GetBucketACL")
// PutBucketACL -- this is a dummy call. // PutBucketACL -- this is a dummy call.
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketacl", h.PutBucketACLHandler))).Queries("acl", ""). m.Handle(APIStats("putbucketacl", h.PutBucketACLHandler))).Queries("acl", "").
Name("PutBucketACL") Name("PutBucketACL")
// GetBucketWebsiteHandler -- this is a dummy call. // GetBucketWebsiteHandler -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketwebsite", h.GetBucketWebsiteHandler))).Queries("website", ""). m.Handle(APIStats("getbucketwebsite", h.GetBucketWebsiteHandler))).Queries("website", "").
Name("GetBucketWebsite") Name("GetBucketWebsite")
// GetBucketAccelerateHandler -- this is a dummy call. // GetBucketAccelerateHandler -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketaccelerate", h.GetBucketAccelerateHandler))).Queries("accelerate", ""). m.Handle(APIStats("getbucketaccelerate", h.GetBucketAccelerateHandler))).Queries("accelerate", "").
Name("GetBucketAccelerate") Name("GetBucketAccelerate")
// GetBucketRequestPaymentHandler -- this is a dummy call. // GetBucketRequestPaymentHandler -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketrequestpayment", h.GetBucketRequestPaymentHandler))).Queries("requestPayment", ""). m.Handle(APIStats("getbucketrequestpayment", h.GetBucketRequestPaymentHandler))).Queries("requestPayment", "").
Name("GetBucketRequestPayment") Name("GetBucketRequestPayment")
// GetBucketLoggingHandler -- this is a dummy call. // GetBucketLoggingHandler -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketlogging", h.GetBucketLoggingHandler))).Queries("logging", ""). m.Handle(APIStats("getbucketlogging", h.GetBucketLoggingHandler))).Queries("logging", "").
Name("GetBucketLogging") Name("GetBucketLogging")
// GetBucketLifecycleHandler -- this is a dummy call. // GetBucketLifecycleHandler -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketlifecycle", h.GetBucketLifecycleHandler))).Queries("lifecycle", ""). m.Handle(APIStats("getbucketlifecycle", h.GetBucketLifecycleHandler))).Queries("lifecycle", "").
Name("GetBucketLifecycle") Name("GetBucketLifecycle")
// GetBucketReplicationHandler -- this is a dummy call. // GetBucketReplicationHandler -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketreplication", h.GetBucketReplicationHandler))).Queries("replication", ""). m.Handle(APIStats("getbucketreplication", h.GetBucketReplicationHandler))).Queries("replication", "").
Name("GetBucketReplication") Name("GetBucketReplication")
// GetBucketTaggingHandler // GetBucketTaggingHandler
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbuckettagging", h.GetBucketTaggingHandler))).Queries("tagging", ""). m.Handle(APIStats("getbuckettagging", h.GetBucketTaggingHandler))).Queries("tagging", "").
Name("GetBucketTagging") Name("GetBucketTagging")
// DeleteBucketWebsiteHandler // DeleteBucketWebsiteHandler
bucket.Methods(http.MethodDelete).HandlerFunc( bucket.Methods(http.MethodDelete).HandlerFunc(
m.Handle(metrics.APIStats("deletebucketwebsite", h.DeleteBucketWebsiteHandler))).Queries("website", ""). m.Handle(APIStats("deletebucketwebsite", h.DeleteBucketWebsiteHandler))).Queries("website", "").
Name("DeleteBucketWebsite") Name("DeleteBucketWebsite")
// DeleteBucketTaggingHandler // DeleteBucketTaggingHandler
bucket.Methods(http.MethodDelete).HandlerFunc( bucket.Methods(http.MethodDelete).HandlerFunc(
m.Handle(metrics.APIStats("deletebuckettagging", h.DeleteBucketTaggingHandler))).Queries("tagging", ""). m.Handle(APIStats("deletebuckettagging", h.DeleteBucketTaggingHandler))).Queries("tagging", "").
Name("DeleteBucketTagging") Name("DeleteBucketTagging")
// GetBucketObjectLockConfig // GetBucketObjectLockConfig
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketobjectlockconfiguration", h.GetBucketObjectLockConfigHandler))).Queries("object-lock", ""). m.Handle(APIStats("getbucketobjectlockconfiguration", h.GetBucketObjectLockConfigHandler))).Queries("object-lock", "").
Name("GetBucketObjectLockConfig") Name("GetBucketObjectLockConfig")
// GetBucketVersioning // GetBucketVersioning
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketversioning", h.GetBucketVersioningHandler))).Queries("versioning", ""). m.Handle(APIStats("getbucketversioning", h.GetBucketVersioningHandler))).Queries("versioning", "").
Name("GetBucketVersioning") Name("GetBucketVersioning")
// GetBucketNotification // GetBucketNotification
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("getbucketnotification", h.GetBucketNotificationHandler))).Queries("notification", ""). m.Handle(APIStats("getbucketnotification", h.GetBucketNotificationHandler))).Queries("notification", "").
Name("GetBucketNotification") Name("GetBucketNotification")
// ListenBucketNotification // ListenBucketNotification
bucket.Methods(http.MethodGet).HandlerFunc(metrics.APIStats("listenbucketnotification", h.ListenBucketNotificationHandler)).Queries("events", "{events:.*}"). bucket.Methods(http.MethodGet).HandlerFunc(APIStats("listenbucketnotification", h.ListenBucketNotificationHandler)).Queries("events", "{events:.*}").
Name("ListenBucketNotification") Name("ListenBucketNotification")
// ListObjectsV2M // ListObjectsV2M
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("listobjectsv2M", h.ListObjectsV2MHandler))).Queries("list-type", "2", "metadata", "true"). m.Handle(APIStats("listobjectsv2M", h.ListObjectsV2MHandler))).Queries("list-type", "2", "metadata", "true").
Name("ListObjectsV2M") Name("ListObjectsV2M")
// ListObjectsV2 // ListObjectsV2
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("listobjectsv2", h.ListObjectsV2Handler))).Queries("list-type", "2"). m.Handle(APIStats("listobjectsv2", h.ListObjectsV2Handler))).Queries("list-type", "2").
Name("ListObjectsV2") Name("ListObjectsV2")
// ListBucketVersions // ListBucketVersions
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("listbucketversions", h.ListBucketObjectVersionsHandler))).Queries("versions", ""). m.Handle(APIStats("listbucketversions", h.ListBucketObjectVersionsHandler))).Queries("versions", "").
Name("ListBucketVersions") Name("ListBucketVersions")
// ListObjectsV1 (Legacy) // ListObjectsV1 (Legacy)
bucket.Methods(http.MethodGet).HandlerFunc( bucket.Methods(http.MethodGet).HandlerFunc(
m.Handle(metrics.APIStats("listobjectsv1", h.ListObjectsV1Handler))). m.Handle(APIStats("listobjectsv1", h.ListObjectsV1Handler))).
Name("ListObjectsV1") Name("ListObjectsV1")
// PutBucketLifecycle // PutBucketLifecycle
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketlifecycle", h.PutBucketLifecycleHandler))).Queries("lifecycle", ""). m.Handle(APIStats("putbucketlifecycle", h.PutBucketLifecycleHandler))).Queries("lifecycle", "").
Name("PutBucketLifecycle") Name("PutBucketLifecycle")
// PutBucketEncryption // PutBucketEncryption
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketencryption", h.PutBucketEncryptionHandler))).Queries("encryption", ""). m.Handle(APIStats("putbucketencryption", h.PutBucketEncryptionHandler))).Queries("encryption", "").
Name("PutBucketEncryption") Name("PutBucketEncryption")
// PutBucketPolicy // PutBucketPolicy
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketpolicy", h.PutBucketPolicyHandler))).Queries("policy", ""). m.Handle(APIStats("putbucketpolicy", h.PutBucketPolicyHandler))).Queries("policy", "").
Name("PutBucketPolicy") Name("PutBucketPolicy")
// PutBucketObjectLockConfig // PutBucketObjectLockConfig
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketobjectlockconfig", h.PutBucketObjectLockConfigHandler))).Queries("object-lock", ""). m.Handle(APIStats("putbucketobjectlockconfig", h.PutBucketObjectLockConfigHandler))).Queries("object-lock", "").
Name("PutBucketObjectLockConfig") Name("PutBucketObjectLockConfig")
// PutBucketTaggingHandler // PutBucketTaggingHandler
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbuckettagging", h.PutBucketTaggingHandler))).Queries("tagging", ""). m.Handle(APIStats("putbuckettagging", h.PutBucketTaggingHandler))).Queries("tagging", "").
Name("PutBucketTagging") Name("PutBucketTagging")
// PutBucketVersioning // PutBucketVersioning
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketversioning", h.PutBucketVersioningHandler))).Queries("versioning", ""). m.Handle(APIStats("putbucketversioning", h.PutBucketVersioningHandler))).Queries("versioning", "").
Name("PutBucketVersioning") Name("PutBucketVersioning")
// PutBucketNotification // PutBucketNotification
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketnotification", h.PutBucketNotificationHandler))).Queries("notification", ""). m.Handle(APIStats("putbucketnotification", h.PutBucketNotificationHandler))).Queries("notification", "").
Name("PutBucketNotification") Name("PutBucketNotification")
// CreateBucket // CreateBucket
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("createbucket", h.CreateBucketHandler))). m.Handle(APIStats("createbucket", h.CreateBucketHandler))).
Name("CreateBucket") Name("CreateBucket")
// HeadBucket // HeadBucket
bucket.Methods(http.MethodHead).HandlerFunc( bucket.Methods(http.MethodHead).HandlerFunc(
m.Handle(metrics.APIStats("headbucket", h.HeadBucketHandler))). m.Handle(APIStats("headbucket", h.HeadBucketHandler))).
Name("HeadBucket") Name("HeadBucket")
// PostPolicy // PostPolicy
bucket.Methods(http.MethodPost).HeadersRegexp(hdrContentType, "multipart/form-data*").HandlerFunc( bucket.Methods(http.MethodPost).HeadersRegexp(hdrContentType, "multipart/form-data*").HandlerFunc(
m.Handle(metrics.APIStats("postobject", h.PostObject))). m.Handle(APIStats("postobject", h.PostObject))).
Name("PostObject") Name("PostObject")
// DeleteMultipleObjects // DeleteMultipleObjects
bucket.Methods(http.MethodPost).HandlerFunc( bucket.Methods(http.MethodPost).HandlerFunc(
m.Handle(metrics.APIStats("deletemultipleobjects", h.DeleteMultipleObjectsHandler))).Queries("delete", ""). m.Handle(APIStats("deletemultipleobjects", h.DeleteMultipleObjectsHandler))).Queries("delete", "").
Name("DeleteMultipleObjects") Name("DeleteMultipleObjects")
// DeleteBucketPolicy // DeleteBucketPolicy
bucket.Methods(http.MethodDelete).HandlerFunc( bucket.Methods(http.MethodDelete).HandlerFunc(
m.Handle(metrics.APIStats("deletebucketpolicy", h.DeleteBucketPolicyHandler))).Queries("policy", ""). m.Handle(APIStats("deletebucketpolicy", h.DeleteBucketPolicyHandler))).Queries("policy", "").
Name("DeleteBucketPolicy") Name("DeleteBucketPolicy")
// DeleteBucketLifecycle // DeleteBucketLifecycle
bucket.Methods(http.MethodDelete).HandlerFunc( bucket.Methods(http.MethodDelete).HandlerFunc(
m.Handle(metrics.APIStats("deletebucketlifecycle", h.DeleteBucketLifecycleHandler))).Queries("lifecycle", ""). m.Handle(APIStats("deletebucketlifecycle", h.DeleteBucketLifecycleHandler))).Queries("lifecycle", "").
Name("DeleteBucketLifecycle") Name("DeleteBucketLifecycle")
// DeleteBucketEncryption // DeleteBucketEncryption
bucket.Methods(http.MethodDelete).HandlerFunc( bucket.Methods(http.MethodDelete).HandlerFunc(
m.Handle(metrics.APIStats("deletebucketencryption", h.DeleteBucketEncryptionHandler))).Queries("encryption", ""). m.Handle(APIStats("deletebucketencryption", h.DeleteBucketEncryptionHandler))).Queries("encryption", "").
Name("DeleteBucketEncryption") Name("DeleteBucketEncryption")
// DeleteBucket // DeleteBucket
bucket.Methods(http.MethodDelete).HandlerFunc( bucket.Methods(http.MethodDelete).HandlerFunc(
m.Handle(metrics.APIStats("deletebucket", h.DeleteBucketHandler))). m.Handle(APIStats("deletebucket", h.DeleteBucketHandler))).
Name("DeleteBucket") Name("DeleteBucket")
} }
// Root operation // Root operation
// ListBuckets // ListBuckets
api.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc( api.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc(
m.Handle(metrics.APIStats("listbuckets", h.ListBucketsHandler))). m.Handle(APIStats("listbuckets", h.ListBucketsHandler))).
Name("ListBuckets") Name("ListBuckets")
// S3 browser with signature v4 adds '//' for ListBuckets request, so rather // S3 browser with signature v4 adds '//' for ListBuckets request, so rather
// than failing with UnknownAPIRequest we simply handle it for now. // than failing with UnknownAPIRequest we simply handle it for now.
api.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).HandlerFunc( api.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).HandlerFunc(
m.Handle(metrics.APIStats("listbuckets", h.ListBucketsHandler))). m.Handle(APIStats("listbuckets", h.ListBucketsHandler))).
Name("ListBuckets") Name("ListBuckets")
// If none of the routes match, add default error handler routes // If none of the routes match, add default error handler routes
api.NotFoundHandler = metrics.APIStats("notfound", errorResponseHandler) api.NotFoundHandler = APIStats("notfound", errorResponseHandler)
api.MethodNotAllowedHandler = metrics.APIStats("methodnotallowed", errorResponseHandler) api.MethodNotAllowedHandler = APIStats("methodnotallowed", errorResponseHandler)
} }