forked from TrueCloudLab/frostfs-s3-gw
NFSSVC-30 Isolate S3 routing from legacy code
This commit is contained in:
parent
1c6da41bee
commit
c6bc8c513b
15 changed files with 3877 additions and 23 deletions
17
auth/errors.go
Normal file
17
auth/errors.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package auth
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
// Minimum length for MinIO access key.
|
||||
accessKeyMinLen = 3
|
||||
|
||||
// Minimum length for MinIO secret key for both server and gateway mode.
|
||||
secretKeyMinLen = 8
|
||||
)
|
||||
|
||||
// Common errors generated for access and secret key validation.
|
||||
var (
|
||||
ErrInvalidAccessKeyLength = fmt.Errorf("access key must be minimum %v or more characters long", accessKeyMinLen)
|
||||
ErrInvalidSecretKeyLength = fmt.Errorf("secret key must be minimum %v or more characters long", secretKeyMinLen)
|
||||
)
|
|
@ -20,5 +20,7 @@ func attachNewUserAuth(router *mux.Router, center *auth.Center, log *zap.Logger)
|
|||
|
||||
})
|
||||
}
|
||||
// TODO: should not be used for all routes,
|
||||
// only for API
|
||||
router.Use(uamw)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@ const (
|
|||
|
||||
defaultKeepaliveTime = 10 * time.Second
|
||||
defaultKeepaliveTimeout = 10 * time.Second
|
||||
|
||||
defaultMaxClientsCount = 100
|
||||
defaultMaxClientsDeadline = time.Second * 30
|
||||
)
|
||||
|
||||
const ( // settings
|
||||
|
@ -69,6 +72,10 @@ const ( // settings
|
|||
cfgRequestTimeout = "request_timeout"
|
||||
cfgRebalanceTimer = "rebalance_timer"
|
||||
|
||||
// MaxClients
|
||||
cfgMaxClientsCount = "max_clients_count"
|
||||
cfgMaxClientsDeadline = "max_clients_deadline"
|
||||
|
||||
// gRPC
|
||||
cfgGRPCVerbose = "verbose"
|
||||
|
||||
|
@ -108,14 +115,16 @@ func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*auth.Center, error) {
|
|||
uapk := v.GetString(cfgUserAuthPrivateKey)
|
||||
userAuthPrivateKey, err = auth.ReadRSAPrivateKeyFromPEMFile(uapk)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not load UserAuth private key")
|
||||
return nil, errors.Wrapf(err, "could not load UserAuth private key %q", uapk)
|
||||
}
|
||||
center, err := auth.NewCenter(l)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create auth center")
|
||||
}
|
||||
center.SetUserAuthKeys(userAuthPrivateKey)
|
||||
center.SetNeoFSKeys(neofsPrivateKey)
|
||||
if err = center.SetNeoFSKeys(neofsPrivateKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return center, nil
|
||||
}
|
||||
|
||||
|
@ -168,6 +177,9 @@ func newSettings() *viper.Viper {
|
|||
flags.Duration(cfgConnectTimeout, defaultConnectTimeout, "set gRPC connect timeout")
|
||||
flags.Duration(cfgRebalanceTimer, defaultRebalanceTimer, "set gRPC connection rebalance timer")
|
||||
|
||||
flags.Int(cfgMaxClientsCount, defaultMaxClientsCount, "set max-clients count")
|
||||
flags.Duration(cfgMaxClientsDeadline, defaultMaxClientsDeadline, "set max-clients deadline")
|
||||
|
||||
ttl := flags.DurationP(cfgConnectionTTL, "t", defaultTTL, "set gRPC connection time to live")
|
||||
|
||||
flags.String(cfgListenAddress, "0.0.0.0:8080", "set address to listen")
|
||||
|
|
|
@ -8,14 +8,17 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/minio/minio/auth"
|
||||
minio "github.com/minio/minio/legacy"
|
||||
"github.com/minio/minio/legacy/config"
|
||||
"github.com/minio/minio/neofs/api"
|
||||
"github.com/minio/minio/neofs/api/handler"
|
||||
"github.com/minio/minio/neofs/layer"
|
||||
"github.com/minio/minio/neofs/metrics"
|
||||
"github.com/minio/minio/neofs/pool"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
|
||||
// should be removed in future
|
||||
"github.com/minio/minio/legacy"
|
||||
"github.com/minio/minio/legacy/config"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -25,13 +28,16 @@ type (
|
|||
log *zap.Logger
|
||||
cfg *viper.Viper
|
||||
tls *tlsConfig
|
||||
obj minio.ObjectLayer
|
||||
obj legacy.ObjectLayer
|
||||
api api.Handler
|
||||
|
||||
conTimeout time.Duration
|
||||
reqTimeout time.Duration
|
||||
|
||||
reBalance time.Duration
|
||||
|
||||
maxClients api.MaxClients
|
||||
|
||||
webDone chan struct{}
|
||||
wrkDone chan struct{}
|
||||
}
|
||||
|
@ -47,10 +53,14 @@ func newApp(l *zap.Logger, v *viper.Viper) *App {
|
|||
err error
|
||||
cli pool.Pool
|
||||
tls *tlsConfig
|
||||
obj minio.ObjectLayer
|
||||
caller api.Handler
|
||||
obj legacy.ObjectLayer
|
||||
reBalance = defaultRebalanceTimer
|
||||
conTimeout = defaultConnectTimeout
|
||||
reqTimeout = defaultRequestTimeout
|
||||
|
||||
maxClientsCount = defaultMaxClientsCount
|
||||
maxClientsDeadline = defaultMaxClientsDeadline
|
||||
)
|
||||
|
||||
center, err := fetchAuthCenter(l, v)
|
||||
|
@ -60,6 +70,10 @@ func newApp(l *zap.Logger, v *viper.Viper) *App {
|
|||
uid := center.GetOwnerID()
|
||||
wif := center.GetWIFString()
|
||||
|
||||
if caller, err = handler.New(); err != nil {
|
||||
l.Fatal("could not initialize API handler", zap.Error(err))
|
||||
}
|
||||
|
||||
if v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile) {
|
||||
tls = &tlsConfig{
|
||||
KeyFile: v.GetString(cfgTLSKeyFile),
|
||||
|
@ -75,6 +89,14 @@ func newApp(l *zap.Logger, v *viper.Viper) *App {
|
|||
reqTimeout = v
|
||||
}
|
||||
|
||||
if v := v.GetInt(cfgMaxClientsCount); v > 0 {
|
||||
maxClientsCount = v
|
||||
}
|
||||
|
||||
if v := v.GetDuration(cfgMaxClientsDeadline); v > 0 {
|
||||
maxClientsDeadline = v
|
||||
}
|
||||
|
||||
poolConfig := &pool.Config{
|
||||
ConnectionTTL: v.GetDuration(cfgConnectionTTL),
|
||||
ConnectTimeout: v.GetDuration(cfgConnectTimeout),
|
||||
|
@ -133,12 +155,15 @@ func newApp(l *zap.Logger, v *viper.Viper) *App {
|
|||
cfg: v,
|
||||
obj: obj,
|
||||
tls: tls,
|
||||
api: caller,
|
||||
|
||||
webDone: make(chan struct{}, 1),
|
||||
wrkDone: make(chan struct{}, 1),
|
||||
|
||||
reBalance: reBalance,
|
||||
|
||||
maxClients: api.NewMaxClientsMiddleware(maxClientsCount, maxClientsDeadline),
|
||||
|
||||
conTimeout: conTimeout,
|
||||
reqTimeout: reqTimeout,
|
||||
}
|
||||
|
@ -179,23 +204,8 @@ func (a *App) Server(ctx context.Context) {
|
|||
attachMetrics(router, a.cfg, a.log)
|
||||
attachProfiler(router, a.cfg, a.log)
|
||||
|
||||
{ // Example for metrics.Middleware and metrics.APIStats
|
||||
r := router.PathPrefix("/test-metrics").Subrouter()
|
||||
r.Handle("/foo", metrics.APIStats("foo", func(w http.ResponseWriter, r *http.Request) {
|
||||
// do something
|
||||
}))
|
||||
|
||||
m := r.PathPrefix("/bar").Subrouter()
|
||||
m.Use(metrics.Middleware)
|
||||
m.Handle("", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// do something
|
||||
}))
|
||||
}
|
||||
|
||||
// Attach S3 API:
|
||||
r := router.PathPrefix(minio.SlashSeparator).Subrouter()
|
||||
r.Use(metrics.Middleware)
|
||||
minio.AttachS3API(r, a.obj, a.log)
|
||||
api.Attach(router, a.maxClients, a.api)
|
||||
|
||||
// Use mux.Router as http.Handler
|
||||
srv.Handler = router
|
||||
|
|
63
neofs/api/crypto/errors.go
Normal file
63
neofs/api/crypto/errors.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package crypto
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error is the generic type for any error happening during decrypting
|
||||
// an object. It indicates that the object itself or its metadata was
|
||||
// modified accidentally or maliciously.
|
||||
type Error struct {
|
||||
err error
|
||||
}
|
||||
|
||||
// Errorf - formats according to a format specifier and returns
|
||||
// the string as a value that satisfies error of type crypto.Error
|
||||
func Errorf(format string, a ...interface{}) error {
|
||||
return Error{err: fmt.Errorf(format, a...)}
|
||||
}
|
||||
|
||||
// Unwrap the internal error.
|
||||
func (e Error) Unwrap() error { return e.err }
|
||||
|
||||
// Error 'error' compatible method.
|
||||
func (e Error) Error() string {
|
||||
if e.err == nil {
|
||||
return "crypto: cause <nil>"
|
||||
}
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrInvalidEncryptionMethod indicates that the specified SSE encryption method
|
||||
// is not supported.
|
||||
ErrInvalidEncryptionMethod = Errorf("The encryption method is not supported")
|
||||
|
||||
// ErrInvalidCustomerAlgorithm indicates that the specified SSE-C algorithm
|
||||
// is not supported.
|
||||
ErrInvalidCustomerAlgorithm = Errorf("The SSE-C algorithm is not supported")
|
||||
|
||||
// ErrMissingCustomerKey indicates that the HTTP headers contains no SSE-C client key.
|
||||
ErrMissingCustomerKey = Errorf("The SSE-C request is missing the customer key")
|
||||
|
||||
// ErrMissingCustomerKeyMD5 indicates that the HTTP headers contains no SSE-C client key
|
||||
// MD5 checksum.
|
||||
ErrMissingCustomerKeyMD5 = Errorf("The SSE-C request is missing the customer key MD5")
|
||||
|
||||
// ErrInvalidCustomerKey indicates that the SSE-C client key is not valid - e.g. not a
|
||||
// base64-encoded string or not 256 bits long.
|
||||
ErrInvalidCustomerKey = Errorf("The SSE-C client key is invalid")
|
||||
|
||||
// ErrSecretKeyMismatch indicates that the provided secret key (SSE-C client key / SSE-S3 KMS key)
|
||||
// does not match the secret key used during encrypting the object.
|
||||
ErrSecretKeyMismatch = Errorf("The secret key does not match the secret key used during upload")
|
||||
|
||||
// ErrCustomerKeyMD5Mismatch indicates that the SSE-C key MD5 does not match the
|
||||
// computed MD5 sum. This means that the client provided either the wrong key for
|
||||
// a certain MD5 checksum or the wrong MD5 for a certain key.
|
||||
ErrCustomerKeyMD5Mismatch = Errorf("The provided SSE-C key MD5 does not match the computed MD5 of the SSE-C key")
|
||||
// ErrIncompatibleEncryptionMethod indicates that both SSE-C headers and SSE-S3 headers were specified, and are incompatible
|
||||
// The client needs to remove the SSE-S3 header or the SSE-C headers
|
||||
ErrIncompatibleEncryptionMethod = Errorf("Server side encryption specified with both SSE-C and SSE-S3 headers")
|
||||
|
||||
// ErrKMSAuthLogin is raised when there is a failure authenticating to KMS
|
||||
ErrKMSAuthLogin = Errorf("Vault service did not return auth info")
|
||||
)
|
1964
neofs/api/errors.go
Normal file
1964
neofs/api/errors.go
Normal file
File diff suppressed because it is too large
Load diff
501
neofs/api/handler/unimplemented.go
Normal file
501
neofs/api/handler/unimplemented.go
Normal file
|
@ -0,0 +1,501 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/minio/neofs/api"
|
||||
)
|
||||
|
||||
type handler struct{}
|
||||
|
||||
var _ api.Handler = (*handler)(nil)
|
||||
|
||||
func New() (api.Handler, error) { return new(handler), nil }
|
||||
|
||||
func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketAccelerateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketReplicationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListObjectsV2MHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListBucketObjectVersionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||
Code: "XNeoFSUnimplemented",
|
||||
Description: "implement me",
|
||||
HTTPStatusCode: http.StatusNotImplemented,
|
||||
}, r.URL)
|
||||
}
|
56
neofs/api/max-clients.go
Normal file
56
neofs/api/max-clients.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
MaxClients interface {
|
||||
Handle(http.HandlerFunc) http.HandlerFunc
|
||||
}
|
||||
|
||||
maxClients struct {
|
||||
pool chan struct{}
|
||||
timeout time.Duration
|
||||
}
|
||||
)
|
||||
|
||||
const defaultRequestDeadline = time.Second * 30
|
||||
|
||||
func NewMaxClientsMiddleware(count int, timeout time.Duration) MaxClients {
|
||||
if timeout <= 0 {
|
||||
timeout = defaultRequestDeadline
|
||||
}
|
||||
|
||||
return &maxClients{
|
||||
pool: make(chan struct{}, count),
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *maxClients) Handle(f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if m.pool == nil {
|
||||
f.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
deadline := time.NewTimer(m.timeout)
|
||||
defer deadline.Stop()
|
||||
|
||||
select {
|
||||
case m.pool <- struct{}{}:
|
||||
defer func() { <-m.pool }()
|
||||
f.ServeHTTP(w, r)
|
||||
case <-deadline.C:
|
||||
// Send a http timeout message
|
||||
WriteErrorResponse(r.Context(), w,
|
||||
errorCodes.ToAPIErr(ErrOperationMaxedOut),
|
||||
r.URL)
|
||||
return
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
474
neofs/api/object-errors.go
Normal file
474
neofs/api/object-errors.go
Normal file
|
@ -0,0 +1,474 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Converts underlying storage error. Convenience function written to
|
||||
// handle all cases where we have known types of errors returned by
|
||||
// underlying storage layer.
|
||||
func toObjectErr(err error, params ...string) error {
|
||||
switch err {
|
||||
case errVolumeNotFound:
|
||||
if len(params) >= 1 {
|
||||
err = BucketNotFound{Bucket: params[0]}
|
||||
}
|
||||
case errVolumeNotEmpty:
|
||||
if len(params) >= 1 {
|
||||
err = BucketNotEmpty{Bucket: params[0]}
|
||||
}
|
||||
case errVolumeExists:
|
||||
if len(params) >= 1 {
|
||||
err = BucketExists{Bucket: params[0]}
|
||||
}
|
||||
case errDiskFull:
|
||||
err = StorageFull{}
|
||||
case errTooManyOpenFiles:
|
||||
err = SlowDown{}
|
||||
case errFileAccessDenied:
|
||||
if len(params) >= 2 {
|
||||
err = PrefixAccessDenied{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case errFileParentIsFile:
|
||||
if len(params) >= 2 {
|
||||
err = ParentIsObject{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case errIsNotRegular:
|
||||
if len(params) >= 2 {
|
||||
err = ObjectExistsAsDirectory{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case errFileNotFound:
|
||||
switch len(params) {
|
||||
case 2:
|
||||
err = ObjectNotFound{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
case 3:
|
||||
err = InvalidUploadID{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
UploadID: params[2],
|
||||
}
|
||||
}
|
||||
case errFileNameTooLong:
|
||||
if len(params) >= 2 {
|
||||
err = ObjectNameInvalid{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case errDataTooLarge:
|
||||
if len(params) >= 2 {
|
||||
err = ObjectTooLarge{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case errDataTooSmall:
|
||||
if len(params) >= 2 {
|
||||
err = ObjectTooSmall{
|
||||
Bucket: params[0],
|
||||
Object: params[1],
|
||||
}
|
||||
}
|
||||
case errXLReadQuorum:
|
||||
err = InsufficientReadQuorum{}
|
||||
case errXLWriteQuorum:
|
||||
err = InsufficientWriteQuorum{}
|
||||
case io.ErrUnexpectedEOF, io.ErrShortWrite:
|
||||
err = IncompleteBody{}
|
||||
case context.Canceled, context.DeadlineExceeded:
|
||||
err = IncompleteBody{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SignatureDoesNotMatch - when content md5 does not match with what was sent from client.
|
||||
type SignatureDoesNotMatch struct{}
|
||||
|
||||
func (e SignatureDoesNotMatch) Error() string {
|
||||
return "The request signature we calculated does not match the signature you provided. Check your key and signing method."
|
||||
}
|
||||
|
||||
// StorageFull storage ran out of space.
|
||||
type StorageFull struct{}
|
||||
|
||||
func (e StorageFull) Error() string {
|
||||
return "Storage reached its minimum free disk threshold."
|
||||
}
|
||||
|
||||
// SlowDown too many file descriptors open or backend busy .
|
||||
type SlowDown struct{}
|
||||
|
||||
func (e SlowDown) Error() string {
|
||||
return "Please reduce your request rate"
|
||||
}
|
||||
|
||||
// InsufficientReadQuorum storage cannot satisfy quorum for read operation.
|
||||
type InsufficientReadQuorum struct{}
|
||||
|
||||
func (e InsufficientReadQuorum) Error() string {
|
||||
return "Storage resources are insufficient for the read operation."
|
||||
}
|
||||
|
||||
// InsufficientWriteQuorum storage cannot satisfy quorum for write operation.
|
||||
type InsufficientWriteQuorum struct{}
|
||||
|
||||
func (e InsufficientWriteQuorum) Error() string {
|
||||
return "Storage resources are insufficient for the write operation."
|
||||
}
|
||||
|
||||
// GenericError - generic object layer error.
|
||||
type GenericError struct {
|
||||
Bucket string
|
||||
Object string
|
||||
}
|
||||
|
||||
// BucketNotFound bucket does not exist.
|
||||
type BucketNotFound GenericError
|
||||
|
||||
func (e BucketNotFound) Error() string {
|
||||
return "Bucket not found: " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketAlreadyExists the requested bucket name is not available.
|
||||
type BucketAlreadyExists GenericError
|
||||
|
||||
func (e BucketAlreadyExists) Error() string {
|
||||
return "The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again."
|
||||
}
|
||||
|
||||
// BucketAlreadyOwnedByYou already owned by you.
|
||||
type BucketAlreadyOwnedByYou GenericError
|
||||
|
||||
func (e BucketAlreadyOwnedByYou) Error() string {
|
||||
return "Bucket already owned by you: " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketNotEmpty bucket is not empty.
|
||||
type BucketNotEmpty GenericError
|
||||
|
||||
func (e BucketNotEmpty) Error() string {
|
||||
return "Bucket not empty: " + e.Bucket
|
||||
}
|
||||
|
||||
// ObjectNotFound object does not exist.
|
||||
type ObjectNotFound GenericError
|
||||
|
||||
func (e ObjectNotFound) Error() string {
|
||||
return "Object not found: " + e.Bucket + "#" + e.Object
|
||||
}
|
||||
|
||||
// ObjectAlreadyExists object already exists.
|
||||
type ObjectAlreadyExists GenericError
|
||||
|
||||
func (e ObjectAlreadyExists) Error() string {
|
||||
return "Object: " + e.Bucket + "#" + e.Object + " already exists"
|
||||
}
|
||||
|
||||
// ObjectExistsAsDirectory object already exists as a directory.
|
||||
type ObjectExistsAsDirectory GenericError
|
||||
|
||||
func (e ObjectExistsAsDirectory) Error() string {
|
||||
return "Object exists on : " + e.Bucket + " as directory " + e.Object
|
||||
}
|
||||
|
||||
// PrefixAccessDenied object access is denied.
|
||||
type PrefixAccessDenied GenericError
|
||||
|
||||
func (e PrefixAccessDenied) Error() string {
|
||||
return "Prefix access is denied: " + e.Bucket + SlashSeparator + e.Object
|
||||
}
|
||||
|
||||
// ParentIsObject object access is denied.
|
||||
type ParentIsObject GenericError
|
||||
|
||||
func (e ParentIsObject) Error() string {
|
||||
return "Parent is object " + e.Bucket + SlashSeparator + path.Dir(e.Object)
|
||||
}
|
||||
|
||||
// BucketExists bucket exists.
|
||||
type BucketExists GenericError
|
||||
|
||||
func (e BucketExists) Error() string {
|
||||
return "Bucket exists: " + e.Bucket
|
||||
}
|
||||
|
||||
// UnsupportedDelimiter - unsupported delimiter.
|
||||
type UnsupportedDelimiter struct {
|
||||
Delimiter string
|
||||
}
|
||||
|
||||
func (e UnsupportedDelimiter) Error() string {
|
||||
return fmt.Sprintf("delimiter '%s' is not supported. Only '/' is supported", e.Delimiter)
|
||||
}
|
||||
|
||||
// InvalidUploadIDKeyCombination - invalid upload id and key marker combination.
|
||||
type InvalidUploadIDKeyCombination struct {
|
||||
UploadIDMarker, KeyMarker string
|
||||
}
|
||||
|
||||
func (e InvalidUploadIDKeyCombination) Error() string {
|
||||
return fmt.Sprintf("Invalid combination of uploadID marker '%s' and marker '%s'", e.UploadIDMarker, e.KeyMarker)
|
||||
}
|
||||
|
||||
// InvalidMarkerPrefixCombination - invalid marker and prefix combination.
|
||||
type InvalidMarkerPrefixCombination struct {
|
||||
Marker, Prefix string
|
||||
}
|
||||
|
||||
func (e InvalidMarkerPrefixCombination) Error() string {
|
||||
return fmt.Sprintf("Invalid combination of marker '%s' and prefix '%s'", e.Marker, e.Prefix)
|
||||
}
|
||||
|
||||
// BucketPolicyNotFound - no bucket policy found.
|
||||
type BucketPolicyNotFound GenericError
|
||||
|
||||
func (e BucketPolicyNotFound) Error() string {
|
||||
return "No bucket policy configuration found for bucket: " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketLifecycleNotFound - no bucket lifecycle found.
|
||||
type BucketLifecycleNotFound GenericError
|
||||
|
||||
func (e BucketLifecycleNotFound) Error() string {
|
||||
return "No bucket lifecycle configuration found for bucket : " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketSSEConfigNotFound - no bucket encryption found
|
||||
type BucketSSEConfigNotFound GenericError
|
||||
|
||||
func (e BucketSSEConfigNotFound) Error() string {
|
||||
return "No bucket encryption configuration found for bucket: " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketTaggingNotFound - no bucket tags found
|
||||
type BucketTaggingNotFound GenericError
|
||||
|
||||
func (e BucketTaggingNotFound) Error() string {
|
||||
return "No bucket tags found for bucket: " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketObjectLockConfigNotFound - no bucket object lock config found
|
||||
type BucketObjectLockConfigNotFound GenericError
|
||||
|
||||
func (e BucketObjectLockConfigNotFound) Error() string {
|
||||
return "No bucket object lock configuration found for bucket: " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketQuotaConfigNotFound - no bucket quota config found.
|
||||
type BucketQuotaConfigNotFound GenericError
|
||||
|
||||
func (e BucketQuotaConfigNotFound) Error() string {
|
||||
return "No quota config found for bucket : " + e.Bucket
|
||||
}
|
||||
|
||||
// BucketQuotaExceeded - bucket quota exceeded.
|
||||
type BucketQuotaExceeded GenericError
|
||||
|
||||
func (e BucketQuotaExceeded) Error() string {
|
||||
return "Bucket quota exceeded for bucket: " + e.Bucket
|
||||
}
|
||||
|
||||
// Bucket related errors.
|
||||
|
||||
// BucketNameInvalid - bucketname provided is invalid.
|
||||
type BucketNameInvalid GenericError
|
||||
|
||||
// Error returns string an error formatted as the given text.
|
||||
func (e BucketNameInvalid) Error() string {
|
||||
return "Bucket name invalid: " + e.Bucket
|
||||
}
|
||||
|
||||
// Object related errors.
|
||||
|
||||
// ObjectNameInvalid - object name provided is invalid.
|
||||
type ObjectNameInvalid GenericError
|
||||
|
||||
// ObjectNameTooLong - object name too long.
|
||||
type ObjectNameTooLong GenericError
|
||||
|
||||
// ObjectNamePrefixAsSlash - object name has a slash as prefix.
|
||||
type ObjectNamePrefixAsSlash GenericError
|
||||
|
||||
// Error returns string an error formatted as the given text.
|
||||
func (e ObjectNameInvalid) Error() string {
|
||||
return "Object name invalid: " + e.Bucket + "#" + e.Object
|
||||
}
|
||||
|
||||
// Error returns string an error formatted as the given text.
|
||||
func (e ObjectNameTooLong) Error() string {
|
||||
return "Object name too long: " + e.Bucket + "#" + e.Object
|
||||
}
|
||||
|
||||
// Error returns string an error formatted as the given text.
|
||||
func (e ObjectNamePrefixAsSlash) Error() string {
|
||||
return "Object name contains forward slash as pefix: " + e.Bucket + "#" + e.Object
|
||||
}
|
||||
|
||||
// AllAccessDisabled All access to this object has been disabled
|
||||
type AllAccessDisabled GenericError
|
||||
|
||||
// Error returns string an error formatted as the given text.
|
||||
func (e AllAccessDisabled) Error() string {
|
||||
return "All access to this object has been disabled"
|
||||
}
|
||||
|
||||
// IncompleteBody You did not provide the number of bytes specified by the Content-Length HTTP header.
|
||||
type IncompleteBody GenericError
|
||||
|
||||
// Error returns string an error formatted as the given text.
|
||||
func (e IncompleteBody) Error() string {
|
||||
return e.Bucket + "#" + e.Object + "has incomplete body"
|
||||
}
|
||||
|
||||
// InvalidRange - invalid range typed error.
|
||||
type InvalidRange struct {
|
||||
OffsetBegin int64
|
||||
OffsetEnd int64
|
||||
ResourceSize int64
|
||||
}
|
||||
|
||||
func (e InvalidRange) Error() string {
|
||||
return fmt.Sprintf("The requested range \"bytes %d-%d/%d\" is not satisfiable.", e.OffsetBegin, e.OffsetEnd, e.ResourceSize)
|
||||
}
|
||||
|
||||
// ObjectTooLarge error returned when the size of the object > max object size allowed (5G) per request.
|
||||
type ObjectTooLarge GenericError
|
||||
|
||||
func (e ObjectTooLarge) Error() string {
|
||||
return "size of the object greater than what is allowed(5G)"
|
||||
}
|
||||
|
||||
// ObjectTooSmall error returned when the size of the object < what is expected.
|
||||
type ObjectTooSmall GenericError
|
||||
|
||||
func (e ObjectTooSmall) Error() string {
|
||||
return "size of the object less than what is expected"
|
||||
}
|
||||
|
||||
// OperationTimedOut - a timeout occurred.
|
||||
type OperationTimedOut struct {
|
||||
}
|
||||
|
||||
func (e OperationTimedOut) Error() string {
|
||||
return "Operation timed out"
|
||||
}
|
||||
|
||||
// Multipart related errors.
|
||||
|
||||
// MalformedUploadID malformed upload id.
|
||||
type MalformedUploadID struct {
|
||||
UploadID string
|
||||
}
|
||||
|
||||
func (e MalformedUploadID) Error() string {
|
||||
return "Malformed upload id " + e.UploadID
|
||||
}
|
||||
|
||||
// InvalidUploadID invalid upload id.
|
||||
type InvalidUploadID struct {
|
||||
Bucket string
|
||||
Object string
|
||||
UploadID string
|
||||
}
|
||||
|
||||
func (e InvalidUploadID) Error() string {
|
||||
return "Invalid upload id " + e.UploadID
|
||||
}
|
||||
|
||||
// InvalidPart One or more of the specified parts could not be found
|
||||
type InvalidPart struct {
|
||||
PartNumber int
|
||||
ExpETag string
|
||||
GotETag string
|
||||
}
|
||||
|
||||
func (e InvalidPart) Error() string {
|
||||
return fmt.Sprintf("Specified part could not be found. PartNumber %d, Expected %s, got %s",
|
||||
e.PartNumber, e.ExpETag, e.GotETag)
|
||||
}
|
||||
|
||||
// PartTooSmall - error if part size is less than 5MB.
|
||||
type PartTooSmall struct {
|
||||
PartSize int64
|
||||
PartNumber int
|
||||
PartETag string
|
||||
}
|
||||
|
||||
func (e PartTooSmall) Error() string {
|
||||
return fmt.Sprintf("Part size for %d should be at least 5MB", e.PartNumber)
|
||||
}
|
||||
|
||||
// PartTooBig returned if size of part is bigger than the allowed limit.
|
||||
type PartTooBig struct{}
|
||||
|
||||
func (e PartTooBig) Error() string {
|
||||
return "Part size bigger than the allowed limit"
|
||||
}
|
||||
|
||||
// InvalidETag error returned when the etag has changed on disk
|
||||
type InvalidETag struct{}
|
||||
|
||||
func (e InvalidETag) Error() string {
|
||||
return "etag of the object has changed"
|
||||
}
|
||||
|
||||
// NotImplemented If a feature is not implemented
|
||||
type NotImplemented struct{}
|
||||
|
||||
func (e NotImplemented) Error() string {
|
||||
return "Not Implemented"
|
||||
}
|
||||
|
||||
// UnsupportedMetadata - unsupported metadata
|
||||
type UnsupportedMetadata struct{}
|
||||
|
||||
func (e UnsupportedMetadata) Error() string {
|
||||
return "Unsupported headers in Metadata"
|
||||
}
|
||||
|
||||
// BackendDown is returned for network errors or if the gateway's backend is down.
|
||||
type BackendDown struct{}
|
||||
|
||||
func (e BackendDown) Error() string {
|
||||
return "Backend down"
|
||||
}
|
||||
|
||||
// isErrBucketNotFound - Check if error type is BucketNotFound.
|
||||
func isErrBucketNotFound(err error) bool {
|
||||
var bkNotFound BucketNotFound
|
||||
return errors.As(err, &bkNotFound)
|
||||
}
|
||||
|
||||
// isErrObjectNotFound - Check if error type is ObjectNotFound.
|
||||
func isErrObjectNotFound(err error) bool {
|
||||
var objNotFound ObjectNotFound
|
||||
return errors.As(err, &objNotFound)
|
||||
}
|
||||
|
||||
// PreConditionFailed - Check if copy precondition failed
|
||||
type PreConditionFailed struct{}
|
||||
|
||||
func (e PreConditionFailed) Error() string {
|
||||
return "At least one of the pre-conditions you specified did not hold"
|
||||
}
|
||||
|
||||
func isErrPreconditionFailed(err error) bool {
|
||||
_, ok := err.(PreConditionFailed)
|
||||
return ok
|
||||
}
|
112
neofs/api/reqinfo.go
Normal file
112
neofs/api/reqinfo.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type (
|
||||
// KeyVal - appended to ReqInfo.Tags
|
||||
KeyVal struct {
|
||||
Key string
|
||||
Val string
|
||||
}
|
||||
|
||||
// ReqInfo stores the request info.
|
||||
ReqInfo struct {
|
||||
sync.RWMutex
|
||||
RemoteHost string // Client Host/IP
|
||||
Host string // Node Host/IP
|
||||
UserAgent string // User Agent
|
||||
DeploymentID string // x-minio-deployment-id
|
||||
RequestID string // x-amz-request-id
|
||||
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
||||
BucketName string // Bucket name
|
||||
ObjectName string // Object name
|
||||
tags []KeyVal // Any additional info not accommodated by above fields
|
||||
}
|
||||
)
|
||||
|
||||
// Key used for Get/SetReqInfo
|
||||
type contextKeyType string
|
||||
|
||||
const ctxRequestInfo = contextKeyType("NeoFS-S3-Gate")
|
||||
|
||||
// NewReqInfo :
|
||||
func NewReqInfo(remoteHost, userAgent, deploymentID, requestID, api, bucket, object string) *ReqInfo {
|
||||
req := ReqInfo{}
|
||||
req.RemoteHost = remoteHost
|
||||
req.UserAgent = userAgent
|
||||
req.API = api
|
||||
req.DeploymentID = deploymentID
|
||||
req.RequestID = requestID
|
||||
req.BucketName = bucket
|
||||
req.ObjectName = object
|
||||
return &req
|
||||
}
|
||||
|
||||
// AppendTags - appends key/val to ReqInfo.tags
|
||||
func (r *ReqInfo) AppendTags(key string, val string) *ReqInfo {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
r.tags = append(r.tags, KeyVal{key, val})
|
||||
return r
|
||||
}
|
||||
|
||||
// SetTags - sets key/val to ReqInfo.tags
|
||||
func (r *ReqInfo) SetTags(key string, val string) *ReqInfo {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
// Search of tag key already exists in tags
|
||||
var updated bool
|
||||
for _, tag := range r.tags {
|
||||
if tag.Key == key {
|
||||
tag.Val = val
|
||||
updated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !updated {
|
||||
// Append to the end of tags list
|
||||
r.tags = append(r.tags, KeyVal{key, val})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GetTags - returns the user defined tags
|
||||
func (r *ReqInfo) GetTags() []KeyVal {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
return append([]KeyVal(nil), r.tags...)
|
||||
}
|
||||
|
||||
// SetReqInfo sets ReqInfo in the context.
|
||||
func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context {
|
||||
if ctx == nil {
|
||||
return nil
|
||||
}
|
||||
return context.WithValue(ctx, ctxRequestInfo, req)
|
||||
}
|
||||
|
||||
// GetReqInfo returns ReqInfo if set.
|
||||
func GetReqInfo(ctx context.Context) *ReqInfo {
|
||||
if ctx != nil {
|
||||
r, ok := ctx.Value(ctxRequestInfo).(*ReqInfo)
|
||||
if ok {
|
||||
return r
|
||||
}
|
||||
r = &ReqInfo{}
|
||||
SetReqInfo(ctx, r)
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
130
neofs/api/response.go
Normal file
130
neofs/api/response.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/minio/minio/misc"
|
||||
)
|
||||
|
||||
type (
|
||||
// APIErrorResponse - error response format
|
||||
ErrorResponse struct {
|
||||
XMLName xml.Name `xml:"Error" json:"-"`
|
||||
Code string
|
||||
Message string
|
||||
Key string `xml:"Key,omitempty" json:"Key,omitempty"`
|
||||
BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"`
|
||||
Resource string
|
||||
Region string `xml:"Region,omitempty" json:"Region,omitempty"`
|
||||
RequestID string `xml:"RequestId" json:"RequestId"`
|
||||
HostID string `xml:"HostId" json:"HostId"`
|
||||
}
|
||||
|
||||
// APIError structure
|
||||
Error struct {
|
||||
Code string
|
||||
Description string
|
||||
HTTPStatusCode int
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
hdrServerInfo = "Server"
|
||||
hdrAcceptRanges = "Accept-Ranges"
|
||||
hdrContentType = "Content-Type"
|
||||
hdrContentLength = "Content-Length"
|
||||
hdrRetryAfter = "Retry-After"
|
||||
|
||||
hdrAmzCopySource = "X-Amz-Copy-Source"
|
||||
|
||||
// Response request id.
|
||||
hdrAmzRequestID = "x-amz-request-id"
|
||||
|
||||
// hdrSSE is the general AWS SSE HTTP header key.
|
||||
hdrSSE = "X-Amz-Server-Side-Encryption"
|
||||
|
||||
// hdrSSECustomerKey is the HTTP header key referencing the
|
||||
// SSE-C client-provided key..
|
||||
hdrSSECustomerKey = hdrSSE + "-Customer-Key"
|
||||
|
||||
// hdrSSECopyKey is the HTTP header key referencing the SSE-C
|
||||
// client-provided key for SSE-C copy requests.
|
||||
hdrSSECopyKey = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key"
|
||||
)
|
||||
|
||||
var deploymentID, _ = uuid.NewRandom()
|
||||
|
||||
// WriteErrorResponse writes error headers
|
||||
func WriteErrorResponse(ctx context.Context, w http.ResponseWriter, err Error, reqURL *url.URL) {
|
||||
switch err.Code {
|
||||
case "SlowDown", "XNeoFSServerNotInitialized", "XNeoFSReadQuorum", "XNeoFSWriteQuorum":
|
||||
// Set retry-after header to indicate user-agents to retry request after 120secs.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
||||
w.Header().Set(hdrRetryAfter, "120")
|
||||
case "AccessDenied":
|
||||
// TODO process when the request is from browser and also if browser
|
||||
}
|
||||
|
||||
// Generate error response.
|
||||
errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path,
|
||||
w.Header().Get(hdrAmzRequestID), deploymentID.String())
|
||||
encodedErrorResponse := encodeResponse(errorResponse)
|
||||
writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML)
|
||||
}
|
||||
|
||||
// If none of the http routes match respond with appropriate errors
|
||||
func errorResponseHandler(w http.ResponseWriter, r *http.Request) {
|
||||
desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path)
|
||||
WriteErrorResponse(r.Context(), w, Error{
|
||||
Code: "XMinioUnknownAPIRequest",
|
||||
Description: desc,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}, r.URL)
|
||||
}
|
||||
|
||||
// Write http common headers
|
||||
func setCommonHeaders(w http.ResponseWriter) {
|
||||
w.Header().Set(hdrServerInfo, "NeoFS-S3-Gate/"+misc.Version)
|
||||
w.Header().Set(hdrAcceptRanges, "bytes")
|
||||
|
||||
// Remove sensitive information
|
||||
removeSensitiveHeaders(w.Header())
|
||||
}
|
||||
|
||||
// removeSensitiveHeaders removes confidential encryption
|
||||
// information - e.g. the SSE-C key - from the HTTP headers.
|
||||
// It has the same semantics as RemoveSensitiveEntires.
|
||||
func removeSensitiveHeaders(h http.Header) {
|
||||
h.Del(hdrSSECustomerKey)
|
||||
h.Del(hdrSSECopyKey)
|
||||
}
|
||||
|
||||
func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) {
|
||||
setCommonHeaders(w)
|
||||
if mType != mimeNone {
|
||||
w.Header().Set(hdrContentType, string(mType))
|
||||
}
|
||||
w.Header().Set(hdrContentLength, strconv.Itoa(len(response)))
|
||||
w.WriteHeader(statusCode)
|
||||
if response != nil {
|
||||
_, _ = w.Write(response)
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Encodes the response headers into XML format.
|
||||
func encodeResponse(response interface{}) []byte {
|
||||
var bytesBuffer bytes.Buffer
|
||||
bytesBuffer.WriteString(xml.Header)
|
||||
_ = xml.
|
||||
NewEncoder(&bytesBuffer).
|
||||
Encode(response)
|
||||
return bytesBuffer.Bytes()
|
||||
}
|
301
neofs/api/router.go
Normal file
301
neofs/api/router.go
Normal file
|
@ -0,0 +1,301 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/neofs/metrics"
|
||||
)
|
||||
|
||||
type (
|
||||
Handler interface {
|
||||
HeadObjectHandler(http.ResponseWriter, *http.Request)
|
||||
CopyObjectPartHandler(http.ResponseWriter, *http.Request)
|
||||
PutObjectPartHandler(http.ResponseWriter, *http.Request)
|
||||
ListObjectPartsHandler(http.ResponseWriter, *http.Request)
|
||||
CompleteMultipartUploadHandler(http.ResponseWriter, *http.Request)
|
||||
NewMultipartUploadHandler(http.ResponseWriter, *http.Request)
|
||||
AbortMultipartUploadHandler(http.ResponseWriter, *http.Request)
|
||||
GetObjectACLHandler(http.ResponseWriter, *http.Request)
|
||||
PutObjectACLHandler(http.ResponseWriter, *http.Request)
|
||||
GetObjectTaggingHandler(http.ResponseWriter, *http.Request)
|
||||
PutObjectTaggingHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteObjectTaggingHandler(http.ResponseWriter, *http.Request)
|
||||
SelectObjectContentHandler(http.ResponseWriter, *http.Request)
|
||||
GetObjectRetentionHandler(http.ResponseWriter, *http.Request)
|
||||
GetObjectLegalHoldHandler(http.ResponseWriter, *http.Request)
|
||||
GetObjectHandler(http.ResponseWriter, *http.Request)
|
||||
CopyObjectHandler(http.ResponseWriter, *http.Request)
|
||||
PutObjectRetentionHandler(http.ResponseWriter, *http.Request)
|
||||
PutObjectLegalHoldHandler(http.ResponseWriter, *http.Request)
|
||||
PutObjectHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteObjectHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketLocationHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketPolicyHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketLifecycleHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketEncryptionHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketACLHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketACLHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketCorsHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketWebsiteHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketAccelerateHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketRequestPaymentHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketLoggingHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketReplicationHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketTaggingHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteBucketWebsiteHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteBucketTaggingHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketObjectLockConfigHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketVersioningHandler(http.ResponseWriter, *http.Request)
|
||||
GetBucketNotificationHandler(http.ResponseWriter, *http.Request)
|
||||
ListenBucketNotificationHandler(http.ResponseWriter, *http.Request)
|
||||
ListMultipartUploadsHandler(http.ResponseWriter, *http.Request)
|
||||
ListObjectsV2MHandler(http.ResponseWriter, *http.Request)
|
||||
ListObjectsV2Handler(http.ResponseWriter, *http.Request)
|
||||
ListBucketObjectVersionsHandler(http.ResponseWriter, *http.Request)
|
||||
ListObjectsV1Handler(http.ResponseWriter, *http.Request)
|
||||
PutBucketLifecycleHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketEncryptionHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketPolicyHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketObjectLockConfigHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketTaggingHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketVersioningHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketNotificationHandler(http.ResponseWriter, *http.Request)
|
||||
PutBucketHandler(http.ResponseWriter, *http.Request)
|
||||
HeadBucketHandler(http.ResponseWriter, *http.Request)
|
||||
PostPolicyBucketHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteMultipleObjectsHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteBucketPolicyHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteBucketLifecycleHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteBucketEncryptionHandler(http.ResponseWriter, *http.Request)
|
||||
DeleteBucketHandler(http.ResponseWriter, *http.Request)
|
||||
ListBucketsHandler(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
// mimeType represents various MIME type used API responses.
|
||||
mimeType string
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// SlashSeparator - slash separator.
|
||||
SlashSeparator = "/"
|
||||
|
||||
// Means no response type.
|
||||
mimeNone mimeType = ""
|
||||
// Means response type is JSON.
|
||||
// mimeJSON mimeType = "application/json"
|
||||
// Means response type is XML.
|
||||
mimeXML mimeType = "application/xml"
|
||||
)
|
||||
|
||||
func Attach(r *mux.Router, m MaxClients, h Handler) {
|
||||
api := r.PathPrefix(SlashSeparator).Subrouter()
|
||||
|
||||
bucket := api.PathPrefix("/{bucket}").Subrouter()
|
||||
|
||||
// Object operations
|
||||
// HeadObject
|
||||
bucket.Methods(http.MethodHead).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("headobject", h.HeadObjectHandler)))
|
||||
// CopyObjectPart
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(hdrAmzCopySource, ".*?(\\/|%2F).*?").HandlerFunc(m.Handle(metrics.APIStats("copyobjectpart", h.CopyObjectPartHandler))).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
||||
// PutObjectPart
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putobjectpart", h.PutObjectPartHandler))).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}")
|
||||
// ListObjectParts
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listobjectparts", h.ListObjectPartsHandler))).Queries("uploadId", "{uploadId:.*}")
|
||||
// CompleteMultipartUpload
|
||||
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("completemutipartupload", h.CompleteMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}")
|
||||
// NewMultipartUpload
|
||||
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("newmultipartupload", h.NewMultipartUploadHandler))).Queries("uploads", "")
|
||||
// AbortMultipartUpload
|
||||
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("abortmultipartupload", h.AbortMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}")
|
||||
// GetObjectACL - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getobjectacl", h.GetObjectACLHandler))).Queries("acl", "")
|
||||
// PutObjectACL - this is a dummy call.
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putobjectacl", h.PutObjectACLHandler))).Queries("acl", "")
|
||||
// GetObjectTagging
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getobjecttagging", h.GetObjectTaggingHandler))).Queries("tagging", "")
|
||||
// PutObjectTagging
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putobjecttagging", h.PutObjectTaggingHandler))).Queries("tagging", "")
|
||||
// DeleteObjectTagging
|
||||
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deleteobjecttagging", h.DeleteObjectTaggingHandler))).Queries("tagging", "")
|
||||
// SelectObjectContent
|
||||
bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("selectobjectcontent", h.SelectObjectContentHandler))).Queries("select", "").Queries("select-type", "2")
|
||||
// GetObjectRetention
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getobjectretention", h.GetObjectRetentionHandler))).Queries("retention", "")
|
||||
// GetObjectLegalHold
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getobjectlegalhold", h.GetObjectLegalHoldHandler))).Queries("legal-hold", "")
|
||||
// GetObject
|
||||
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getobject", h.GetObjectHandler)))
|
||||
// CopyObject
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(hdrAmzCopySource, ".*?(\\/|%2F).*?").HandlerFunc(m.Handle(metrics.APIStats("copyobject", h.CopyObjectHandler)))
|
||||
// PutObjectRetention
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putobjectretention", h.PutObjectRetentionHandler))).Queries("retention", "")
|
||||
// PutObjectLegalHold
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putobjectlegalhold", h.PutObjectLegalHoldHandler))).Queries("legal-hold", "")
|
||||
|
||||
// PutObject
|
||||
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putobject", h.PutObjectHandler)))
|
||||
// DeleteObject
|
||||
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deleteobject", h.DeleteObjectHandler)))
|
||||
|
||||
// Bucket operations
|
||||
// GetBucketLocation
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketlocation", h.GetBucketLocationHandler))).Queries("location", "")
|
||||
// GetBucketPolicy
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketpolicy", h.GetBucketPolicyHandler))).Queries("policy", "")
|
||||
// GetBucketLifecycle
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketlifecycle", h.GetBucketLifecycleHandler))).Queries("lifecycle", "")
|
||||
// GetBucketEncryption
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketencryption", h.GetBucketEncryptionHandler))).Queries("encryption", "")
|
||||
|
||||
// Dummy Bucket Calls
|
||||
// GetBucketACL -- this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketacl", h.GetBucketACLHandler))).Queries("acl", "")
|
||||
// PutBucketACL -- this is a dummy call.
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucketacl", h.PutBucketACLHandler))).Queries("acl", "")
|
||||
// GetBucketCors - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketcors", h.GetBucketCorsHandler))).Queries("cors", "")
|
||||
// GetBucketWebsiteHandler - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketwebsite", h.GetBucketWebsiteHandler))).Queries("website", "")
|
||||
// GetBucketAccelerateHandler - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketaccelerate", h.GetBucketAccelerateHandler))).Queries("accelerate", "")
|
||||
// GetBucketRequestPaymentHandler - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketrequestpayment", h.GetBucketRequestPaymentHandler))).Queries("requestPayment", "")
|
||||
// GetBucketLoggingHandler - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketlogging", h.GetBucketLoggingHandler))).Queries("logging", "")
|
||||
// GetBucketLifecycleHandler - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketlifecycle", h.GetBucketLifecycleHandler))).Queries("lifecycle", "")
|
||||
// GetBucketReplicationHandler - this is a dummy call.
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketreplication", h.GetBucketReplicationHandler))).Queries("replication", "")
|
||||
// GetBucketTaggingHandler
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbuckettagging", h.GetBucketTaggingHandler))).Queries("tagging", "")
|
||||
// DeleteBucketWebsiteHandler
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deletebucketwebsite", h.DeleteBucketWebsiteHandler))).Queries("website", "")
|
||||
// DeleteBucketTaggingHandler
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deletebuckettagging", h.DeleteBucketTaggingHandler))).Queries("tagging", "")
|
||||
|
||||
// GetBucketObjectLockConfig
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketobjectlockconfiguration", h.GetBucketObjectLockConfigHandler))).Queries("object-lock", "")
|
||||
// GetBucketVersioning
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketversioning", h.GetBucketVersioningHandler))).Queries("versioning", "")
|
||||
// GetBucketNotification
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("getbucketnotification", h.GetBucketNotificationHandler))).Queries("notification", "")
|
||||
// ListenBucketNotification
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(metrics.APIStats("listenbucketnotification", h.ListenBucketNotificationHandler)).Queries("events", "{events:.*}")
|
||||
// ListMultipartUploads
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listmultipartuploads", h.ListMultipartUploadsHandler))).Queries("uploads", "")
|
||||
// ListObjectsV2M
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listobjectsv2M", h.ListObjectsV2MHandler))).Queries("list-type", "2", "metadata", "true")
|
||||
// ListObjectsV2
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listobjectsv2", h.ListObjectsV2Handler))).Queries("list-type", "2")
|
||||
// ListBucketVersions
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listbucketversions", h.ListBucketObjectVersionsHandler))).Queries("versions", "")
|
||||
// ListObjectsV1 (Legacy)
|
||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listobjectsv1", h.ListObjectsV1Handler)))
|
||||
// PutBucketLifecycle
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucketlifecycle", h.PutBucketLifecycleHandler))).Queries("lifecycle", "")
|
||||
// PutBucketEncryption
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucketencryption", h.PutBucketEncryptionHandler))).Queries("encryption", "")
|
||||
|
||||
// PutBucketPolicy
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucketpolicy", h.PutBucketPolicyHandler))).Queries("policy", "")
|
||||
|
||||
// PutBucketObjectLockConfig
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucketobjectlockconfig", h.PutBucketObjectLockConfigHandler))).Queries("object-lock", "")
|
||||
// PutBucketTaggingHandler
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbuckettagging", h.PutBucketTaggingHandler))).Queries("tagging", "")
|
||||
// PutBucketVersioning
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucketversioning", h.PutBucketVersioningHandler))).Queries("versioning", "")
|
||||
// PutBucketNotification
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucketnotification", h.PutBucketNotificationHandler))).Queries("notification", "")
|
||||
// PutBucket
|
||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("putbucket", h.PutBucketHandler)))
|
||||
// HeadBucket
|
||||
bucket.Methods(http.MethodHead).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("headbucket", h.HeadBucketHandler)))
|
||||
// PostPolicy
|
||||
bucket.Methods(http.MethodPost).HeadersRegexp(hdrContentType, "multipart/form-data*").HandlerFunc(
|
||||
m.Handle(metrics.APIStats("postpolicybucket", h.PostPolicyBucketHandler)))
|
||||
// DeleteMultipleObjects
|
||||
bucket.Methods(http.MethodPost).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deletemultipleobjects", h.DeleteMultipleObjectsHandler))).Queries("delete", "")
|
||||
// DeleteBucketPolicy
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deletebucketpolicy", h.DeleteBucketPolicyHandler))).Queries("policy", "")
|
||||
// DeleteBucketLifecycle
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deletebucketlifecycle", h.DeleteBucketLifecycleHandler))).Queries("lifecycle", "")
|
||||
// DeleteBucketEncryption
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deletebucketencryption", h.DeleteBucketEncryptionHandler))).Queries("encryption", "")
|
||||
// DeleteBucket
|
||||
bucket.Methods(http.MethodDelete).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("deletebucket", h.DeleteBucketHandler)))
|
||||
|
||||
// Root operation
|
||||
|
||||
// ListBuckets
|
||||
api.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listbuckets", h.ListBucketsHandler)))
|
||||
|
||||
// S3 browser with signature v4 adds '//' for ListBuckets request, so rather
|
||||
// than failing with UnknownAPIRequest we simply handle it for now.
|
||||
api.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).HandlerFunc(
|
||||
m.Handle(metrics.APIStats("listbuckets", h.ListBucketsHandler)))
|
||||
|
||||
// If none of the routes match add default error handler routes
|
||||
api.NotFoundHandler = metrics.APIStats("notfound", errorResponseHandler)
|
||||
api.MethodNotAllowedHandler = metrics.APIStats("methodnotallowed", errorResponseHandler)
|
||||
}
|
93
neofs/api/storage-errors.go
Normal file
93
neofs/api/storage-errors.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package api
|
||||
|
||||
// errUnexpected - unexpected error, requires manual intervention.
|
||||
var errUnexpected = StorageErr("Unexpected error, please report this issue at https://github.com/minio/minio/issues")
|
||||
|
||||
// errCorruptedFormat - corrupted backend format.
|
||||
var errCorruptedFormat = StorageErr("corrupted backend format, please join https://slack.min.io for assistance")
|
||||
|
||||
// errUnformattedDisk - unformatted disk found.
|
||||
var errUnformattedDisk = StorageErr("unformatted disk found")
|
||||
|
||||
// errUnsupporteDisk - when disk does not support O_DIRECT flag.
|
||||
var errUnsupportedDisk = StorageErr("disk does not support O_DIRECT")
|
||||
|
||||
// errDiskFull - cannot create volume or files when disk is full.
|
||||
var errDiskFull = StorageErr("disk path full")
|
||||
|
||||
// errDiskNotFound - cannot find the underlying configured disk anymore.
|
||||
var errDiskNotFound = StorageErr("disk not found")
|
||||
|
||||
// errFaultyRemoteDisk - remote disk is faulty.
|
||||
var errFaultyRemoteDisk = StorageErr("remote disk is faulty")
|
||||
|
||||
// errFaultyDisk - disk is faulty.
|
||||
var errFaultyDisk = StorageErr("disk is faulty")
|
||||
|
||||
// errDiskAccessDenied - we don't have write permissions on disk.
|
||||
var errDiskAccessDenied = StorageErr("disk access denied")
|
||||
|
||||
// errFileNotFound - cannot find the file.
|
||||
var errFileNotFound = StorageErr("file not found")
|
||||
|
||||
// errTooManyOpenFiles - too many open files.
|
||||
var errTooManyOpenFiles = StorageErr("too many open files")
|
||||
|
||||
// errFileNameTooLong - given file name is too long than supported length.
|
||||
var errFileNameTooLong = StorageErr("file name too long")
|
||||
|
||||
// errVolumeExists - cannot create same volume again.
|
||||
var errVolumeExists = StorageErr("volume already exists")
|
||||
|
||||
// errIsNotRegular - not of regular file type.
|
||||
var errIsNotRegular = StorageErr("not of regular file type")
|
||||
|
||||
// errVolumeNotFound - cannot find the volume.
|
||||
var errVolumeNotFound = StorageErr("volume not found")
|
||||
|
||||
// errVolumeNotEmpty - volume not empty.
|
||||
var errVolumeNotEmpty = StorageErr("volume is not empty")
|
||||
|
||||
// errVolumeAccessDenied - cannot access volume, insufficient permissions.
|
||||
var errVolumeAccessDenied = StorageErr("volume access denied")
|
||||
|
||||
// errFileAccessDenied - cannot access file, insufficient permissions.
|
||||
var errFileAccessDenied = StorageErr("file access denied")
|
||||
|
||||
// errFileCorrupt - file has an unexpected size, or is not readable
|
||||
var errFileCorrupt = StorageErr("file is corrupted")
|
||||
|
||||
// errFileParentIsFile - cannot have overlapping objects, parent is already a file.
|
||||
var errFileParentIsFile = StorageErr("parent is a file")
|
||||
|
||||
// errBitrotHashAlgoInvalid - the algo for bit-rot hash
|
||||
// verification is empty or invalid.
|
||||
var errBitrotHashAlgoInvalid = StorageErr("bit-rot hash algorithm is invalid")
|
||||
|
||||
// errCrossDeviceLink - rename across devices not allowed.
|
||||
var errCrossDeviceLink = StorageErr("Rename across devices not allowed, please fix your backend configuration")
|
||||
|
||||
// errMinDiskSize - cannot create volume or files when disk size is less than threshold.
|
||||
var errMinDiskSize = StorageErr("The disk size is less than 900MiB threshold")
|
||||
|
||||
// errLessData - returned when less data available than what was requested.
|
||||
var errLessData = StorageErr("less data available than what was requested")
|
||||
|
||||
// errMoreData = returned when more data was sent by the caller than what it was supposed to.
|
||||
var errMoreData = StorageErr("more data was sent than what was advertised")
|
||||
|
||||
// StorageErr represents error generated by posix call.
|
||||
type StorageErr string
|
||||
|
||||
func (h StorageErr) Error() string {
|
||||
return string(h)
|
||||
}
|
||||
|
||||
// Collection of basic errors.
|
||||
var baseErrs = []error{
|
||||
errDiskNotFound,
|
||||
errFaultyDisk,
|
||||
errFaultyRemoteDisk,
|
||||
}
|
||||
|
||||
var baseIgnoredErrs = baseErrs
|
107
neofs/api/typed-errors.go
Normal file
107
neofs/api/typed-errors.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// errInvalidArgument means that input argument is invalid.
|
||||
var errInvalidArgument = errors.New("Invalid arguments specified")
|
||||
|
||||
// errMethodNotAllowed means that method is not allowed.
|
||||
var errMethodNotAllowed = errors.New("Method not allowed")
|
||||
|
||||
// errSignatureMismatch means signature did not match.
|
||||
var errSignatureMismatch = errors.New("Signature does not match")
|
||||
|
||||
// used when we deal with data larger than expected
|
||||
var errSizeUnexpected = errors.New("Data size larger than expected")
|
||||
|
||||
// used when we deal with data with unknown size
|
||||
var errSizeUnspecified = errors.New("Data size is unspecified")
|
||||
|
||||
// When upload object size is greater than 5G in a single PUT/POST operation.
|
||||
var errDataTooLarge = errors.New("Object size larger than allowed limit")
|
||||
|
||||
// When upload object size is less than what was expected.
|
||||
var errDataTooSmall = errors.New("Object size smaller than expected")
|
||||
|
||||
// errServerNotInitialized - server not initialized.
|
||||
var errServerNotInitialized = errors.New("Server not initialized, please try again")
|
||||
|
||||
// errRPCAPIVersionUnsupported - unsupported rpc API version.
|
||||
var errRPCAPIVersionUnsupported = errors.New("Unsupported rpc API version")
|
||||
|
||||
// errServerTimeMismatch - server times are too far apart.
|
||||
var errServerTimeMismatch = errors.New("Server times are too far apart")
|
||||
|
||||
// errInvalidBucketName - bucket name is reserved for MinIO, usually
|
||||
// returned for 'minio', '.minio.sys', buckets with capital letters.
|
||||
var errInvalidBucketName = errors.New("The specified bucket is not valid")
|
||||
|
||||
// errInvalidRange - returned when given range value is not valid.
|
||||
var errInvalidRange = errors.New("Invalid range")
|
||||
|
||||
// errInvalidRangeSource - returned when given range value exceeds
|
||||
// the source object size.
|
||||
var errInvalidRangeSource = errors.New("Range specified exceeds source object size")
|
||||
|
||||
// error returned by disks which are to be initialized are waiting for the
|
||||
// first server to initialize them in distributed set to initialize them.
|
||||
var errNotFirstDisk = errors.New("Not first disk")
|
||||
|
||||
// error returned by first disk waiting to initialize other servers.
|
||||
var errFirstDiskWait = errors.New("Waiting on other disks")
|
||||
|
||||
// error returned when a bucket already exists
|
||||
var errBucketAlreadyExists = errors.New("Your previous request to create the named bucket succeeded and you already own it")
|
||||
|
||||
// error returned for a negative actual size.
|
||||
var errInvalidDecompressedSize = errors.New("Invalid Decompressed Size")
|
||||
|
||||
// error returned in IAM subsystem when user doesn't exist.
|
||||
var errNoSuchUser = errors.New("Specified user does not exist")
|
||||
|
||||
// error returned in IAM subsystem when groups doesn't exist.
|
||||
var errNoSuchGroup = errors.New("Specified group does not exist")
|
||||
|
||||
// error returned in IAM subsystem when a non-empty group needs to be
|
||||
// deleted.
|
||||
var errGroupNotEmpty = errors.New("Specified group is not empty - cannot remove it")
|
||||
|
||||
// error returned in IAM subsystem when policy doesn't exist.
|
||||
var errNoSuchPolicy = errors.New("Specified canned policy does not exist")
|
||||
|
||||
// error returned in IAM subsystem when an external users systems is configured.
|
||||
var errIAMActionNotAllowed = errors.New("Specified IAM action is not allowed with LDAP configuration")
|
||||
|
||||
// error returned in IAM subsystem when IAM sub-system is still being initialized.
|
||||
var errIAMNotInitialized = errors.New("IAM sub-system is being initialized, please try again")
|
||||
|
||||
// error returned when access is denied.
|
||||
var errAccessDenied = errors.New("Do not have enough permissions to access this resource")
|
||||
|
||||
// error returned when object is locked.
|
||||
var errLockedObject = errors.New("Object is WORM protected and cannot be overwritten or deleted")
|
||||
|
||||
var (
|
||||
errInvalidAccessKeyID = errors.New("The access key ID you provided does not exist in our records")
|
||||
errChangeCredNotAllowed = errors.New("Changing access key and secret key not allowed")
|
||||
errAuthentication = errors.New("Authentication failed, check your access credentials")
|
||||
errNoAuthToken = errors.New("JWT token missing")
|
||||
errIncorrectCreds = errors.New("Current access key or secret key is incorrect")
|
||||
errPresignedNotAllowed = errors.New("Unable to generate shareable URL due to lack of read permissions")
|
||||
)
|
||||
|
||||
var (
|
||||
// AWS errors for invalid SSE-C requests.
|
||||
errEncryptedObject = errors.New("The object was stored using a form of SSE")
|
||||
errInvalidSSEParameters = errors.New("The SSE-C key for key-rotation is not correct") // special access denied
|
||||
errKMSNotConfigured = errors.New("KMS not configured for a server side encrypted object")
|
||||
// Additional MinIO errors for SSE-C requests.
|
||||
errObjectTampered = errors.New("The requested object was modified and may be compromised")
|
||||
// error returned when invalid encryption parameters are specified
|
||||
errInvalidEncryptionParameters = errors.New("The encryption parameters are not applicable to this object")
|
||||
)
|
||||
|
||||
// ErrNoEntriesFound - Indicates no entries were found for the given key (directory)
|
||||
var ErrNoEntriesFound = errors.New("No entries found for this key")
|
12
neofs/api/xl-v1-errors.go
Normal file
12
neofs/api/xl-v1-errors.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package api
|
||||
|
||||
import "errors"
|
||||
|
||||
// errXLReadQuorum - did not meet read quorum.
|
||||
var errXLReadQuorum = errors.New("Read failed. Insufficient number of disks online")
|
||||
|
||||
// errXLWriteQuorum - did not meet write quorum.
|
||||
var errXLWriteQuorum = errors.New("Write failed. Insufficient number of disks online")
|
||||
|
||||
// errNoHealRequired - returned when healing is attempted on a previously healed disks.
|
||||
var errNoHealRequired = errors.New("No healing is required")
|
Loading…
Reference in a new issue