forked from TrueCloudLab/frostfs-s3-gw
[#405] English Check
Signed-off-by: Elizaveta Chichindaeva <elizaveta@nspcc.ru>
This commit is contained in:
parent
a0a04a73bd
commit
bf38007692
42 changed files with 205 additions and 205 deletions
|
@ -252,7 +252,7 @@ func hmacSHA256(key []byte, data []byte) []byte {
|
||||||
return hash.Sum(nil)
|
return hash.Sum(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultipartFormValue get value by key from multipart form.
|
// MultipartFormValue gets value by key from multipart form.
|
||||||
func MultipartFormValue(r *http.Request, key string) string {
|
func MultipartFormValue(r *http.Request, key string) string {
|
||||||
if r.MultipartForm == nil {
|
if r.MultipartForm == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|
6
api/cache/accessbox.go
vendored
6
api/cache/accessbox.go
vendored
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// AccessBoxCache stores access box by its address.
|
// AccessBoxCache stores an access box by its address.
|
||||||
AccessBoxCache struct {
|
AccessBoxCache struct {
|
||||||
cache gcache.Cache
|
cache gcache.Cache
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ const (
|
||||||
DefaultAccessBoxCacheLifetime = 10 * time.Minute
|
DefaultAccessBoxCacheLifetime = 10 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultAccessBoxConfig return new default cache expiration values.
|
// DefaultAccessBoxConfig returns new default cache expiration values.
|
||||||
func DefaultAccessBoxConfig() *Config {
|
func DefaultAccessBoxConfig() *Config {
|
||||||
return &Config{Size: DefaultAccessBoxCacheSize, Lifetime: DefaultAccessBoxCacheLifetime}
|
return &Config{Size: DefaultAccessBoxCacheSize, Lifetime: DefaultAccessBoxCacheLifetime}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func NewAccessBoxCache(config *Config) *AccessBoxCache {
|
||||||
return &AccessBoxCache{cache: gc}
|
return &AccessBoxCache{cache: gc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns cached object.
|
// Get returns a cached object.
|
||||||
func (o *AccessBoxCache) Get(address *address.Address) *accessbox.Box {
|
func (o *AccessBoxCache) Get(address *address.Address) *accessbox.Box {
|
||||||
entry, err := o.cache.Get(address.String())
|
entry, err := o.cache.Get(address.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
6
api/cache/buckets.go
vendored
6
api/cache/buckets.go
vendored
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BucketCache contains cache with objects and lifetime of cache entries.
|
// BucketCache contains cache with objects and the lifetime of cache entries.
|
||||||
type BucketCache struct {
|
type BucketCache struct {
|
||||||
cache gcache.Cache
|
cache gcache.Cache
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ const (
|
||||||
DefaultBucketCacheLifetime = time.Minute
|
DefaultBucketCacheLifetime = time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultBucketConfig return new default cache expiration values.
|
// DefaultBucketConfig returns new default cache expiration values.
|
||||||
func DefaultBucketConfig() *Config {
|
func DefaultBucketConfig() *Config {
|
||||||
return &Config{Size: DefaultBucketCacheSize, Lifetime: DefaultBucketCacheLifetime}
|
return &Config{Size: DefaultBucketCacheSize, Lifetime: DefaultBucketCacheLifetime}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ func NewBucketCache(config *Config) *BucketCache {
|
||||||
return &BucketCache{cache: gc}
|
return &BucketCache{cache: gc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns cached object.
|
// Get returns a cached object.
|
||||||
func (o *BucketCache) Get(key string) *data.BucketInfo {
|
func (o *BucketCache) Get(key string) *data.BucketInfo {
|
||||||
entry, err := o.cache.Get(key)
|
entry, err := o.cache.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
8
api/cache/names.go
vendored
8
api/cache/names.go
vendored
|
@ -7,8 +7,8 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObjectsNameCache provides for lru cache for objects.
|
// ObjectsNameCache provides lru cache for objects.
|
||||||
// This cache contains mapping nice name to object addresses.
|
// This cache contains mapping nice names to object addresses.
|
||||||
// Key is bucketName+objectName.
|
// Key is bucketName+objectName.
|
||||||
type ObjectsNameCache struct {
|
type ObjectsNameCache struct {
|
||||||
cache gcache.Cache
|
cache gcache.Cache
|
||||||
|
@ -21,7 +21,7 @@ const (
|
||||||
DefaultObjectsNameCacheLifetime = time.Minute
|
DefaultObjectsNameCacheLifetime = time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultObjectsNameConfig return new default cache expiration values.
|
// DefaultObjectsNameConfig returns new default cache expiration values.
|
||||||
func DefaultObjectsNameConfig() *Config {
|
func DefaultObjectsNameConfig() *Config {
|
||||||
return &Config{Size: DefaultObjectsNameCacheSize, Lifetime: DefaultObjectsNameCacheLifetime}
|
return &Config{Size: DefaultObjectsNameCacheSize, Lifetime: DefaultObjectsNameCacheLifetime}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ func NewObjectsNameCache(config *Config) *ObjectsNameCache {
|
||||||
return &ObjectsNameCache{cache: gc}
|
return &ObjectsNameCache{cache: gc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns cached object.
|
// Get returns a cached object.
|
||||||
func (o *ObjectsNameCache) Get(key string) *address.Address {
|
func (o *ObjectsNameCache) Get(key string) *address.Address {
|
||||||
entry, err := o.cache.Get(key)
|
entry, err := o.cache.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
6
api/cache/objects.go
vendored
6
api/cache/objects.go
vendored
|
@ -16,11 +16,11 @@ type ObjectsCache struct {
|
||||||
const (
|
const (
|
||||||
// DefaultObjectsCacheLifetime is a default lifetime of entries in objects' cache.
|
// DefaultObjectsCacheLifetime is a default lifetime of entries in objects' cache.
|
||||||
DefaultObjectsCacheLifetime = time.Minute * 5
|
DefaultObjectsCacheLifetime = time.Minute * 5
|
||||||
// DefaultObjectsCacheSize is a default maximum number of entries in objects' in cache.
|
// DefaultObjectsCacheSize is a default maximum number of entries in objects' cache.
|
||||||
DefaultObjectsCacheSize = 1e6
|
DefaultObjectsCacheSize = 1e6
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultObjectsConfig return new default cache expiration values.
|
// DefaultObjectsConfig returns new default cache expiration values.
|
||||||
func DefaultObjectsConfig() *Config {
|
func DefaultObjectsConfig() *Config {
|
||||||
return &Config{Size: DefaultObjectsCacheSize, Lifetime: DefaultObjectsCacheLifetime}
|
return &Config{Size: DefaultObjectsCacheSize, Lifetime: DefaultObjectsCacheLifetime}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func New(config *Config) *ObjectsCache {
|
||||||
return &ObjectsCache{cache: gc}
|
return &ObjectsCache{cache: gc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns cached object.
|
// Get returns a cached object.
|
||||||
func (o *ObjectsCache) Get(address *address.Address) *object.Object {
|
func (o *ObjectsCache) Get(address *address.Address) *object.Object {
|
||||||
entry, err := o.cache.Get(address.String())
|
entry, err := o.cache.Get(address.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
16
api/cache/objectslist.go
vendored
16
api/cache/objectslist.go
vendored
|
@ -11,16 +11,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is an implementation of a cache which keeps unsorted lists of objects' IDs (all versions)
|
This is an implementation of cache which keeps unsorted lists of objects' IDs (all versions)
|
||||||
for a specified bucket and a prefix.
|
for a specified bucket and a prefix.
|
||||||
|
|
||||||
The cache contains gcache whose entries have a key: ObjectsListKey struct and a value: list of ids.
|
The cache contains gcache whose entries have a key: ObjectsListKey struct and a value: list of ids.
|
||||||
After putting a record it lives for a while (default value is 60 seconds).
|
After putting a record, it lives for a while (default value is 60 seconds).
|
||||||
|
|
||||||
When we receive a request from the user we try to find the suitable and non-expired cache entry, go through the list
|
When we receive a request from a user, we try to find the suitable and non-expired cache entry, go through the list
|
||||||
and get ObjectInfos from common object cache or with a request to NeoFS.
|
and get ObjectInfos from common object cache or with a request to NeoFS.
|
||||||
|
|
||||||
When we put an object into a container we invalidate entries with prefixes that are prefixes of the object's name.
|
When we put an object into a container, we invalidate entries with prefixes that are prefixes of the object's name.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -43,18 +43,18 @@ const (
|
||||||
DefaultObjectsListCacheSize = 1e5
|
DefaultObjectsListCacheSize = 1e5
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultObjectsListConfig return new default cache expiration values.
|
// DefaultObjectsListConfig returns new default cache expiration values.
|
||||||
func DefaultObjectsListConfig() *Config {
|
func DefaultObjectsListConfig() *Config {
|
||||||
return &Config{Size: DefaultObjectsListCacheSize, Lifetime: DefaultObjectsListCacheLifetime}
|
return &Config{Size: DefaultObjectsListCacheSize, Lifetime: DefaultObjectsListCacheLifetime}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewObjectsListCache is a constructor which creates an object of ListObjectsCache with given lifetime of entries.
|
// NewObjectsListCache is a constructor which creates an object of ListObjectsCache with the given lifetime of entries.
|
||||||
func NewObjectsListCache(config *Config) *ObjectsListCache {
|
func NewObjectsListCache(config *Config) *ObjectsListCache {
|
||||||
gc := gcache.New(config.Size).LRU().Expiration(config.Lifetime).Build()
|
gc := gcache.New(config.Size).LRU().Expiration(config.Lifetime).Build()
|
||||||
return &ObjectsListCache{cache: gc}
|
return &ObjectsListCache{cache: gc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get return list of ObjectInfo.
|
// Get returns a list of ObjectInfo.
|
||||||
func (l *ObjectsListCache) Get(key ObjectsListKey) []oid.ID {
|
func (l *ObjectsListCache) Get(key ObjectsListKey) []oid.ID {
|
||||||
entry, err := l.cache.Get(key)
|
entry, err := l.cache.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,7 +93,7 @@ func (l *ObjectsListCache) CleanCacheEntriesContainingObject(objectName string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateObjectsListCacheKey returns ObjectsListKey with given CID and prefix.
|
// CreateObjectsListCacheKey returns ObjectsListKey with the given CID and prefix.
|
||||||
func CreateObjectsListCacheKey(cid *cid.ID, prefix string) ObjectsListKey {
|
func CreateObjectsListCacheKey(cid *cid.ID, prefix string) ObjectsListKey {
|
||||||
p := ObjectsListKey{
|
p := ObjectsListKey{
|
||||||
cid: cid.String(),
|
cid: cid.String(),
|
||||||
|
|
4
api/cache/system.go
vendored
4
api/cache/system.go
vendored
|
@ -21,7 +21,7 @@ const (
|
||||||
DefaultSystemCacheLifetime = 5 * time.Minute
|
DefaultSystemCacheLifetime = 5 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultSystemConfig return new default cache expiration values.
|
// DefaultSystemConfig returns new default cache expiration values.
|
||||||
func DefaultSystemConfig() *Config {
|
func DefaultSystemConfig() *Config {
|
||||||
return &Config{Size: DefaultSystemCacheSize, Lifetime: DefaultSystemCacheLifetime}
|
return &Config{Size: DefaultSystemCacheSize, Lifetime: DefaultSystemCacheLifetime}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ func NewSystemCache(config *Config) *SystemCache {
|
||||||
return &SystemCache{cache: gc}
|
return &SystemCache{cache: gc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObject returns cached object.
|
// GetObject returns a cached object.
|
||||||
func (o *SystemCache) GetObject(key string) *data.ObjectInfo {
|
func (o *SystemCache) GetObject(key string) *data.ObjectInfo {
|
||||||
entry, err := o.cache.Get(key)
|
entry, err := o.cache.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -68,10 +68,10 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SettingsObjectName is system name for bucket settings file.
|
// SettingsObjectName is a system name for a bucket settings file.
|
||||||
func (b *BucketInfo) SettingsObjectName() string { return bktSettingsObject }
|
func (b *BucketInfo) SettingsObjectName() string { return bktSettingsObject }
|
||||||
|
|
||||||
// CORSObjectName returns system name for bucket CORS configuration file.
|
// CORSObjectName returns a system name for a bucket CORS configuration file.
|
||||||
func (b *BucketInfo) CORSObjectName() string { return bktCORSConfigurationObject }
|
func (b *BucketInfo) CORSObjectName() string { return bktCORSConfigurationObject }
|
||||||
|
|
||||||
func (b *BucketInfo) NotificationConfigurationObjectName() string {
|
func (b *BucketInfo) NotificationConfigurationObjectName() string {
|
||||||
|
@ -82,7 +82,7 @@ func (b *BucketInfo) NotificationConfigurationObjectName() string {
|
||||||
func (o *ObjectInfo) Version() string { return o.ID.String() }
|
func (o *ObjectInfo) Version() string { return o.ID.String() }
|
||||||
|
|
||||||
// NullableVersion returns object version from ObjectInfo.
|
// NullableVersion returns object version from ObjectInfo.
|
||||||
// Return "null" if "S3-Versions-unversioned" header present.
|
// Return "null" if "S3-Versions-unversioned" header is present.
|
||||||
func (o *ObjectInfo) NullableVersion() string {
|
func (o *ObjectInfo) NullableVersion() string {
|
||||||
if _, ok := o.Headers["S3-Versions-unversioned"]; ok {
|
if _, ok := o.Headers["S3-Versions-unversioned"]; ok {
|
||||||
return "null"
|
return "null"
|
||||||
|
@ -102,11 +102,11 @@ func (o *ObjectInfo) Address() *address.Address {
|
||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagsObject returns name of system object for tags.
|
// TagsObject returns the name of a system object for tags.
|
||||||
func (o *ObjectInfo) TagsObject() string { return ".tagset." + o.Name + "." + o.Version() }
|
func (o *ObjectInfo) TagsObject() string { return ".tagset." + o.Name + "." + o.Version() }
|
||||||
|
|
||||||
// LegalHoldObject returns name of system object for lock object.
|
// LegalHoldObject returns the name of a system object for a lock object.
|
||||||
func (o *ObjectInfo) LegalHoldObject() string { return ".lock." + o.Name + "." + o.Version() }
|
func (o *ObjectInfo) LegalHoldObject() string { return ".lock." + o.Name + "." + o.Version() }
|
||||||
|
|
||||||
// RetentionObject returns name of system object for retention lock object.
|
// RetentionObject returns the name of a system object for a retention lock object.
|
||||||
func (o *ObjectInfo) RetentionObject() string { return ".retention." + o.Name + "." + o.Version() }
|
func (o *ObjectInfo) RetentionObject() string { return ".retention." + o.Name + "." + o.Version() }
|
||||||
|
|
|
@ -28,7 +28,7 @@ type (
|
||||||
Value string `xml:"Value" json:"Value"`
|
Value string `xml:"Value" json:"Value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TopicConfiguration and LambdaFunctionConfiguration -- we don't support these configurations
|
// TopicConfiguration and LambdaFunctionConfiguration -- we don't support these configurations,
|
||||||
// but we need them to detect in notification configurations in requests.
|
// but we need them to detect in notification configurations in requests.
|
||||||
TopicConfiguration struct{}
|
TopicConfiguration struct{}
|
||||||
LambdaFunctionConfiguration struct{}
|
LambdaFunctionConfiguration struct{}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// ErrorCode type of error status.
|
// ErrorCode type of an error status.
|
||||||
ErrorCode int
|
ErrorCode int
|
||||||
|
|
||||||
errorCodeMap map[ErrorCode]Error
|
errorCodeMap map[ErrorCode]Error
|
||||||
|
@ -20,7 +20,7 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error codes, non exhaustive list - http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
// Error codes, non exhaustive list -- http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
||||||
const (
|
const (
|
||||||
_ ErrorCode = iota
|
_ ErrorCode = iota
|
||||||
ErrAccessDenied
|
ErrAccessDenied
|
||||||
|
@ -273,7 +273,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// error code to Error structure, these fields carry respective
|
// error code to Error structure, these fields carry respective
|
||||||
// descriptions for all the error responses.
|
// descriptions for all error responses.
|
||||||
var errorCodes = errorCodeMap{
|
var errorCodes = errorCodeMap{
|
||||||
ErrInvalidCopyDest: {
|
ErrInvalidCopyDest: {
|
||||||
ErrCode: ErrInvalidCopyDest,
|
ErrCode: ErrInvalidCopyDest,
|
||||||
|
@ -768,7 +768,7 @@ var errorCodes = errorCodeMap{
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
|
|
||||||
// FIXME: Actual XML error response also contains the header which missed in list of signed header parameters.
|
// FIXME: Actual XML error response also contains the header which is missed in the list of signed header parameters.
|
||||||
ErrUnsignedHeaders: {
|
ErrUnsignedHeaders: {
|
||||||
ErrCode: ErrUnsignedHeaders,
|
ErrCode: ErrUnsignedHeaders,
|
||||||
Code: "AccessDenied",
|
Code: "AccessDenied",
|
||||||
|
@ -1663,7 +1663,7 @@ var errorCodes = errorCodeMap{
|
||||||
// Add your error structure here.
|
// Add your error structure here.
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsS3Error check if the provided error is a specific s3 error.
|
// IsS3Error checks if the provided error is a specific s3 error.
|
||||||
func IsS3Error(err error, code ErrorCode) bool {
|
func IsS3Error(err error, code ErrorCode) bool {
|
||||||
e, ok := err.(Error)
|
e, ok := err.(Error)
|
||||||
return ok && e.ErrCode == code
|
return ok && e.ErrCode == code
|
||||||
|
@ -1688,7 +1688,7 @@ func (e Error) Error() string {
|
||||||
return fmt.Sprintf("%s: %d => %s", e.Code, e.HTTPStatusCode, e.Description)
|
return fmt.Sprintf("%s: %d => %s", e.Code, e.HTTPStatusCode, e.Description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIError provides API Error for input API error code.
|
// GetAPIError provides API Error for an input API error code.
|
||||||
func GetAPIError(code ErrorCode) Error {
|
func GetAPIError(code ErrorCode) Error {
|
||||||
if apiErr, ok := errorCodes[code]; ok {
|
if apiErr, ok := errorCodes[code]; ok {
|
||||||
return apiErr
|
return apiErr
|
||||||
|
@ -1696,12 +1696,12 @@ func GetAPIError(code ErrorCode) Error {
|
||||||
return errorCodes.toAPIErr(ErrInternalError)
|
return errorCodes.toAPIErr(ErrInternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIErrorWithError provides API Error with additional error message for input API error code.
|
// GetAPIErrorWithError provides API Error with additional error message for an input API error code.
|
||||||
func GetAPIErrorWithError(code ErrorCode, err error) Error {
|
func GetAPIErrorWithError(code ErrorCode, err error) Error {
|
||||||
return errorCodes.toAPIErrWithErr(code, err)
|
return errorCodes.toAPIErrWithErr(code, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectError - error that linked to specific object.
|
// ObjectError -- error that is linked to a specific object.
|
||||||
type ObjectError struct {
|
type ObjectError struct {
|
||||||
Err error
|
Err error
|
||||||
Object string
|
Object string
|
||||||
|
@ -1712,7 +1712,7 @@ func (e ObjectError) Error() string {
|
||||||
return fmt.Sprintf("%s (%s:%s)", e.Err, e.Object, e.Version)
|
return fmt.Sprintf("%s (%s:%s)", e.Err, e.Object, e.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectVersion get "object:version" string.
|
// ObjectVersion gets "object:version" string.
|
||||||
func (e ObjectError) ObjectVersion() string {
|
func (e ObjectError) ObjectVersion() string {
|
||||||
return e.Object + ":" + e.Version
|
return e.Object + ":" + e.Version
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,7 +357,7 @@ func checkOwner(info *data.BucketInfo, owner string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe need to convert owner to appropriate format
|
// may need to convert owner to appropriate format
|
||||||
if info.Owner.String() != owner {
|
if info.Owner.String() != owner {
|
||||||
return errors.GetAPIError(errors.ErrAccessDenied)
|
return errors.GetAPIError(errors.ErrAccessDenied)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,14 @@ type (
|
||||||
cfg *Config
|
cfg *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config contains data which handler need to keep.
|
// Config contains data which handler needs to keep.
|
||||||
Config struct {
|
Config struct {
|
||||||
DefaultPolicy *netmap.PlacementPolicy
|
DefaultPolicy *netmap.PlacementPolicy
|
||||||
DefaultMaxAge int
|
DefaultMaxAge int
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultPolicy is a default policy of placing container in NeoFS if it's not set at the request.
|
// DefaultPolicy is a default policy of placing containers in NeoFS if it's not set at the request.
|
||||||
const DefaultPolicy = "REP 3"
|
const DefaultPolicy = "REP 3"
|
||||||
|
|
||||||
var _ api.Handler = (*handler)(nil)
|
var _ api.Handler = (*handler)(nil)
|
||||||
|
|
|
@ -20,7 +20,7 @@ type copyObjectArgs struct {
|
||||||
|
|
||||||
const replaceMetadataDirective = "REPLACE"
|
const replaceMetadataDirective = "REPLACE"
|
||||||
|
|
||||||
// path2BucketObject returns bucket and object.
|
// path2BucketObject returns a bucket and an object.
|
||||||
func path2BucketObject(path string) (bucket, prefix string) {
|
func path2BucketObject(path string) (bucket, prefix string) {
|
||||||
path = strings.TrimPrefix(path, api.SlashSeparator)
|
path = strings.TrimPrefix(path, api.SlashSeparator)
|
||||||
m := strings.Index(path, api.SlashSeparator)
|
m := strings.Index(path, api.SlashSeparator)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultMaxAge -- default value of Access-Control-Max-Age if this value is not set in a rule.
|
// DefaultMaxAge is a default value of Access-Control-Max-Age if this value is not set in a rule.
|
||||||
DefaultMaxAge = 600
|
DefaultMaxAge = 600
|
||||||
wildcard = "*"
|
wildcard = "*"
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeleteObjectsRequest - xml carrying the object key names which needs to be deleted.
|
// DeleteObjectsRequest -- xml carrying the object key names which should be deleted.
|
||||||
type DeleteObjectsRequest struct {
|
type DeleteObjectsRequest struct {
|
||||||
// Element to enable quiet mode for the request
|
// Element to enable quiet mode for the request
|
||||||
Quiet bool
|
Quiet bool
|
||||||
|
@ -23,13 +23,13 @@ type DeleteObjectsRequest struct {
|
||||||
Objects []ObjectIdentifier `xml:"Object"`
|
Objects []ObjectIdentifier `xml:"Object"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectIdentifier carries key name for the object to delete.
|
// ObjectIdentifier carries the key name for the object to delete.
|
||||||
type ObjectIdentifier struct {
|
type ObjectIdentifier struct {
|
||||||
ObjectName string `xml:"Key"`
|
ObjectName string `xml:"Key"`
|
||||||
VersionID string `xml:"VersionId,omitempty"`
|
VersionID string `xml:"VersionId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletedObject carries key name for the object to delete.
|
// DeletedObject carries the key name for the object to delete.
|
||||||
type DeletedObject struct {
|
type DeletedObject struct {
|
||||||
ObjectIdentifier
|
ObjectIdentifier
|
||||||
DeleteMarker bool `xml:"DeleteMarker,omitempty"`
|
DeleteMarker bool `xml:"DeleteMarker,omitempty"`
|
||||||
|
@ -144,7 +144,7 @@ func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
reqInfo := api.GetReqInfo(r.Context())
|
reqInfo := api.GetReqInfo(r.Context())
|
||||||
|
|
||||||
// Content-Md5 is requied should be set
|
// Content-Md5 is required and should be set
|
||||||
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
||||||
if _, ok := r.Header[api.ContentMD5]; !ok {
|
if _, ok := r.Header[api.ContentMD5]; !ok {
|
||||||
h.logAndSendError(w, "missing Content-MD5", reqInfo, errors.GetAPIError(errors.ErrMissingContentMD5))
|
h.logAndSendError(w, "missing Content-MD5", reqInfo, errors.GetAPIError(errors.ErrMissingContentMD5))
|
||||||
|
|
|
@ -480,7 +480,7 @@ func TestObjectLegalHold(t *testing.T) {
|
||||||
hc.Handler().GetObjectLegalHoldHandler(w, r)
|
hc.Handler().GetObjectLegalHoldHandler(w, r)
|
||||||
assertLegalHold(t, w, legalHoldOn)
|
assertLegalHold(t, w, legalHoldOn)
|
||||||
|
|
||||||
// to make sure put hold is idempotent operation
|
// to make sure put hold is an idempotent operation
|
||||||
w, r = prepareTestRequest(t, bktName, objName, &data.LegalHold{Status: legalHoldOn})
|
w, r = prepareTestRequest(t, bktName, objName, &data.LegalHold{Status: legalHoldOn})
|
||||||
hc.Handler().PutObjectLegalHoldHandler(w, r)
|
hc.Handler().PutObjectLegalHoldHandler(w, r)
|
||||||
require.Equal(t, http.StatusOK, w.Code)
|
require.Equal(t, http.StatusOK, w.Code)
|
||||||
|
@ -493,7 +493,7 @@ func TestObjectLegalHold(t *testing.T) {
|
||||||
hc.Handler().GetObjectLegalHoldHandler(w, r)
|
hc.Handler().GetObjectLegalHoldHandler(w, r)
|
||||||
assertLegalHold(t, w, legalHoldOff)
|
assertLegalHold(t, w, legalHoldOff)
|
||||||
|
|
||||||
// to make sure put hold is idempotent operation
|
// to make sure put hold is an idempotent operation
|
||||||
w, r = prepareTestRequest(t, bktName, objName, &data.LegalHold{Status: legalHoldOff})
|
w, r = prepareTestRequest(t, bktName, objName, &data.LegalHold{Status: legalHoldOff})
|
||||||
hc.Handler().PutObjectLegalHoldHandler(w, r)
|
hc.Handler().PutObjectLegalHoldHandler(w, r)
|
||||||
require.Equal(t, http.StatusOK, w.Code)
|
require.Equal(t, http.StatusOK, w.Code)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package handler
|
||||||
|
|
||||||
import "encoding/xml"
|
import "encoding/xml"
|
||||||
|
|
||||||
// ListBucketsResponse - format for list buckets response.
|
// ListBucketsResponse -- format for list buckets response.
|
||||||
type ListBucketsResponse struct {
|
type ListBucketsResponse struct {
|
||||||
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"`
|
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"`
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ func NewGrantee(t GranteeType) *Grantee {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Owner - bucket owner/principal.
|
// Owner -- bucket owner/principal.
|
||||||
type Owner struct {
|
type Owner struct {
|
||||||
ID string
|
ID string
|
||||||
DisplayName string
|
DisplayName string
|
||||||
|
@ -106,7 +106,7 @@ type Object struct {
|
||||||
// Owner of the object.
|
// Owner of the object.
|
||||||
Owner *Owner `xml:"Owner,omitempty"`
|
Owner *Owner `xml:"Owner,omitempty"`
|
||||||
|
|
||||||
// The class of storage used to store the object.
|
// Class of storage used to store the object.
|
||||||
StorageClass string `xml:"StorageClass,omitempty"`
|
StorageClass string `xml:"StorageClass,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ type DeleteMarkerEntry struct {
|
||||||
// StringMap is a map[string]string.
|
// StringMap is a map[string]string.
|
||||||
type StringMap map[string]string
|
type StringMap map[string]string
|
||||||
|
|
||||||
// LocationResponse - format for location response.
|
// LocationResponse -- format for location response.
|
||||||
type LocationResponse struct {
|
type LocationResponse struct {
|
||||||
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ LocationConstraint" json:"-"`
|
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ LocationConstraint" json:"-"`
|
||||||
Location string `xml:",chardata"`
|
Location string `xml:",chardata"`
|
||||||
|
@ -182,13 +182,13 @@ type PostResponse struct {
|
||||||
ETag string `xml:"Etag"`
|
ETag string `xml:"Etag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tag is AWS key-value tag.
|
// Tag is an AWS key-value tag.
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
Key string
|
Key string
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalXML - StringMap marshals into XML.
|
// MarshalXML -- StringMap marshals into XML.
|
||||||
func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||||
tokens := []xml.Token{start}
|
tokens := []xml.Token{start}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ func shouldEscape(c byte) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// s3URLEncode is based on url.QueryEscape() code,
|
// s3URLEncode is based on url.QueryEscape() code
|
||||||
// while considering some S3 exceptions.
|
// while considering some S3 exceptions.
|
||||||
func s3URLEncode(s string, mode encoding) string {
|
func s3URLEncode(s string, mode encoding) string {
|
||||||
spaceCount, hexCount := 0, 0
|
spaceCount, hexCount := 0, 0
|
||||||
|
|
|
@ -270,8 +270,8 @@ func DefaultCachesConfigs() *CachesConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLayer creates instance of layer. It checks credentials
|
// NewLayer creates an instance of a layer. It checks credentials
|
||||||
// and establishes gRPC connection with node.
|
// and establishes gRPC connection with the node.
|
||||||
func NewLayer(log *zap.Logger, neoFS neofs.NeoFS, config *Config) Client {
|
func NewLayer(log *zap.Logger, neoFS neofs.NeoFS, config *Config) Client {
|
||||||
return &layer{
|
return &layer{
|
||||||
neoFS: neoFS,
|
neoFS: neoFS,
|
||||||
|
@ -307,7 +307,7 @@ func (n *layer) IsNotificationEnabled() bool {
|
||||||
return n.ncontroller != nil
|
return n.ncontroller != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAuthenticatedRequest check if access box exists in current request.
|
// IsAuthenticatedRequest checks if access box exists in the current request.
|
||||||
func IsAuthenticatedRequest(ctx context.Context) bool {
|
func IsAuthenticatedRequest(ctx context.Context) bool {
|
||||||
_, ok := ctx.Value(api.BoxData).(*accessbox.Box)
|
_, ok := ctx.Value(api.BoxData).(*accessbox.Box)
|
||||||
return ok
|
return ok
|
||||||
|
@ -364,12 +364,12 @@ func (n *layer) GetBucketACL(ctx context.Context, bktInfo *data.BucketInfo) (*Bu
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutBucketACL put bucket acl by name.
|
// PutBucketACL puts bucket acl by name.
|
||||||
func (n *layer) PutBucketACL(ctx context.Context, param *PutBucketACLParams) error {
|
func (n *layer) PutBucketACL(ctx context.Context, param *PutBucketACLParams) error {
|
||||||
return n.setContainerEACLTable(ctx, param.BktInfo.CID, param.EACL)
|
return n.setContainerEACLTable(ctx, param.BktInfo.CID, param.EACL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListBuckets returns all user containers. Name of the bucket is a container
|
// ListBuckets returns all user containers. The name of the bucket is a container
|
||||||
// id. Timestamp is omitted since it is not saved in neofs container.
|
// id. Timestamp is omitted since it is not saved in neofs container.
|
||||||
func (n *layer) ListBuckets(ctx context.Context) ([]*data.BucketInfo, error) {
|
func (n *layer) ListBuckets(ctx context.Context) ([]*data.BucketInfo, error) {
|
||||||
return n.containerList(ctx)
|
return n.containerList(ctx)
|
||||||
|
@ -532,7 +532,7 @@ func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*data.Obje
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObject removes all objects with passed nice name.
|
// DeleteObject removes all objects with the passed nice name.
|
||||||
func (n *layer) deleteObject(ctx context.Context, bkt *data.BucketInfo, settings *data.BucketSettings, obj *VersionedObject) *VersionedObject {
|
func (n *layer) deleteObject(ctx context.Context, bkt *data.BucketInfo, settings *data.BucketSettings, obj *VersionedObject) *VersionedObject {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
|
|
@ -255,7 +255,7 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
|
||||||
zap.String("uploadID", p.Info.UploadID),
|
zap.String("uploadID", p.Info.UploadID),
|
||||||
zap.String("uploadKey", p.Info.Key),
|
zap.String("uploadKey", p.Info.Key),
|
||||||
)
|
)
|
||||||
// we return InternalError because if we are here it means we've checked InitPart before in handler and
|
// we return InternalError because if we are here it means we've checked InitPart in handler before and
|
||||||
// received successful result, it's strange we didn't get the InitPart again
|
// received successful result, it's strange we didn't get the InitPart again
|
||||||
return nil, errors.GetAPIError(errors.ErrInternalError)
|
return nil, errors.GetAPIError(errors.ErrInternalError)
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
|
||||||
initMetadata[api.ContentType] = objects[0].ContentType
|
initMetadata[api.ContentType] = objects[0].ContentType
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We will keep "S3-Upload-Id" attribute in completed object to determine is it "common" object or completed object.
|
/* We will keep "S3-Upload-Id" attribute in a completed object to determine if it is a "common" object or a completed object.
|
||||||
We will need to differ these objects if something goes wrong during completing multipart upload.
|
We will need to differ these objects if something goes wrong during completing multipart upload.
|
||||||
I.e. we had completed the object but didn't put tagging/acl for some reason */
|
I.e. we had completed the object but didn't put tagging/acl for some reason */
|
||||||
delete(initMetadata, UploadPartNumberAttributeName)
|
delete(initMetadata, UploadPartNumberAttributeName)
|
||||||
|
|
|
@ -57,7 +57,7 @@ type PrmObjectSelect struct {
|
||||||
// Container to select the objects from.
|
// Container to select the objects from.
|
||||||
Container cid.ID
|
Container cid.ID
|
||||||
|
|
||||||
// Key-value object attribute which should exactly be
|
// Key-value object attribute which should be
|
||||||
// presented in selected objects. Optional, empty key means any.
|
// presented in selected objects. Optional, empty key means any.
|
||||||
ExactAttribute [2]string
|
ExactAttribute [2]string
|
||||||
|
|
||||||
|
@ -141,53 +141,53 @@ var ErrAccessDenied = errors.New("access denied")
|
||||||
// NeoFS represents virtual connection to NeoFS network.
|
// NeoFS represents virtual connection to NeoFS network.
|
||||||
type NeoFS interface {
|
type NeoFS interface {
|
||||||
// CreateContainer creates and saves parameterized container in NeoFS.
|
// CreateContainer creates and saves parameterized container in NeoFS.
|
||||||
// It sets 'Timestamp' attribute to current time.
|
// It sets 'Timestamp' attribute to the current time.
|
||||||
// Returns ID of the saved container.
|
// It returns the ID of the saved container.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the container to be created.
|
// prevented the container from being created.
|
||||||
CreateContainer(context.Context, PrmContainerCreate) (*cid.ID, error)
|
CreateContainer(context.Context, PrmContainerCreate) (*cid.ID, error)
|
||||||
|
|
||||||
// Container reads container from NeoFS by ID.
|
// Container reads a container from NeoFS by ID.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the container to be read.
|
// prevented the container from being read.
|
||||||
Container(context.Context, cid.ID) (*container.Container, error)
|
Container(context.Context, cid.ID) (*container.Container, error)
|
||||||
|
|
||||||
// UserContainers reads list of the containers owned by specified user.
|
// UserContainers reads a list of the containers owned by the specified user.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the containers to be listed.
|
// prevented the containers from being listed.
|
||||||
UserContainers(context.Context, owner.ID) ([]cid.ID, error)
|
UserContainers(context.Context, owner.ID) ([]cid.ID, error)
|
||||||
|
|
||||||
// SetContainerEACL saves eACL table of the container in NeoFS.
|
// SetContainerEACL saves the eACL table of the container in NeoFS.
|
||||||
//
|
//
|
||||||
// Returns any error encountered which prevented the eACL to be saved.
|
// It returns any error encountered which prevented the eACL from being saved.
|
||||||
SetContainerEACL(context.Context, eacl.Table) error
|
SetContainerEACL(context.Context, eacl.Table) error
|
||||||
|
|
||||||
// ContainerEACL reads container eACL from NeoFS by container ID.
|
// ContainerEACL reads the container eACL from NeoFS by the container ID.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the eACL to be read.
|
// prevented the eACL from being read.
|
||||||
ContainerEACL(context.Context, cid.ID) (*eacl.Table, error)
|
ContainerEACL(context.Context, cid.ID) (*eacl.Table, error)
|
||||||
|
|
||||||
// DeleteContainer marks the container to be removed from NeoFS by ID.
|
// DeleteContainer marks the container to be removed from NeoFS by ID.
|
||||||
// Request is sent within session if the session token is specified.
|
// Request is sent within session if the session token is specified.
|
||||||
// Successful return does not guarantee the actual removal.
|
// Successful return does not guarantee actual removal.
|
||||||
//
|
//
|
||||||
// Returns any error encountered which prevented the removal request to be sent.
|
// It returns any error encountered which prevented the removal request from being sent.
|
||||||
DeleteContainer(context.Context, cid.ID, *session.Token) error
|
DeleteContainer(context.Context, cid.ID, *session.Token) error
|
||||||
|
|
||||||
// SelectObjects perform object selection from the NeoFS container according
|
// SelectObjects performs object selection from the NeoFS container according
|
||||||
// to specified parameters. Selects user objects only.
|
// to the specified parameters. It selects user's objects only.
|
||||||
//
|
//
|
||||||
// Returns ErrAccessDenied on selection access violation.
|
// It returns ErrAccessDenied on selection access violation.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the objects to be selected.
|
// prevented the objects from being selected.
|
||||||
SelectObjects(context.Context, PrmObjectSelect) ([]oid.ID, error)
|
SelectObjects(context.Context, PrmObjectSelect) ([]oid.ID, error)
|
||||||
|
|
||||||
// ReadObject reads part of the object from the NeoFS container by identifier.
|
// ReadObject reads a part of the object from the NeoFS container by identifier.
|
||||||
// Exact part is returned according to the parameters:
|
// Exact part is returned according to the parameters:
|
||||||
// * with header only: empty payload (both in-mem and reader parts are nil);
|
// * with header only: empty payload (both in-mem and reader parts are nil);
|
||||||
// * with payload only: header is nil (zero range means full payload);
|
// * with payload only: header is nil (zero range means full payload);
|
||||||
|
@ -197,37 +197,37 @@ type NeoFS interface {
|
||||||
//
|
//
|
||||||
// Payload reader should be closed if it is no longer needed.
|
// Payload reader should be closed if it is no longer needed.
|
||||||
//
|
//
|
||||||
// Returns ErrAccessDenied on read access violation.
|
// It returns ErrAccessDenied on read access violation.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the object header to be read.
|
// prevented the object header from being read.
|
||||||
ReadObject(context.Context, PrmObjectRead) (*ObjectPart, error)
|
ReadObject(context.Context, PrmObjectRead) (*ObjectPart, error)
|
||||||
|
|
||||||
// CreateObject creates and saves parameterized object in the NeoFS container.
|
// CreateObject creates and saves a parameterized object in the NeoFS container.
|
||||||
// It sets 'Timestamp' attribute to current time.
|
// It sets 'Timestamp' attribute to the current time.
|
||||||
// Returns ID of the saved object.
|
// It returns the ID of the saved object.
|
||||||
//
|
//
|
||||||
// Creation time should be written into object (UTC).
|
// Creation time should be written into the object (UTC).
|
||||||
//
|
//
|
||||||
// Returns ErrAccessDenied on write access violation.
|
// It returns ErrAccessDenied on write access violation.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the container to be created.
|
// prevented the container from being created.
|
||||||
CreateObject(context.Context, PrmObjectCreate) (*oid.ID, error)
|
CreateObject(context.Context, PrmObjectCreate) (*oid.ID, error)
|
||||||
|
|
||||||
// DeleteObject marks the object to be removed from the NeoFS container by identifier.
|
// DeleteObject marks the object to be removed from the NeoFS container by identifier.
|
||||||
// Successful return does not guarantee the actual removal.
|
// Successful return does not guarantee actual removal.
|
||||||
//
|
//
|
||||||
// Returns ErrAccessDenied on remove access violation.
|
// It returns ErrAccessDenied on remove access violation.
|
||||||
//
|
//
|
||||||
// Returns any error encountered which prevented the removal request to be sent.
|
// It returns any error encountered which prevented the removal request from being sent.
|
||||||
DeleteObject(context.Context, PrmObjectDelete) error
|
DeleteObject(context.Context, PrmObjectDelete) error
|
||||||
|
|
||||||
// TimeToEpoch compute current epoch and epoch that corresponds provided time.
|
// TimeToEpoch computes current epoch and the epoch that corresponds to the provided time.
|
||||||
// Note:
|
// Note:
|
||||||
// * time must be in the future
|
// * time must be in the future
|
||||||
// * time will be ceil rounded to match epoch
|
// * time will be ceil rounded to match epoch
|
||||||
//
|
//
|
||||||
// Returns any error encountered which prevented computing epochs.
|
// It returns any error encountered which prevented computing epochs.
|
||||||
TimeToEpoch(context.Context, time.Time) (uint64, uint64, error)
|
TimeToEpoch(context.Context, time.Time) (uint64, uint64, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ func (n *layer) SendNotifications(ctx context.Context, p *SendNotificationParams
|
||||||
return n.ncontroller.SendNotifications(topics, p)
|
return n.ncontroller.SendNotifications(topics, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkBucketConfiguration checks notification configuration and generates ID for configurations with empty ids.
|
// checkBucketConfiguration checks notification configuration and generates an ID for configurations with empty ids.
|
||||||
func (n *layer) checkBucketConfiguration(conf *data.NotificationConfiguration, r *api.ReqInfo) (completed bool, err error) {
|
func (n *layer) checkBucketConfiguration(conf *data.NotificationConfiguration, r *api.ReqInfo) (completed bool, err error) {
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
return
|
return
|
||||||
|
@ -248,7 +248,7 @@ func filterSubjects(conf *data.NotificationConfiguration, eventType, objName str
|
||||||
for _, t := range conf.QueueConfigurations {
|
for _, t := range conf.QueueConfigurations {
|
||||||
event := false
|
event := false
|
||||||
for _, e := range t.Events {
|
for _, e := range t.Events {
|
||||||
// the second condition is comparison with events ending with *:
|
// the second condition is comparison with the events ending with *:
|
||||||
// s3:ObjectCreated:*, s3:ObjectRemoved:* etc without the last char
|
// s3:ObjectCreated:*, s3:ObjectRemoved:* etc without the last char
|
||||||
if eventType == e || strings.HasPrefix(eventType, e[:len(e)-1]) {
|
if eventType == e || strings.HasPrefix(eventType, e[:len(e)-1]) {
|
||||||
event = true
|
event = true
|
||||||
|
|
|
@ -40,7 +40,7 @@ type (
|
||||||
IsLatest bool
|
IsLatest bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListObjectVersionsInfo stores info and list of objects' versions.
|
// ListObjectVersionsInfo stores info and list of objects versions.
|
||||||
ListObjectVersionsInfo struct {
|
ListObjectVersionsInfo struct {
|
||||||
CommonPrefixes []string
|
CommonPrefixes []string
|
||||||
IsTruncated bool
|
IsTruncated bool
|
||||||
|
@ -138,7 +138,7 @@ func filenameFromObject(o *object.Object) string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
// NameFromString splits name into base file name and directory path.
|
// NameFromString splits name into a base file name and a directory path.
|
||||||
func NameFromString(name string) (string, string) {
|
func NameFromString(name string) (string, string) {
|
||||||
ind := strings.LastIndex(name, PathSeparator)
|
ind := strings.LastIndex(name, PathSeparator)
|
||||||
return name[ind+1:], name[:ind+1]
|
return name[ind+1:], name[:ind+1]
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// MaxClients provides HTTP handler wrapper with client limit.
|
// MaxClients provides HTTP handler wrapper with the client limit.
|
||||||
MaxClients interface {
|
MaxClients interface {
|
||||||
Handle(http.HandlerFunc) http.HandlerFunc
|
Handle(http.HandlerFunc) http.HandlerFunc
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ type (
|
||||||
const defaultRequestDeadline = time.Second * 30
|
const defaultRequestDeadline = time.Second * 30
|
||||||
|
|
||||||
// NewMaxClientsMiddleware returns MaxClients interface with handler wrapper based on
|
// NewMaxClientsMiddleware returns MaxClients interface with handler wrapper based on
|
||||||
// provided count and timeout limits.
|
// the provided count and the timeout limits.
|
||||||
func NewMaxClientsMiddleware(count int, timeout time.Duration) MaxClients {
|
func NewMaxClientsMiddleware(count int, timeout time.Duration) MaxClients {
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = defaultRequestDeadline
|
timeout = defaultRequestDeadline
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// HTTPAPIStats holds statistics information about
|
// HTTPAPIStats holds statistics information about
|
||||||
// a given API in the requests.
|
// the API given in the requests.
|
||||||
HTTPAPIStats struct {
|
HTTPAPIStats struct {
|
||||||
apiStats map[string]int
|
apiStats map[string]int
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
@ -64,7 +64,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collects HTTP metrics for NeoFS S3 Gate in Prometheus specific format
|
// Collects HTTP metrics for NeoFS S3 Gate in Prometheus specific format
|
||||||
// and sends to given channel.
|
// and sends to the given channel.
|
||||||
func collectHTTPMetrics(ch chan<- prometheus.Metric) {
|
func collectHTTPMetrics(ch chan<- prometheus.Metric) {
|
||||||
for api, value := range httpStatsMetric.currentS3Requests.Load() {
|
for api, value := range httpStatsMetric.currentS3Requests.Load() {
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
@ -122,7 +122,7 @@ func APIStats(api string, f http.HandlerFunc) http.HandlerFunc {
|
||||||
f.ServeHTTP(statsWriter, r)
|
f.ServeHTTP(statsWriter, r)
|
||||||
|
|
||||||
// Time duration in secs since the call started.
|
// Time duration in secs since the call started.
|
||||||
// We don't need to do nanosecond precision in this
|
// We don't need to do nanosecond precision here
|
||||||
// simply for the fact that it is not human readable.
|
// simply for the fact that it is not human readable.
|
||||||
durationSecs := time.Since(statsWriter.startTime).Seconds()
|
durationSecs := time.Since(statsWriter.startTime).Seconds()
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ func (st *HTTPStats) updateStats(api string, w http.ResponseWriter, r *http.Requ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHeader - writes http status code.
|
// WriteHeader -- writes http status code.
|
||||||
func (w *responseWrapper) WriteHeader(code int) {
|
func (w *responseWrapper) WriteHeader(code int) {
|
||||||
w.Do(func() {
|
w.Do(func() {
|
||||||
w.statusCode = code
|
w.statusCode = code
|
||||||
|
@ -209,7 +209,7 @@ func (w *responseWrapper) WriteHeader(code int) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush - Calls the underlying Flush.
|
// Flush -- calls the underlying Flush.
|
||||||
func (w *responseWrapper) Flush() {
|
func (w *responseWrapper) Flush() {
|
||||||
if f, ok := w.ResponseWriter.(http.Flusher); ok {
|
if f, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||||
f.Flush()
|
f.Flush()
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// KeyVal - appended to ReqInfo.Tags.
|
// KeyVal -- appended to ReqInfo.Tags.
|
||||||
KeyVal struct {
|
KeyVal struct {
|
||||||
Key string
|
Key string
|
||||||
Val string
|
Val string
|
||||||
|
@ -27,7 +27,7 @@ type (
|
||||||
UserAgent string // User Agent
|
UserAgent string // User Agent
|
||||||
DeploymentID string // random generated s3-deployment-id
|
DeploymentID string // random generated s3-deployment-id
|
||||||
RequestID string // x-amz-request-id
|
RequestID string // x-amz-request-id
|
||||||
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
API string // API name -- GetObject PutObject NewMultipartUpload etc.
|
||||||
BucketName string // Bucket name
|
BucketName string // Bucket name
|
||||||
ObjectName string // Object name
|
ObjectName string // Object name
|
||||||
URL *url.URL // Request url
|
URL *url.URL // Request url
|
||||||
|
@ -64,15 +64,15 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetSourceIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239
|
// GetSourceIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239
|
||||||
// Forwarded headers (in that order), falls back to r.RemoteAddr when all
|
// Forwarded headers (in that order), falls back to r.RemoteAddr when everything
|
||||||
// else fails.
|
// else fails.
|
||||||
func GetSourceIP(r *http.Request) string {
|
func GetSourceIP(r *http.Request) string {
|
||||||
var addr string
|
var addr string
|
||||||
|
|
||||||
if fwd := r.Header.Get(xForwardedFor); fwd != "" {
|
if fwd := r.Header.Get(xForwardedFor); fwd != "" {
|
||||||
// Only grab the first (client) address. Note that '192.168.0.1,
|
// Only grabs the first (client) address. Note that '192.168.0.1,
|
||||||
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
|
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
|
||||||
// the first may represent forwarding proxies earlier in the chain.
|
// the first one may represent forwarding proxies earlier in the chain.
|
||||||
s := strings.Index(fwd, ", ")
|
s := strings.Index(fwd, ", ")
|
||||||
if s == -1 {
|
if s == -1 {
|
||||||
s = len(fwd)
|
s = len(fwd)
|
||||||
|
@ -141,7 +141,7 @@ func NewReqInfo(w http.ResponseWriter, r *http.Request, req ObjectRequest) *ReqI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendTags - appends key/val to ReqInfo.tags.
|
// AppendTags -- appends key/val to ReqInfo.tags.
|
||||||
func (r *ReqInfo) AppendTags(key string, val string) *ReqInfo {
|
func (r *ReqInfo) AppendTags(key string, val string) *ReqInfo {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -152,14 +152,14 @@ func (r *ReqInfo) AppendTags(key string, val string) *ReqInfo {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTags - sets key/val to ReqInfo.tags.
|
// SetTags -- sets key/val to ReqInfo.tags.
|
||||||
func (r *ReqInfo) SetTags(key string, val string) *ReqInfo {
|
func (r *ReqInfo) SetTags(key string, val string) *ReqInfo {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
r.Lock()
|
r.Lock()
|
||||||
defer r.Unlock()
|
defer r.Unlock()
|
||||||
// Search of tag key already exists in tags
|
// Search for a tag key already existing in tags
|
||||||
var updated bool
|
var updated bool
|
||||||
for _, tag := range r.tags {
|
for _, tag := range r.tags {
|
||||||
if tag.Key == key {
|
if tag.Key == key {
|
||||||
|
@ -175,7 +175,7 @@ func (r *ReqInfo) SetTags(key string, val string) *ReqInfo {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTags - returns the user defined tags.
|
// GetTags -- returns the user defined tags.
|
||||||
func (r *ReqInfo) GetTags() []KeyVal {
|
func (r *ReqInfo) GetTags() []KeyVal {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -18,8 +18,8 @@ const (
|
||||||
type NeoFS interface {
|
type NeoFS interface {
|
||||||
// SystemDNS reads system DNS network parameters of the NeoFS.
|
// SystemDNS reads system DNS network parameters of the NeoFS.
|
||||||
//
|
//
|
||||||
// Returns exactly on non-zero value. Returns any error encountered
|
// It returns exactly on non-zero value. It returns any error encountered
|
||||||
// which prevented the parameter to be read.
|
// which prevented the parameter from being read.
|
||||||
SystemDNS(context.Context) (string, error)
|
SystemDNS(context.Context) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// ErrorResponse - error response format.
|
// ErrorResponse -- error response format.
|
||||||
ErrorResponse struct {
|
ErrorResponse struct {
|
||||||
XMLName xml.Name `xml:"Error" json:"-"`
|
XMLName xml.Name `xml:"Error" json:"-"`
|
||||||
Code string
|
Code string
|
||||||
|
@ -24,7 +24,7 @@ type (
|
||||||
RequestID string `xml:"RequestId" json:"RequestId"`
|
RequestID string `xml:"RequestId" json:"RequestId"`
|
||||||
HostID string `xml:"HostId" json:"HostId"`
|
HostID string `xml:"HostId" json:"HostId"`
|
||||||
|
|
||||||
// Region where the bucket is located. This header is returned
|
// The region where the bucket is located. This header is returned
|
||||||
// only in HEAD bucket and ListObjects response.
|
// only in HEAD bucket and ListObjects response.
|
||||||
Region string `xml:"Region,omitempty" json:"Region,omitempty"`
|
Region string `xml:"Region,omitempty" json:"Region,omitempty"`
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ func WriteErrorResponse(w http.ResponseWriter, reqInfo *ReqInfo, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate error response.
|
// Generates error response.
|
||||||
errorResponse := getAPIErrorResponse(reqInfo, err)
|
errorResponse := getAPIErrorResponse(reqInfo, err)
|
||||||
encodedErrorResponse := EncodeResponse(errorResponse)
|
encodedErrorResponse := EncodeResponse(errorResponse)
|
||||||
WriteResponse(w, code, encodedErrorResponse, MimeXML)
|
WriteResponse(w, code, encodedErrorResponse, MimeXML)
|
||||||
|
@ -152,7 +152,7 @@ func setCommonHeaders(w http.ResponseWriter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeSensitiveHeaders removes confidential encryption
|
// removeSensitiveHeaders removes confidential encryption
|
||||||
// information - e.g. the SSE-C key - from the HTTP headers.
|
// information -- e.g. the SSE-C key -- from the HTTP headers.
|
||||||
// It has the same semantics as RemoveSensitiveEntries.
|
// It has the same semantics as RemoveSensitiveEntries.
|
||||||
func removeSensitiveHeaders(h http.Header) {
|
func removeSensitiveHeaders(h http.Header) {
|
||||||
h.Del(hdrSSECustomerKey)
|
h.Del(hdrSSECustomerKey)
|
||||||
|
@ -212,7 +212,7 @@ func WriteSuccessResponseHeadersOnly(w http.ResponseWriter) {
|
||||||
WriteResponse(w, http.StatusOK, nil, MimeNone)
|
WriteResponse(w, http.StatusOK, nil, MimeNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error - Returns S3 error string.
|
// Error -- Returns S3 error string.
|
||||||
func (e ErrorResponse) Error() string {
|
func (e ErrorResponse) Error() string {
|
||||||
if e.Message == "" {
|
if e.Message == "" {
|
||||||
msg, ok := s3ErrorResponseMap[e.Code]
|
msg, ok := s3ErrorResponseMap[e.Code]
|
||||||
|
@ -225,7 +225,7 @@ func (e ErrorResponse) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getErrorResponse gets in standard error and resource value and
|
// getErrorResponse gets in standard error and resource value and
|
||||||
// provides a encodable populated response values.
|
// provides an encodable populated response values.
|
||||||
func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse {
|
func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse {
|
||||||
code := "InternalError"
|
code := "InternalError"
|
||||||
desc := err.Error()
|
desc := err.Error()
|
||||||
|
|
|
@ -83,7 +83,7 @@ type (
|
||||||
ListMultipartUploadsHandler(http.ResponseWriter, *http.Request)
|
ListMultipartUploadsHandler(http.ResponseWriter, *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mimeType represents various MIME type used API responses.
|
// mimeType represents various MIME types used in API responses.
|
||||||
mimeType string
|
mimeType string
|
||||||
|
|
||||||
logResponseWriter struct {
|
logResponseWriter struct {
|
||||||
|
@ -95,7 +95,7 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// SlashSeparator - slash separator.
|
// SlashSeparator -- slash separator.
|
||||||
SlashSeparator = "/"
|
SlashSeparator = "/"
|
||||||
|
|
||||||
// MimeNone means no response type.
|
// MimeNone means no response type.
|
||||||
|
@ -172,7 +172,7 @@ func logErrorResponse(l *zap.Logger) mux.MiddlewareFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequestID returns request ID from response writer or context.
|
// GetRequestID returns the request ID from the response writer or the context.
|
||||||
func GetRequestID(v interface{}) string {
|
func GetRequestID(v interface{}) string {
|
||||||
switch t := v.(type) {
|
switch t := v.(type) {
|
||||||
case context.Context:
|
case context.Context:
|
||||||
|
@ -244,11 +244,11 @@ func Attach(r *mux.Router, domains []string, m MaxClients, h Handler, center aut
|
||||||
bucket.Methods(http.MethodGet).HandlerFunc(
|
bucket.Methods(http.MethodGet).HandlerFunc(
|
||||||
m.Handle(metrics.APIStats("listmultipartuploads", h.ListMultipartUploadsHandler))).Queries("uploads", "").
|
m.Handle(metrics.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(metrics.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(metrics.APIStats("putobjectacl", h.PutObjectACLHandler))).Queries("acl", "").
|
||||||
Name("PutObjectACL")
|
Name("PutObjectACL")
|
||||||
|
@ -336,27 +336,27 @@ func Attach(r *mux.Router, domains []string, m MaxClients, h Handler, center aut
|
||||||
bucket.Methods(http.MethodPut).HandlerFunc(
|
bucket.Methods(http.MethodPut).HandlerFunc(
|
||||||
m.Handle(metrics.APIStats("putbucketacl", h.PutBucketACLHandler))).Queries("acl", "").
|
m.Handle(metrics.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(metrics.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(metrics.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(metrics.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(metrics.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(metrics.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(metrics.APIStats("getbucketreplication", h.GetBucketReplicationHandler))).Queries("replication", "").
|
||||||
Name("GetBucketReplication")
|
Name("GetBucketReplication")
|
||||||
|
@ -480,7 +480,7 @@ func Attach(r *mux.Router, domains []string, m MaxClients, h Handler, center aut
|
||||||
m.Handle(metrics.APIStats("listbuckets", h.ListBucketsHandler))).
|
m.Handle(metrics.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 = metrics.APIStats("notfound", errorResponseHandler)
|
||||||
api.MethodNotAllowedHandler = metrics.APIStats("methodnotallowed", errorResponseHandler)
|
api.MethodNotAllowedHandler = metrics.APIStats("methodnotallowed", errorResponseHandler)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,26 +54,26 @@ type NeoFS interface {
|
||||||
tokens.NeoFS
|
tokens.NeoFS
|
||||||
|
|
||||||
// ContainerExists checks container presence in NeoFS by identifier.
|
// ContainerExists checks container presence in NeoFS by identifier.
|
||||||
// Returns nil iff container exists.
|
// Returns nil if container exists.
|
||||||
ContainerExists(context.Context, cid.ID) error
|
ContainerExists(context.Context, cid.ID) error
|
||||||
|
|
||||||
// CreateContainer creates and saves parameterized container in NeoFS.
|
// CreateContainer creates and saves parameterized container in NeoFS.
|
||||||
// It sets 'Timestamp' attribute to current time.
|
// It sets 'Timestamp' attribute to the current time.
|
||||||
// Returns ID of the saved container.
|
// It returns the ID of the saved container.
|
||||||
//
|
//
|
||||||
// The container must be private with GET access of OTHERS group.
|
// The container must be private with GET access for OTHERS group.
|
||||||
// Creation time should also be stamped.
|
// Creation time should also be stamped.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the container to be created.
|
// prevented the container from being created.
|
||||||
CreateContainer(context.Context, PrmContainerCreate) (*cid.ID, error)
|
CreateContainer(context.Context, PrmContainerCreate) (*cid.ID, error)
|
||||||
|
|
||||||
// TimeToEpoch compute current epoch and epoch that corresponds provided time.
|
// TimeToEpoch computes the current epoch and the epoch that corresponds to the provided time.
|
||||||
// Note:
|
// Note:
|
||||||
// * time must be in the future
|
// * time must be in the future
|
||||||
// * time will be ceil rounded to match epoch
|
// * time will be ceil rounded to match epoch
|
||||||
//
|
//
|
||||||
// Returns any error encountered which prevented computing epochs.
|
// It returns any error encountered which prevented computing epochs.
|
||||||
TimeToEpoch(context.Context, time.Time) (uint64, uint64, error)
|
TimeToEpoch(context.Context, time.Time) (uint64, uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// lifetimeOptions holds NeoFS epochs, iat -- epoch, which a token was issued at, exp -- epoch, when the token expires.
|
// lifetimeOptions holds NeoFS epochs, iat -- epoch which the token was issued at, exp -- epoch when the token expires.
|
||||||
type lifetimeOptions struct {
|
type lifetimeOptions struct {
|
||||||
Iat uint64
|
Iat uint64
|
||||||
Exp uint64
|
Exp uint64
|
||||||
|
@ -141,7 +141,7 @@ type (
|
||||||
|
|
||||||
func (a *Agent) checkContainer(ctx context.Context, opts ContainerOptions, idOwner *owner.ID) (*cid.ID, error) {
|
func (a *Agent) checkContainer(ctx context.Context, opts ContainerOptions, idOwner *owner.ID) (*cid.ID, error) {
|
||||||
if opts.ID != nil {
|
if opts.ID != nil {
|
||||||
// check that container exists
|
// check that the container exists
|
||||||
return opts.ID, a.neoFS.ContainerExists(ctx, *opts.ID)
|
return opts.ID, a.neoFS.ContainerExists(ctx, *opts.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -345,7 +345,7 @@ func getJSONRules(val string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSessionRules reads json session rules.
|
// getSessionRules reads json session rules.
|
||||||
// Returns true if rules must be skipped.
|
// It returns true if rules must be skipped.
|
||||||
func getSessionRules(r string) ([]byte, bool, error) {
|
func getSessionRules(r string) ([]byte, bool, error) {
|
||||||
if r == "none" {
|
if r == "none" {
|
||||||
return nil, true, nil
|
return nil, true, nil
|
||||||
|
|
|
@ -194,11 +194,11 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait waits for application to finish.
|
// Wait waits for an application to finish.
|
||||||
//
|
//
|
||||||
// Pre-logs a message about the launch of the application mentioning its
|
// Pre-logs a message about the launch of the application mentioning its
|
||||||
// version (version.Version) and name (neofs-s3-gw). At the end writes to the
|
// version (version.Version) and its name (neofs-s3-gw). At the end, it writes
|
||||||
// log about the stop.
|
// about the stop to the log.
|
||||||
func (a *App) Wait() {
|
func (a *App) Wait() {
|
||||||
a.log.Info("application started",
|
a.log.Info("application started",
|
||||||
zap.String("name", "neofs-s3-gw"),
|
zap.String("name", "neofs-s3-gw"),
|
||||||
|
|
|
@ -99,7 +99,7 @@ const ( // Settings.
|
||||||
cmdVersion = "version"
|
cmdVersion = "version"
|
||||||
cmdConfig = "config"
|
cmdConfig = "config"
|
||||||
|
|
||||||
// envPrefix is environment variables prefix used for configuration.
|
// envPrefix is an environment variables prefix used for configuration.
|
||||||
envPrefix = "S3_GW"
|
envPrefix = "S3_GW"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// newLogger constructs a zap.Logger instance for current application.
|
// newLogger constructs a zap.Logger instance for the current application.
|
||||||
// Panics on failure.
|
// Panics on failure.
|
||||||
//
|
//
|
||||||
// Logger is built from zap's production logging configuration with:
|
// Logger is built from zap's production logging configuration with:
|
||||||
|
|
|
@ -40,22 +40,22 @@ type GateData struct {
|
||||||
GateKey *keys.PublicKey
|
GateKey *keys.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGateData returns GateData from provided bearer token and public gate key.
|
// NewGateData returns GateData from the provided bearer token and the public gate key.
|
||||||
func NewGateData(gateKey *keys.PublicKey, bearerTkn *token.BearerToken) *GateData {
|
func NewGateData(gateKey *keys.PublicKey, bearerTkn *token.BearerToken) *GateData {
|
||||||
return &GateData{GateKey: gateKey, BearerToken: bearerTkn}
|
return &GateData{GateKey: gateKey, BearerToken: bearerTkn}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionTokenForPut return the first suitable container session context for PUT operation.
|
// SessionTokenForPut returns the first suitable container session context for PUT operation.
|
||||||
func (g *GateData) SessionTokenForPut() *session.Token {
|
func (g *GateData) SessionTokenForPut() *session.Token {
|
||||||
return g.containerSessionToken(apisession.ContainerVerbPut)
|
return g.containerSessionToken(apisession.ContainerVerbPut)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionTokenForDelete return the first suitable container session context for DELETE operation.
|
// SessionTokenForDelete returns the first suitable container session context for DELETE operation.
|
||||||
func (g *GateData) SessionTokenForDelete() *session.Token {
|
func (g *GateData) SessionTokenForDelete() *session.Token {
|
||||||
return g.containerSessionToken(apisession.ContainerVerbDelete)
|
return g.containerSessionToken(apisession.ContainerVerbDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionTokenForSetEACL return the first suitable container session context for SetEACL operation.
|
// SessionTokenForSetEACL returns the first suitable container session context for SetEACL operation.
|
||||||
func (g *GateData) SessionTokenForSetEACL() *session.Token {
|
func (g *GateData) SessionTokenForSetEACL() *session.Token {
|
||||||
return g.containerSessionToken(apisession.ContainerVerbSetEACL)
|
return g.containerSessionToken(apisession.ContainerVerbSetEACL)
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ func isAppropriateContainerContext(ctx *session.ContainerContext, verb apisessio
|
||||||
verb == apisession.ContainerVerbSetEACL && ctx.IsForSetEACL()
|
verb == apisession.ContainerVerbSetEACL && ctx.IsForSetEACL()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secrets represents AccessKey and key to encrypt gate tokens.
|
// Secrets represents AccessKey and the key to encrypt gate tokens.
|
||||||
type Secrets struct {
|
type Secrets struct {
|
||||||
AccessKey string
|
AccessKey string
|
||||||
EphemeralKey *keys.PrivateKey
|
EphemeralKey *keys.PrivateKey
|
||||||
|
@ -94,7 +94,7 @@ func (x *AccessBox) Unmarshal(data []byte) error {
|
||||||
return proto.Unmarshal(data, x)
|
return proto.Unmarshal(data, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackTokens adds a bearer and session tokens to BearerTokens and SessionToken lists respectively.
|
// PackTokens adds bearer and session tokens to BearerTokens and SessionToken lists respectively.
|
||||||
// Session token can be nil.
|
// Session token can be nil.
|
||||||
func PackTokens(gatesData []*GateData) (*AccessBox, *Secrets, error) {
|
func PackTokens(gatesData []*GateData) (*AccessBox, *Secrets, error) {
|
||||||
box := &AccessBox{}
|
box := &AccessBox{}
|
||||||
|
@ -156,7 +156,7 @@ func (x *AccessBox) GetPlacementPolicy() ([]*ContainerPolicy, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBox parse AccessBox to Box.
|
// GetBox parses AccessBox to Box.
|
||||||
func (x *AccessBox) GetBox(owner *keys.PrivateKey) (*Box, error) {
|
func (x *AccessBox) GetBox(owner *keys.PrivateKey) (*Box, error) {
|
||||||
tokens, err := x.GetTokens(owner)
|
tokens, err := x.GetTokens(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -51,18 +51,18 @@ type PrmObjectCreate struct {
|
||||||
// NeoFS represents virtual connection to NeoFS network.
|
// NeoFS represents virtual connection to NeoFS network.
|
||||||
type NeoFS interface {
|
type NeoFS interface {
|
||||||
// CreateObject creates and saves a parameterized object in the specified
|
// CreateObject creates and saves a parameterized object in the specified
|
||||||
// NeoFS container from a specific user. It sets 'Timestamp' attribute to current time.
|
// NeoFS container from a specific user. It sets 'Timestamp' attribute to the current time.
|
||||||
// Returns ID of the saved object.
|
// It returns the ID of the saved object.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the object to be created.
|
// prevented the object from being created.
|
||||||
CreateObject(context.Context, PrmObjectCreate) (*oid.ID, error)
|
CreateObject(context.Context, PrmObjectCreate) (*oid.ID, error)
|
||||||
|
|
||||||
// ReadObjectPayload reads payload of the object from NeoFS network by address
|
// ReadObjectPayload reads payload of the object from NeoFS network by address
|
||||||
// into memory.
|
// into memory.
|
||||||
//
|
//
|
||||||
// Returns exactly one non-nil value. Returns any error encountered which
|
// It returns exactly one non-nil value. It returns any error encountered which
|
||||||
// prevented the object payload to be read.
|
// prevented the object payload from being read.
|
||||||
ReadObjectPayload(context.Context, address.Address) ([]byte, error)
|
ReadObjectPayload(context.Context, address.Address) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ var (
|
||||||
|
|
||||||
var _ = New
|
var _ = New
|
||||||
|
|
||||||
// New creates new Credentials instance using given cli and key.
|
// New creates a new Credentials instance using the given cli and key.
|
||||||
func New(neoFS NeoFS, key *keys.PrivateKey, config *cache.Config) Credentials {
|
func New(neoFS NeoFS, key *keys.PrivateKey, config *cache.Config) Credentials {
|
||||||
return &cred{neoFS: neoFS, key: key, cache: cache.NewAccessBoxCache(config)}
|
return &cred{neoFS: neoFS, key: key, cache: cache.NewAccessBoxCache(config)}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
Authmate is a tool to create gateway AWS credentials. AWS users
|
Authmate is a tool to create gateway AWS credentials. AWS users
|
||||||
are authenticated with access key IDs and secrets, while NeoFS users are
|
are authenticated with access key IDs and secrets, while NeoFS users are
|
||||||
authenticated with key pairs. To complicate things further we have S3 gateway
|
authenticated with key pairs. To complicate things further, we have S3 gateway
|
||||||
that usually acts on behalf of some user, but user doesn't necessarily want to
|
that usually acts on behalf of some user, but the user doesn't necessarily want to
|
||||||
give their keys to the gateway.
|
give their keys to the gateway.
|
||||||
|
|
||||||
To solve this, we use NeoFS bearer tokens that are signed by the owner (NeoFS
|
To solve this, we use NeoFS bearer tokens that are signed by the owner (NeoFS
|
||||||
"user") and that can implement any kind of policy for NeoFS requests allowed
|
"user") and that can implement any kind of policy for NeoFS requests allowed
|
||||||
using this token. However, tokens can't be used as AWS credentials directly, thus
|
to use this token. However, tokens can't be used as AWS credentials directly. Thus,
|
||||||
they're stored on NeoFS as regular objects, and access key ID is just an
|
they're stored on NeoFS as regular objects, and an access key ID is just an
|
||||||
address of this object while secret is generated randomly.
|
address of this object while a secret is generated randomly.
|
||||||
|
|
||||||
Tokens are not stored on NeoFS in plaintext, they're encrypted with a set of
|
Tokens are not stored on NeoFS in plaintext, they're encrypted with a set of
|
||||||
gateway keys. So in order for a gateway to be able to successfully extract bearer
|
gateway keys. So, in order for a gateway to be able to successfully extract bearer
|
||||||
token, the object needs to be stored in a container available for the gateway
|
token, the object needs to be stored in a container available for the gateway
|
||||||
to read, and it needs to be encrypted with this gateway's key (among others
|
to read, and it needs to be encrypted with this gateway's key (among others
|
||||||
potentially).
|
potentially).
|
||||||
|
@ -67,10 +67,10 @@ Confirm passphrase >
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet successfully created, file location is wallet.json
|
wallet is successfully created, the file location is wallet.json
|
||||||
```
|
```
|
||||||
|
|
||||||
To get public key from the wallet:
|
To get the public key from the wallet:
|
||||||
```shell
|
```shell
|
||||||
$ ./bin/neo-go wallet dump-keys -w wallet.json
|
$ ./bin/neo-go wallet dump-keys -w wallet.json
|
||||||
|
|
||||||
|
@ -80,23 +80,23 @@ NhLQpDnerpviUWDF77j5qyjFgavCmasJ4p (simple signature contract):
|
||||||
|
|
||||||
## Issuance of a secret
|
## Issuance of a secret
|
||||||
|
|
||||||
To issue a secret means to create a Bearer and, optionally, Session tokens and
|
To issue a secret means to create Bearer and, optionally, Session tokens and
|
||||||
put them as an object into a container on the NeoFS network.
|
put them as an object into a container on the NeoFS network.
|
||||||
|
|
||||||
### CLI parameters
|
### CLI parameters
|
||||||
|
|
||||||
**Required parameters:**
|
**Required parameters:**
|
||||||
* `--wallet` - a path to a wallet `.json` file. You can provide a passphrase to decrypt
|
* `--wallet` is a path to a wallet `.json` file. You can provide a passphrase to decrypt
|
||||||
a wallet via environment variable `AUTHMATE_WALLET_PASSPHRASE`, or you will be asked to enter a passphrase
|
a wallet via environment variable `AUTHMATE_WALLET_PASSPHRASE`, or you will be asked to enter a passphrase
|
||||||
interactively. You can also specify an account address to use from a wallet using the `--address` parameter.
|
interactively. You can also specify an account address to use from a wallet using the `--address` parameter.
|
||||||
* `--peer` - address of a NeoFS peer to connect to
|
* `--peer` is an address of a NeoFS peer to connect to
|
||||||
* `--gate-public-key` -- public `secp256r1` 33-byte short key of a gate (use flags repeatedly for multiple gates). The tokens are encrypted
|
* `--gate-public-key` is a public `secp256r1` 33-byte short key of a gate (use flags repeatedly for multiple gates). The tokens are encrypted
|
||||||
by a set of gateway keys, so you need to pass them as well.
|
by a set of gateway keys, so you need to pass them as well.
|
||||||
|
|
||||||
You can issue a secret using the parameters above only. The tool will
|
You can issue a secret using the parameters above only. The tool will
|
||||||
1. create a new container
|
1. create a new container
|
||||||
1. without a friendly name
|
1. without a friendly name
|
||||||
2. with ACL `0x3c8c8cce` - all operations are forbidden for `OTHERS` and `BEARER` user groups, except for `GET`
|
2. with ACL `0x3c8c8cce` -- all operations are forbidden for `OTHERS` and `BEARER` user groups, except for `GET`
|
||||||
3. with policy `REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
|
3. with policy `REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
|
||||||
2. put bearer and session tokens with default rules (details in [Bearer tokens](#Bearer tokens) and
|
2. put bearer and session tokens with default rules (details in [Bearer tokens](#Bearer tokens) and
|
||||||
[Session tokens](#Session tokens))
|
[Session tokens](#Session tokens))
|
||||||
|
@ -135,9 +135,9 @@ the secret. Format of `access_key_id`: `%cid0%oid`, where 0(zero) is a delimiter
|
||||||
|
|
||||||
### Bearer tokens
|
### Bearer tokens
|
||||||
|
|
||||||
Creation of the bearer tokens is mandatory.
|
Creation of bearer tokens is mandatory.
|
||||||
|
|
||||||
Rules for bearer token can be set via parameter `--bearer-rules` (json-string and file path allowed):
|
Rules for a bearer token can be set via parameter `--bearer-rules` (json-string and file path allowed):
|
||||||
```shell
|
```shell
|
||||||
$ neofs-authmate issue-secret --wallet wallet.json \
|
$ neofs-authmate issue-secret --wallet wallet.json \
|
||||||
--peer 192.168.130.71:8080 \
|
--peer 192.168.130.71:8080 \
|
||||||
|
@ -186,7 +186,7 @@ If bearer rules are not set, a token will be auto-generated with a value:
|
||||||
|
|
||||||
### Session tokens
|
### Session tokens
|
||||||
|
|
||||||
With session token, there are 3 options:
|
With a session token, there are 3 options:
|
||||||
1. append `--session-tokens` parameter with your custom rules in json format (as a string or file path). E.g.:
|
1. append `--session-tokens` parameter with your custom rules in json format (as a string or file path). E.g.:
|
||||||
```shell
|
```shell
|
||||||
$ neofs-authmate issue-secret --wallet wallet.json \
|
$ neofs-authmate issue-secret --wallet wallet.json \
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
### Credentials
|
### Credentials
|
||||||
|
|
||||||
To configure basic settings that the AWS CLI uses to interact with the Gateway, follow steps below:
|
To configure basic settings that the AWS CLI uses to interact with the Gateway, follow the steps below:
|
||||||
|
|
||||||
1. issue a secret with neofs-authmate tool (see [NeoFS Authmate] (#neofs-authmate))
|
1. issue a secret with neofs-authmate tool (see [NeoFS Authmate] (#neofs-authmate))
|
||||||
2. execute the command
|
2. execute the command
|
||||||
|
|
|
@ -30,7 +30,7 @@ Reference:
|
||||||
## ACL
|
## ACL
|
||||||
|
|
||||||
For now there are some limitations:
|
For now there are some limitations:
|
||||||
* [Bucket policy](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html) support only one `Principal` (type `AWS`) per `Statement`. To refer all users use `"AWS": "*"`
|
* [Bucket policy](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html) supports only one `Principal` (type `AWS`) per `Statement`. To refer all users use `"AWS": "*"`
|
||||||
* AWS conditions and wildcard are not supported in [resources](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-arn-format.html)
|
* AWS conditions and wildcard are not supported in [resources](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-arn-format.html)
|
||||||
* Only `CanonicalUser` (with hex encoded public key) and `All Users Group` are supported in [ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html)
|
* Only `CanonicalUser` (with hex encoded public key) and `All Users Group` are supported in [ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html)
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ a gateway spread requests equally among them (using weight 1 for every node):
|
||||||
$ neofs-s3-gw -p 192.168.130.72:8080 -p 192.168.130.71:8080
|
$ neofs-s3-gw -p 192.168.130.72:8080 -p 192.168.130.71:8080
|
||||||
```
|
```
|
||||||
If you want some specific load distribution proportions, use weights and priorities, they
|
If you want some specific load distribution proportions, use weights and priorities, they
|
||||||
can only be specified via environment variables or configuration file.
|
can only be specified via environment variables or a configuration file.
|
||||||
|
|
||||||
### Wallet
|
### Wallet
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ Gateway listens on `0.0.0.0:8080` by default, and you can change that with the `
|
||||||
|
|
||||||
It can also provide TLS interface for its users, just specify paths to the key and
|
It can also provide TLS interface for its users, just specify paths to the key and
|
||||||
certificate files via `--tls.key_file` and `--tls.cert_file` parameters. Note
|
certificate files via `--tls.key_file` and `--tls.cert_file` parameters. Note
|
||||||
that using these options makes gateway TLS-only, if you need to serve both TLS
|
that using these options makes gateway TLS-only. If you need to serve both TLS
|
||||||
and plain text you either have to run two gateway instances or use some
|
and plain text, you either have to run two gateway instances or use some
|
||||||
external redirecting solution.
|
external redirecting solution.
|
||||||
|
|
||||||
Example to bind to `192.168.130.130:443` and serve TLS there (keys and nodes are
|
Example to bind to `192.168.130.130:443` and serve TLS there (keys and nodes are
|
||||||
|
@ -100,7 +100,7 @@ default. To enable them, use `--pprof` and `--metrics` flags or
|
||||||
|
|
||||||
## YAML file and environment variables
|
## YAML file and environment variables
|
||||||
|
|
||||||
Example of YAML configuration file: [.yaml-example](/config/config.yaml)
|
Example of a YAML configuration file: [.yaml-example](/config/config.yaml)
|
||||||
Examples of environment variables: [.env-example](/config/config.env).
|
Examples of environment variables: [.env-example](/config/config.env).
|
||||||
|
|
||||||
A path to a configuration file can be specified with `--config` parameter:
|
A path to a configuration file can be specified with `--config` parameter:
|
||||||
|
@ -109,7 +109,7 @@ A path to a configuration file can be specified with `--config` parameter:
|
||||||
$ neofs-s3-gw --config your-config.yaml
|
$ neofs-s3-gw --config your-config.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Parameters of the following groups can be configured via `.yaml` file or environment variables only:
|
Parameters of the following groups can be configured via a `.yaml` file or environment variables only:
|
||||||
1. logging -- logging level
|
1. logging -- logging level
|
||||||
2. caching -- lifetime and size for each cache
|
2. caching -- lifetime and size for each cache
|
||||||
3. notifications
|
3. notifications
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# S3 compatibility test results
|
# S3 compatibility test results
|
||||||
|
|
||||||
To update this file using tests result run:
|
To update this file using tests result, run:
|
||||||
```sh
|
```sh
|
||||||
./updateTestsResult.sh ceph_tests_result.txt
|
./updateTestsResult.sh ceph_tests_result.txt
|
||||||
```
|
```
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetPassword gets passphrase for wallet.
|
// GetPassword gets the passphrase for a wallet.
|
||||||
func GetPassword(v *viper.Viper, variable string) *string {
|
func GetPassword(v *viper.Viper, variable string) *string {
|
||||||
var password *string
|
var password *string
|
||||||
if v.IsSet(variable) {
|
if v.IsSet(variable) {
|
||||||
|
@ -21,7 +21,7 @@ func GetPassword(v *viper.Viper, variable string) *string {
|
||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKeyFromPath reads wallet and gets private key.
|
// GetKeyFromPath reads a wallet and gets the private key.
|
||||||
func GetKeyFromPath(walletPath, addrStr string, password *string) (*keys.PrivateKey, error) {
|
func GetKeyFromPath(walletPath, addrStr string, password *string) (*keys.PrivateKey, error) {
|
||||||
if len(walletPath) == 0 {
|
if len(walletPath) == 0 {
|
||||||
return nil, fmt.Errorf("wallet path must not be empty")
|
return nil, fmt.Errorf("wallet path must not be empty")
|
||||||
|
|
Loading…
Reference in a new issue