forked from TrueCloudLab/frostfs-s3-gw
Compare commits
4 commits
master
...
test-27704
Author | SHA1 | Date | |
---|---|---|---|
f72d9ecf2a | |||
6c68e21777 | |||
a025f2e9c5 | |||
bd3164c57f |
34 changed files with 1353 additions and 792 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,4 +28,3 @@ debian/files
|
||||||
debian/*.log
|
debian/*.log
|
||||||
debian/*.substvars
|
debian/*.substvars
|
||||||
debian/frostfs-s3-gw/
|
debian/frostfs-s3-gw/
|
||||||
|
|
||||||
|
|
32
CHANGELOG.md
32
CHANGELOG.md
|
@ -24,7 +24,7 @@ This document outlines major changes between releases.
|
||||||
- Return error on invalid LocationConstraint (TrueCloudLab#23)
|
- Return error on invalid LocationConstraint (TrueCloudLab#23)
|
||||||
- Place billing metrics to separate url path (TrueCloudLab#26)
|
- Place billing metrics to separate url path (TrueCloudLab#26)
|
||||||
- Add generated deb builder files to .gitignore, and fix typo (TrueCloudLab#28)
|
- Add generated deb builder files to .gitignore, and fix typo (TrueCloudLab#28)
|
||||||
- Limit number of objects to delete at one time (TrueCloudLab#37)
|
- Limit number of objects to delete at one time (TrueCloudLab#37)
|
||||||
- CompleteMultipartUpload handler now sends whitespace characters to keep alive client's connection (#60)
|
- CompleteMultipartUpload handler now sends whitespace characters to keep alive client's connection (#60)
|
||||||
- Support new system attributes (#64)
|
- Support new system attributes (#64)
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ If you configure application using `.yaml` file change:
|
||||||
### Changed
|
### Changed
|
||||||
- GitHub actions update (#710)
|
- GitHub actions update (#710)
|
||||||
- Makefile help (#725)
|
- Makefile help (#725)
|
||||||
- Optimized object tags setting (#669)
|
- Optimized object tags setting (#669)
|
||||||
- Improved logging (#728)
|
- Improved logging (#728)
|
||||||
- Unified unit test names (#617)
|
- Unified unit test names (#617)
|
||||||
- Improved docs (#732)
|
- Improved docs (#732)
|
||||||
|
@ -156,7 +156,7 @@ If you configure application using environment variables change:
|
||||||
* `S3_GW_LISTEN_DOMAINS_N` -> `S3_GW_LISTEN_DOMAINS` (use it as array variable)
|
* `S3_GW_LISTEN_DOMAINS_N` -> `S3_GW_LISTEN_DOMAINS` (use it as array variable)
|
||||||
|
|
||||||
If you configure application using `.yaml` file change:
|
If you configure application using `.yaml` file change:
|
||||||
* `wallet` -> `wallet.path`
|
* `wallet` -> `wallet.path`
|
||||||
* `address` -> `wallet.address`
|
* `address` -> `wallet.address`
|
||||||
* `listen_domains.n` -> `listen_domains` (use it as array param)
|
* `listen_domains.n` -> `listen_domains` (use it as array param)
|
||||||
|
|
||||||
|
@ -179,8 +179,8 @@ If you configure application using `.yaml` file change:
|
||||||
- Rely on string sanitizing from zap (#498)
|
- Rely on string sanitizing from zap (#498)
|
||||||
|
|
||||||
### Updating from v0.22.0
|
### Updating from v0.22.0
|
||||||
1. To enable pprof use `pprof.enabled` instead of `pprof` in config.
|
1. To enable pprof use `pprof.enabled` instead of `pprof` in config.
|
||||||
To enable prometheus metrics use `prometheus.enabled` instead of `metrics` in config.
|
To enable prometheus metrics use `prometheus.enabled` instead of `metrics` in config.
|
||||||
If you are using the command line flags you can skip this step.
|
If you are using the command line flags you can skip this step.
|
||||||
|
|
||||||
## [0.22.0] - 2022-07-25
|
## [0.22.0] - 2022-07-25
|
||||||
|
@ -202,7 +202,7 @@ Tree service support
|
||||||
- Cache type cast error logging (#465)
|
- Cache type cast error logging (#465)
|
||||||
- `docker/*` target in Makefile (#471)
|
- `docker/*` target in Makefile (#471)
|
||||||
- Pre signed requests (#529)
|
- Pre signed requests (#529)
|
||||||
- Tagging and ACL notifications (#361)
|
- Tagging and ACL notifications (#361)
|
||||||
- AWSv4 signer package to improve compatibility with S3 clients (#528)
|
- AWSv4 signer package to improve compatibility with S3 clients (#528)
|
||||||
- Extension mimetype detector (#289)
|
- Extension mimetype detector (#289)
|
||||||
- Default params documentation (#592)
|
- Default params documentation (#592)
|
||||||
|
@ -236,7 +236,7 @@ Tree service support
|
||||||
- Obtainment of ETag value (#431)
|
- Obtainment of ETag value (#431)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Authmate doesn't parse session context anymore, now it accepts application defined
|
- Authmate doesn't parse session context anymore, now it accepts application defined
|
||||||
flexible structure with container ID in human-readable format (#428)
|
flexible structure with container ID in human-readable format (#428)
|
||||||
|
|
||||||
## [0.20.0] - 2022-04-29
|
## [0.20.0] - 2022-04-29
|
||||||
|
@ -246,19 +246,19 @@ Tree service support
|
||||||
- Support of basic notifications (#357, #358, #359)
|
- Support of basic notifications (#357, #358, #359)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Logger behavior: now it writes to stderr instead of stdout, app name and
|
- Logger behavior: now it writes to stderr instead of stdout, app name and
|
||||||
version are always presented and fixed, all user options except of `level` are
|
version are always presented and fixed, all user options except of `level` are
|
||||||
dropped (#380)
|
dropped (#380)
|
||||||
- Improved docs, added config examples (#396, #398)
|
- Improved docs, added config examples (#396, #398)
|
||||||
- Updated NeoFS SDK (#365, #409)
|
- Updated NeoFS SDK (#365, #409)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Added check of `SetEACL` tokens before processing of requests (#347)
|
- Added check of `SetEACL` tokens before processing of requests (#347)
|
||||||
- Authmate: returned lost session tokens when a parameter `--session-token` is
|
- Authmate: returned lost session tokens when a parameter `--session-token` is
|
||||||
omitted (#387)
|
omitted (#387)
|
||||||
- Error when a bucket hasn't a settings file (#389)
|
- Error when a bucket hasn't a settings file (#389)
|
||||||
- Response to a request to delete not existing object (#392)
|
- Response to a request to delete not existing object (#392)
|
||||||
- Replaced gate key in ACL Grantee by key of bearer token issuer (#395)
|
- Replaced gate key in ACL Grantee by key of bearer token issuer (#395)
|
||||||
- Missing attach of bearer token to requests to put system object (#399)
|
- Missing attach of bearer token to requests to put system object (#399)
|
||||||
- Deletion of system object while CompleteMultipartUpload (#400)
|
- Deletion of system object while CompleteMultipartUpload (#400)
|
||||||
- Improved English in docs and comments (#405)
|
- Improved English in docs and comments (#405)
|
||||||
|
@ -285,7 +285,7 @@ Tree service support
|
||||||
- Updated NeoFS SDK to v1.0.0-rc.3 (#297, #333, #346, #376)
|
- Updated NeoFS SDK to v1.0.0-rc.3 (#297, #333, #346, #376)
|
||||||
- Authmate: changed session token rules handling (#329, #336, #338, #352)
|
- Authmate: changed session token rules handling (#329, #336, #338, #352)
|
||||||
- Changed status code for some failed requests (#308)
|
- Changed status code for some failed requests (#308)
|
||||||
- GetBucketLocation returns policy name used at bucket creation (#301)
|
- GetBucketLocation returns policy name used at bucket creation (#301)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Waiting for bucket to be deleted (#366)
|
- Waiting for bucket to be deleted (#366)
|
||||||
|
@ -305,7 +305,7 @@ Tree service support
|
||||||
## [0.18.0] - 2021-12-16
|
## [0.18.0] - 2021-12-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Support for MultipartUpload (#186, #187)
|
- Support for MultipartUpload (#186, #187)
|
||||||
- CORS support (#217)
|
- CORS support (#217)
|
||||||
- Authmate supports setting of tokens lifetime in a more convenient format (duration) (#258)
|
- Authmate supports setting of tokens lifetime in a more convenient format (duration) (#258)
|
||||||
- Generation of a random key for `--no-sign-request` (#276)
|
- Generation of a random key for `--no-sign-request` (#276)
|
||||||
|
@ -334,10 +334,10 @@ With this release we introduce [ceph-based](https://github.com/ceph/s3-tests) S3
|
||||||
* POST uploading support (#190)
|
* POST uploading support (#190)
|
||||||
* Delete marker support (#248)
|
* Delete marker support (#248)
|
||||||
* Expiration for access box (#255)
|
* Expiration for access box (#255)
|
||||||
* AWS CLI credential generating by authmate (#241)
|
* AWS CLI credential generating by authmate (#241)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Default placement policy is now configurable (#218)
|
* Default placement policy is now configurable (#218)
|
||||||
* README is split into different files (#210)
|
* README is split into different files (#210)
|
||||||
* Unified error handling (#89, #149, #184)
|
* Unified error handling (#89, #149, #184)
|
||||||
* Authmate issue-secret response contains container id (#163)
|
* Authmate issue-secret response contains container id (#163)
|
||||||
|
@ -382,7 +382,7 @@ cryptography with NEP-6 wallet support.
|
||||||
* Support of time-based conditional CopyObject and GetObject (#94)
|
* Support of time-based conditional CopyObject and GetObject (#94)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Accesskey format: now `0` used as a delimiter between container ID and object
|
* Accesskey format: now `0` used as a delimiter between container ID and object
|
||||||
ID instead of `_` (#164)
|
ID instead of `_` (#164)
|
||||||
* Accessbox is encoded in protobuf format (#48)
|
* Accessbox is encoded in protobuf format (#48)
|
||||||
* Authentication uses secp256r1 instead of ed25519 (#75)
|
* Authentication uses secp256r1 instead of ed25519 (#75)
|
||||||
|
|
|
@ -16,4 +16,4 @@ In chronological order:
|
||||||
- Elizaveta Chichindaeva
|
- Elizaveta Chichindaeva
|
||||||
- Stanislav Bogatyrev
|
- Stanislav Bogatyrev
|
||||||
- Anastasia Prasolova
|
- Anastasia Prasolova
|
||||||
- Leonard Liubich
|
- Leonard Liubich
|
||||||
|
|
|
@ -66,7 +66,7 @@ $ S3_GW_PEERS_0_ADDRESS=grpcs://192.168.130.72:8080 \
|
||||||
|
|
||||||
## Domains
|
## Domains
|
||||||
|
|
||||||
By default, s3-gw enable only `path-style access`.
|
By default, s3-gw enable only `path-style access`.
|
||||||
To be able to use both: `virtual-hosted-style` and `path-style` access you must configure `listen_domains`:
|
To be able to use both: `virtual-hosted-style` and `path-style` access you must configure `listen_domains`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -100,6 +100,6 @@ Also, you can configure domains using `.env` variables or `yaml` file.
|
||||||
- [AWS S3 API compatibility](./docs/aws_s3_compat.md)
|
- [AWS S3 API compatibility](./docs/aws_s3_compat.md)
|
||||||
- [AWS S3 Compatibility test results](./docs/s3_test_results.md)
|
- [AWS S3 Compatibility test results](./docs/s3_test_results.md)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Please see [CREDITS](CREDITS.md) for details.
|
Please see [CREDITS](CREDITS.md) for details.
|
||||||
|
|
|
@ -1368,7 +1368,7 @@ func TestBucketPolicyUnmarshal(t *testing.T) {
|
||||||
},
|
},
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Action": [
|
"Action": [
|
||||||
"s3:GetObject",
|
"s3:GetObject",
|
||||||
"s3:GetObjectVersion"
|
"s3:GetObjectVersion"
|
||||||
],
|
],
|
||||||
"Resource": [
|
"Resource": [
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/resolver"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/resolver"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
@ -88,7 +89,7 @@ func prepareHandlerContext(t *testing.T) *handlerContext {
|
||||||
Caches: layer.DefaultCachesConfigs(zap.NewExample()),
|
Caches: layer.DefaultCachesConfigs(zap.NewExample()),
|
||||||
AnonKey: layer.AnonymousKey{Key: key},
|
AnonKey: layer.AnonymousKey{Key: key},
|
||||||
Resolver: testResolver,
|
Resolver: testResolver,
|
||||||
TreeService: layer.NewTreeService(),
|
TreeService: NewTreeServiceMock(t),
|
||||||
}
|
}
|
||||||
|
|
||||||
var pp netmap.PlacementPolicy
|
var pp netmap.PlacementPolicy
|
||||||
|
@ -113,6 +114,12 @@ func prepareHandlerContext(t *testing.T) *handlerContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewTreeServiceMock(t *testing.T) *tree.Tree {
|
||||||
|
memCli, err := tree.NewTreeServiceClientMemory()
|
||||||
|
require.NoError(t, err)
|
||||||
|
return tree.NewTree(memCli)
|
||||||
|
}
|
||||||
|
|
||||||
func createTestBucket(hc *handlerContext, bktName string) *data.BucketInfo {
|
func createTestBucket(hc *handlerContext, bktName string) *data.BucketInfo {
|
||||||
_, err := hc.MockedPool().CreateContainer(hc.Context(), layer.PrmContainerCreate{
|
_, err := hc.MockedPool().CreateContainer(hc.Context(), layer.PrmContainerCreate{
|
||||||
Creator: hc.owner,
|
Creator: hc.owner,
|
||||||
|
|
|
@ -753,12 +753,3 @@ func periodicXMLWriter(w io.Writer, dur time.Duration) (stop func() bool) {
|
||||||
|
|
||||||
return stop
|
return stop
|
||||||
}
|
}
|
||||||
|
|
||||||
// periodicWriterErrorSender returns handler function to send error. If header is
|
|
||||||
// alreay written by periodic XML writer, do not send HTTP and XML headers.
|
|
||||||
func (h *handler) periodicWriterErrorSender(headerWritten bool) func(http.ResponseWriter, string, *api.ReqInfo, error, ...zap.Field) {
|
|
||||||
if headerWritten {
|
|
||||||
return h.logAndSendErrorNoHeader
|
|
||||||
}
|
|
||||||
return h.logAndSendError
|
|
||||||
}
|
|
||||||
|
|
|
@ -245,7 +245,7 @@ func issueSecret() *cli.Command {
|
||||||
},
|
},
|
||||||
&cli.DurationFlag{
|
&cli.DurationFlag{
|
||||||
Name: "lifetime",
|
Name: "lifetime",
|
||||||
Usage: `Lifetime of tokens. For example 50h30m (note: max time unit is an hour so to set a day you should use 24h).
|
Usage: `Lifetime of tokens. For example 50h30m (note: max time unit is an hour so to set a day you should use 24h).
|
||||||
It will be ceil rounded to the nearest amount of epoch.`,
|
It will be ceil rounded to the nearest amount of epoch.`,
|
||||||
Required: false,
|
Required: false,
|
||||||
Destination: &lifetimeFlag,
|
Destination: &lifetimeFlag,
|
||||||
|
@ -394,7 +394,7 @@ Note to override credentials you must provide both access key and secret key.`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.DurationFlag{
|
&cli.DurationFlag{
|
||||||
Name: "lifetime",
|
Name: "lifetime",
|
||||||
Usage: `Lifetime of presigned URL. For example 50h30m (note: max time unit is an hour so to set a day you should use 24h).
|
Usage: `Lifetime of presigned URL. For example 50h30m (note: max time unit is an hour so to set a day you should use 24h).
|
||||||
It will be ceil rounded to the nearest amount of epoch.`,
|
It will be ceil rounded to the nearest amount of epoch.`,
|
||||||
Required: false,
|
Required: false,
|
||||||
Destination: &lifetimeFlag,
|
Destination: &lifetimeFlag,
|
||||||
|
|
|
@ -25,12 +25,15 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/xml"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/xml"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/metrics"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/metrics"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -110,10 +113,12 @@ func (a *App) initLayer(ctx context.Context) {
|
||||||
a.initResolver()
|
a.initResolver()
|
||||||
|
|
||||||
treeServiceEndpoint := a.cfg.GetString(cfgTreeServiceEndpoint)
|
treeServiceEndpoint := a.cfg.GetString(cfgTreeServiceEndpoint)
|
||||||
treeService, err := frostfs.NewTreeClient(ctx, treeServiceEndpoint, a.key)
|
grpcDialOpt := grpc.WithTransportCredentials(insecure.NewCredentials())
|
||||||
|
treeGRPCClient, err := tree.NewTreeServiceClientGRPC(ctx, treeServiceEndpoint, a.key, grpcDialOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.log.Fatal("failed to create tree service", zap.Error(err))
|
a.log.Fatal("failed to create tree service", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
treeService := tree.NewTree(treeGRPCClient)
|
||||||
a.log.Info("init tree service", zap.String("endpoint", treeServiceEndpoint))
|
a.log.Info("init tree service", zap.String("endpoint", treeServiceEndpoint))
|
||||||
|
|
||||||
// prepare random key for anonymous requests
|
// prepare random key for anonymous requests
|
||||||
|
|
|
@ -27,4 +27,3 @@ message Tokens {
|
||||||
bytes bearerToken = 2 [json_name = "bearerToken"];
|
bytes bearerToken = 2 [json_name = "bearerToken"];
|
||||||
repeated bytes sessionTokens = 3 [json_name = "sessionTokens"];
|
repeated bytes sessionTokens = 3 [json_name = "sessionTokens"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
debian/copyright
vendored
6
debian/copyright
vendored
|
@ -14,11 +14,11 @@ License: AGPL-3.0-only
|
||||||
This program is free software: you can redistribute it and/or modify it
|
This program is free software: you can redistribute it and/or modify it
|
||||||
under the terms of the GNU Affero General Public License as published
|
under the terms of the GNU Affero General Public License as published
|
||||||
by the Free Software Foundation; version 3.
|
by the Free Software Foundation; version 3.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
General Public License for more details.
|
General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
2
debian/frostfs-s3-gw.install
vendored
2
debian/frostfs-s3-gw.install
vendored
|
@ -1,4 +1,4 @@
|
||||||
config/config.yaml etc/frostfs/s3
|
config/config.yml etc/frostfs/s3
|
||||||
config/rules.json var/lib/frostfs/s3
|
config/rules.json var/lib/frostfs/s3
|
||||||
bin/frostfs-s3-gw usr/bin
|
bin/frostfs-s3-gw usr/bin
|
||||||
bin/frostfs-s3-authmate usr/bin
|
bin/frostfs-s3-authmate usr/bin
|
||||||
|
|
10
debian/frostfs-s3-gw.postinst
vendored
Normal file → Executable file
10
debian/frostfs-s3-gw.postinst
vendored
Normal file → Executable file
|
@ -24,14 +24,14 @@ case "$1" in
|
||||||
id -u frostfs-$USERNAME >/dev/null 2>&1 || useradd -s /usr/sbin/nologin -d /var/lib/frostfs/s3 --system -M -U -c "FrostFS S3 gateway" frostfs-$USERNAME
|
id -u frostfs-$USERNAME >/dev/null 2>&1 || useradd -s /usr/sbin/nologin -d /var/lib/frostfs/s3 --system -M -U -c "FrostFS S3 gateway" frostfs-$USERNAME
|
||||||
if ! dpkg-statoverride --list /etc/frostfs/$USERNAME >/dev/null; then
|
if ! dpkg-statoverride --list /etc/frostfs/$USERNAME >/dev/null; then
|
||||||
chown -f -R root:frostfs-$USERNAME /etc/frostfs/$USERNAME
|
chown -f -R root:frostfs-$USERNAME /etc/frostfs/$USERNAME
|
||||||
chown -f root:frostfs-$USERNAME /etc/frostfs/$USERNAME/config.yaml || true
|
chown -f root:frostfs-$USERNAME /etc/frostfs/$USERNAME/config.yml || true
|
||||||
chmod -f 0750 /etc/frostfs/$USERNAME
|
chmod -f 0750 /etc/frostfs/$USERNAME
|
||||||
chmod -f 0640 /etc/frostfs/$USERNAME/config.yaml || true
|
chmod -f 0640 /etc/frostfs/$USERNAME/config.yml || true
|
||||||
fi
|
fi
|
||||||
USERDIR=$(getent passwd "frostfs-$USERNAME" | cut -d: -f6)
|
USERDIR=$(getent passwd "frostfs-$USERNAME" | cut -d: -f6)
|
||||||
if ! dpkg-statoverride --list frostfs-$USERDIR >/dev/null; then
|
if ! dpkg-statoverride --list frostfs-"$USERDIR" >/dev/null; then
|
||||||
chown -f frostfs-$USERNAME: $USERDIR
|
chown -f frostfs-$USERNAME: "$USERDIR"
|
||||||
chown -f frostfs-$USERNAME: $USERDIR/rules.json
|
chown -f frostfs-$USERNAME: "$USERDIR"/rules.json
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
|
0
debian/frostfs-s3-gw.postrm
vendored
Normal file → Executable file
0
debian/frostfs-s3-gw.postrm
vendored
Normal file → Executable file
0
debian/frostfs-s3-gw.preinst
vendored
Normal file → Executable file
0
debian/frostfs-s3-gw.preinst
vendored
Normal file → Executable file
0
debian/frostfs-s3-gw.prerm
vendored
Normal file → Executable file
0
debian/frostfs-s3-gw.prerm
vendored
Normal file → Executable file
2
debian/frostfs-s3-gw.service
vendored
2
debian/frostfs-s3-gw.service
vendored
|
@ -4,7 +4,7 @@ Requires=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/bin/frostfs-s3-gw --config /etc/frostfs/s3/config.yaml
|
ExecStart=/usr/bin/frostfs-s3-gw --config /etc/frostfs/s3/config.yml
|
||||||
User=frostfs-s3
|
User=frostfs-s3
|
||||||
Group=frostfs-s3
|
Group=frostfs-s3
|
||||||
WorkingDirectory=/var/lib/frostfs/s3
|
WorkingDirectory=/var/lib/frostfs/s3
|
||||||
|
|
6
debian/rules
vendored
6
debian/rules
vendored
|
@ -8,9 +8,7 @@ SERVICE = frostfs-s3-gw
|
||||||
dh $@
|
dh $@
|
||||||
|
|
||||||
override_dh_installsystemd:
|
override_dh_installsystemd:
|
||||||
dh_installsystemd --no-enable --no-start $(SERVICE).service
|
dh_installsystemd --no-enable --no-start $(SERVICE).service
|
||||||
|
|
||||||
override_dh_installchangelogs:
|
override_dh_installchangelogs:
|
||||||
dh_installchangelogs -k CHANGELOG.md
|
dh_installchangelogs -k CHANGELOG.md
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,8 @@ To generate a wallet for a gateway, run the following command:
|
||||||
$ ./neo-go wallet init -a -w wallet.json
|
$ ./neo-go wallet init -a -w wallet.json
|
||||||
|
|
||||||
Enter the name of the account > AccountTestName
|
Enter the name of the account > AccountTestName
|
||||||
Enter passphrase >
|
Enter passphrase >
|
||||||
Confirm passphrase >
|
Confirm passphrase >
|
||||||
|
|
||||||
{
|
{
|
||||||
"version": "3.0",
|
"version": "3.0",
|
||||||
|
@ -89,18 +89,18 @@ put them as an object into a container on the FrostFS network.
|
||||||
|
|
||||||
**Required parameters:**
|
**Required parameters:**
|
||||||
* `--wallet` is 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` is an address of a FrostFS peer to connect to
|
* `--peer` is an address of a FrostFS peer to connect to
|
||||||
* `--gate-public-key` is a 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))
|
||||||
|
|
||||||
E.g.:
|
E.g.:
|
||||||
|
@ -109,9 +109,9 @@ $ frostfs-s3-authmate issue-secret --wallet wallet.json \
|
||||||
--peer 192.168.130.71:8080 \
|
--peer 192.168.130.71:8080 \
|
||||||
--gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf\
|
--gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf\
|
||||||
--gate-public-key 0317585fa8274f7afdf1fc5f2a2e7bece549d5175c4e5182e37924f30229aef967
|
--gate-public-key 0317585fa8274f7afdf1fc5f2a2e7bece549d5175c4e5182e37924f30229aef967
|
||||||
|
|
||||||
Enter password for wallet.json >
|
Enter password for wallet.json >
|
||||||
|
|
||||||
{
|
{
|
||||||
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT0AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
|
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT0AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
|
||||||
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c",
|
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c",
|
||||||
|
@ -130,9 +130,9 @@ the secret. Format of `access_key_id`: `%cid0%oid`, where 0(zero) is a delimiter
|
||||||
* `--container-friendly-name` -- name of a container with tokens, by default container will not have a friendly name
|
* `--container-friendly-name` -- name of a container with tokens, by default container will not have a friendly name
|
||||||
* `--container-placement-policy` - placement policy of auth container to put the secret into. Default value is
|
* `--container-placement-policy` - placement policy of auth container to put the secret into. Default value is
|
||||||
`REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
|
`REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
|
||||||
* `--lifetime`-- lifetime of tokens. For example 50h30m (note: max time unit is an hour so to set a day you should use
|
* `--lifetime`-- lifetime of tokens. For example 50h30m (note: max time unit is an hour so to set a day you should use
|
||||||
24h). Default value is `720h` (30 days). It will be ceil rounded to the nearest amount of epoch
|
24h). Default value is `720h` (30 days). It will be ceil rounded to the nearest amount of epoch
|
||||||
* `--aws-cli-credentials` - path to the aws cli credentials file, where authmate will write `access_key_id` and
|
* `--aws-cli-credentials` - path to the aws cli credentials file, where authmate will write `access_key_id` and
|
||||||
`secret_access_key` to
|
`secret_access_key` to
|
||||||
|
|
||||||
### Bearer tokens
|
### Bearer tokens
|
||||||
|
@ -161,7 +161,7 @@ where content of `bearer-rules.json`:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** such rules allow all operations for all users (the same behavior when records are empty).
|
**Note:** such rules allow all operations for all users (the same behavior when records are empty).
|
||||||
To restrict access you MUST provide records with `DENY` action. That's why we recommend always place such records
|
To restrict access you MUST provide records with `DENY` action. That's why we recommend always place such records
|
||||||
at the end of records (see default rules below) to prevent undesirable access violation.
|
at the end of records (see default rules below) to prevent undesirable access violation.
|
||||||
Since the rules are applied from top to bottom, they do not override what was previously allowed.
|
Since the rules are applied from top to bottom, they do not override what was previously allowed.
|
||||||
|
@ -192,7 +192,7 @@ If bearer rules are not set, a token will be auto-generated with a value:
|
||||||
|
|
||||||
### Session tokens
|
### Session tokens
|
||||||
|
|
||||||
With a 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
|
||||||
$ frostfs-s3-authmate issue-secret --wallet wallet.json \
|
$ frostfs-s3-authmate issue-secret --wallet wallet.json \
|
||||||
|
@ -224,8 +224,8 @@ If `containerID` is `null` or omitted, then session token rule will be applied
|
||||||
to all containers. Otherwise, specify `containerID` value in human-redabale
|
to all containers. Otherwise, specify `containerID` value in human-redabale
|
||||||
format (base58 encoded string).
|
format (base58 encoded string).
|
||||||
|
|
||||||
> **_NB!_** To create buckets in FrostFS it's necessary to have session tokens with `PUT` and `SETEACL` permissions, that's why
|
> **_NB!_** To create buckets in FrostFS it's necessary to have session tokens with `PUT` and `SETEACL` permissions, that's why
|
||||||
the authmate creates a `SETEACL` session token automatically in case when a user specified the token rule with `PUT` and
|
the authmate creates a `SETEACL` session token automatically in case when a user specified the token rule with `PUT` and
|
||||||
forgot about the rule with `SETEACL`.
|
forgot about the rule with `SETEACL`.
|
||||||
|
|
||||||
2. append `--session-tokens` parameter with the value `none` -- no session token will be created
|
2. append `--session-tokens` parameter with the value `none` -- no session token will be created
|
||||||
|
@ -234,7 +234,7 @@ in example above)
|
||||||
|
|
||||||
### Containers policy
|
### Containers policy
|
||||||
|
|
||||||
Rules for mapping of `LocationConstraint` ([aws spec](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html#API_CreateBucket_RequestBody))
|
Rules for mapping of `LocationConstraint` ([aws spec](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html#API_CreateBucket_RequestBody))
|
||||||
to `PlacementPolicy`
|
to `PlacementPolicy`
|
||||||
can be set via parameter `--container-policy` (json-string and file path allowed):
|
can be set via parameter `--container-policy` (json-string and file path allowed):
|
||||||
```json
|
```json
|
||||||
|
@ -248,7 +248,7 @@ can be set via parameter `--container-policy` (json-string and file path allowed
|
||||||
## Obtainment of a secret access key
|
## Obtainment of a secret access key
|
||||||
|
|
||||||
You can get a secret access key associated with an access key ID by obtaining a
|
You can get a secret access key associated with an access key ID by obtaining a
|
||||||
secret stored on the FrostFS network. Here is an example of providing one password (for `wallet.json`) via env variable
|
secret stored on the FrostFS network. Here is an example of providing one password (for `wallet.json`) via env variable
|
||||||
and the other (for `gate-wallet.json`) interactively:
|
and the other (for `gate-wallet.json`) interactively:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -267,14 +267,14 @@ Enter password for gate-wallet.json >
|
||||||
|
|
||||||
## Generate presigned URL
|
## Generate presigned URL
|
||||||
|
|
||||||
You can generate [presigned url](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)
|
You can generate [presigned url](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)
|
||||||
using AWS credentials from `~/.aws/credentials` (you can specify profile using the `--profile` flag)
|
using AWS credentials from `~/.aws/credentials` (you can specify profile using the `--profile` flag)
|
||||||
with the following command:
|
with the following command:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ frostfs-s3-authmate generate-presigned-url --endpoint http://localhost:8084 \
|
$ frostfs-s3-authmate generate-presigned-url --endpoint http://localhost:8084 \
|
||||||
--method get --bucket presigned --object obj --lifetime 30s
|
--method get --bucket presigned --object obj --lifetime 30s
|
||||||
|
|
||||||
{
|
{
|
||||||
"URL": "http://localhost:8084/presigned/obj?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=6UpmiuYspPLMWfyhEKYmZQSsTGkFLS5MhQVdsda3fhz908Hw9eo9urTmaJtfvHMHUpY8SWAptk61bns2Js8f1M5tZ%2F20220615%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20220615T084203Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=47f74d4b84566708a17dded05cce732690745f141235215104ad051e265e3c59"
|
"URL": "http://localhost:8084/presigned/obj?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=6UpmiuYspPLMWfyhEKYmZQSsTGkFLS5MhQVdsda3fhz908Hw9eo9urTmaJtfvHMHUpY8SWAptk61bns2Js8f1M5tZ%2F20220615%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20220615T084203Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=47f74d4b84566708a17dded05cce732690745f141235215104ad051e265e3c59"
|
||||||
}
|
}
|
||||||
|
@ -297,7 +297,7 @@ $ frostfs-s3-authmate generate-presigned-url --endpoint http://localhost:8084 \
|
||||||
You can also can get the presigned URL (only for GET) using aws cli v2:
|
You can also can get the presigned URL (only for GET) using aws cli v2:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ aws s3 --endpoint http://localhost:8084 presign s3://pregigned/obj
|
$ aws s3 --endpoint http://localhost:8084 presign s3://pregigned/obj
|
||||||
|
|
||||||
http://localhost:8084/pregigned/obj?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=6UpmiuYspPLMWfyhEKYmZQSsTGkFLS5MhQVdsda3fhz908Hw9eo9urTmaJtfvHMHUpY8SWAptk61bns2Js8f1M5tZ%2F20220615%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20220615T072348Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=b82c13952534b1bba699a718f2d42d135c2833a1e64030d4ce0e198af46551d4
|
http://localhost:8084/pregigned/obj?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=6UpmiuYspPLMWfyhEKYmZQSsTGkFLS5MhQVdsda3fhz908Hw9eo9urTmaJtfvHMHUpY8SWAptk61bns2Js8f1M5tZ%2F20220615%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20220615T072348Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=b82c13952534b1bba699a718f2d42d135c2833a1e64030d4ce0e198af46551d4
|
||||||
```
|
```
|
||||||
|
|
|
@ -16,8 +16,8 @@ after you enter this command, the AWS CLI will prompt you for four pieces of inf
|
||||||
```
|
```
|
||||||
AWS Access Key ID [None]: 5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT0AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM
|
AWS Access Key ID [None]: 5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT0AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM
|
||||||
AWS Secret Access Key [None]: 438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c
|
AWS Secret Access Key [None]: 438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c
|
||||||
Default region name [None]: ru
|
Default region name [None]: ru
|
||||||
Default output format [none]: json
|
Default output format [none]: json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
@ -26,11 +26,11 @@ Default output format [none]: json
|
||||||
|
|
||||||
### Bucket
|
### Bucket
|
||||||
|
|
||||||
#### Obtainment of a list of buckets
|
#### Obtainment of a list of buckets
|
||||||
|
|
||||||
To view the list of the buckets in the FrostFS node, to which the gateway is connected, enter the following command:
|
To view the list of the buckets in the FrostFS node, to which the gateway is connected, enter the following command:
|
||||||
```
|
```
|
||||||
$ aws s3 ls
|
$ aws s3 ls
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Creation of a bucket
|
#### Creation of a bucket
|
||||||
|
@ -41,7 +41,7 @@ To create a bucket, run the following command:
|
||||||
```
|
```
|
||||||
$ aws s3api create-bucket --bucket %BUCKET_NAME --acl %ACL
|
$ aws s3api create-bucket --bucket %BUCKET_NAME --acl %ACL
|
||||||
```
|
```
|
||||||
where `%ACL` can be represented by a hex encoded value or by keywords `public-read-write`, `private`, `public-read`.
|
where `%ACL` can be represented by a hex encoded value or by keywords `public-read-write`, `private`, `public-read`.
|
||||||
If the parameter is not set, the default value is `private`.
|
If the parameter is not set, the default value is `private`.
|
||||||
|
|
||||||
> **_NOTE:_** Bucket creation uses async-poll approach. `BucketAlreadyOwnedByYou`
|
> **_NOTE:_** Bucket creation uses async-poll approach. `BucketAlreadyOwnedByYou`
|
||||||
|
@ -54,7 +54,7 @@ If the parameter is not set, the default value is `private`.
|
||||||
> (previous success will result in the status mentioned above). It is worth clarifying
|
> (previous success will result in the status mentioned above). It is worth clarifying
|
||||||
> that the final success of the bucket creation is not guaranteed after a timeout error.
|
> that the final success of the bucket creation is not guaranteed after a timeout error.
|
||||||
|
|
||||||
#### Deletion of a bucket
|
#### Deletion of a bucket
|
||||||
|
|
||||||
To delete a bucket, execute the following command:
|
To delete a bucket, execute the following command:
|
||||||
```
|
```
|
||||||
|
@ -67,7 +67,7 @@ $ aws s3api delete-bucket --bucket %BUCKET_NAME
|
||||||
|
|
||||||
To view the list of the objects in a bucket, run:
|
To view the list of the objects in a bucket, run:
|
||||||
```
|
```
|
||||||
$ aws s3api list-objects --bucket %BUCKET_NAME
|
$ aws s3api list-objects --bucket %BUCKET_NAME
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Upload of a file
|
#### Upload of a file
|
||||||
|
@ -83,7 +83,7 @@ where %OBJECT_KEY is the filepath of an object in FrostFS
|
||||||
To upload a dir into a bucket in the FrostFS network, run the following command:
|
To upload a dir into a bucket in the FrostFS network, run the following command:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ aws s3 sync %DIRPATH s3://%BUCKET_NAME
|
$ aws s3 sync %DIRPATH s3://%BUCKET_NAME
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Download of a file
|
#### Download of a file
|
||||||
|
|
|
@ -31,7 +31,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) supports only one `Principal` per `Statement`.
|
* [Bucket policy](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html) supports only one `Principal` per `Statement`.
|
||||||
Principal must be `"AWS": "*"` (to refer all users) or `"CanonicalUser": "0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf"` (hex encoded public key of desired user).
|
Principal must be `"AWS": "*"` (to refer all users) or `"CanonicalUser": "0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf"` (hex encoded public key of desired user).
|
||||||
* Resource in bucket policy is an array. Each item MUST contain bucket name, CAN contain object name (wildcards are not supported):
|
* Resource in bucket policy is an array. Each item MUST contain bucket name, CAN contain object name (wildcards are not supported):
|
||||||
```json
|
```json
|
||||||
|
@ -160,7 +160,7 @@ See also `GetObject` and other method parameters.
|
||||||
| 🔵 | GetBucketInventoryConfiguration | |
|
| 🔵 | GetBucketInventoryConfiguration | |
|
||||||
| 🔵 | ListBucketInventoryConfigurations | |
|
| 🔵 | ListBucketInventoryConfigurations | |
|
||||||
| 🔵 | PutBucketInventoryConfiguration | |
|
| 🔵 | PutBucketInventoryConfiguration | |
|
||||||
|
|
||||||
## Lifecycle
|
## Lifecycle
|
||||||
|
|
||||||
| | Method | Comments |
|
| | Method | Comments |
|
||||||
|
|
|
@ -62,7 +62,7 @@ $ frostfs-s3-gw --listen_address 192.168.130.130:443 \
|
||||||
--tls.key_file=key.pem --tls.cert_file=cert.pem
|
--tls.key_file=key.pem --tls.cert_file=cert.pem
|
||||||
```
|
```
|
||||||
|
|
||||||
Using these flag you can configure only one address. To set multiple addresses use yaml config.
|
Using these flag you can configure only one address. To set multiple addresses use yaml config.
|
||||||
|
|
||||||
### RPC endpoint and resolving of bucket names
|
### RPC endpoint and resolving of bucket names
|
||||||
|
|
||||||
|
@ -101,13 +101,13 @@ Pprof and Prometheus are integrated into the gateway. To enable them, use `--ppr
|
||||||
|
|
||||||
## YAML file and environment variables
|
## YAML file and environment variables
|
||||||
|
|
||||||
Example of a YAML configuration file: [yaml-example](/config/config.yaml)
|
Example of a YAML configuration file: [yaml-example](/config/config.yml)
|
||||||
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:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ frostfs-s3-gw --config your-config.yaml
|
$ frostfs-s3-gw --config your-config.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multiple configs
|
### Multiple configs
|
||||||
|
@ -118,18 +118,18 @@ You can either provide several files with repeating `--config` flag or provide p
|
||||||
Also, you can combine these flags:
|
Also, you can combine these flags:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ frostfs-s3-gw --config ./config/config.yaml --config /your/partial/config.yaml --config-dir ./config/dir
|
$ frostfs-s3-gw --config ./config/config.yml --config /your/partial/config.yml --config-dir ./config/dir
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** next file in `--config` flag overwrites values from the previous one.
|
**Note:** next file in `--config` flag overwrites values from the previous one.
|
||||||
Files from `--config-dir` directory overwrite values from `--config` files.
|
Files from `--config-dir` directory overwrite values from `--config` files.
|
||||||
So the command above run `frostfs-s3-gw` to listen on `0.0.0.0:8080` address (value from `./config/config.yaml`),
|
So the command above run `frostfs-s3-gw` to listen on `0.0.0.0:8080` address (value from `./config/config.yml`),
|
||||||
applies parameters from `/your/partial/config.yaml`,
|
applies parameters from `/your/partial/config.yml`,
|
||||||
enable pprof (value from `./config/dir/pprof.yaml`) and prometheus (value from `./config/dir/prometheus.yaml`).
|
enable pprof (value from `./config/dir/pprof.yaml`) and prometheus (value from `./config/dir/prometheus.yaml`).
|
||||||
|
|
||||||
### Reload on SIGHUP
|
### Reload on SIGHUP
|
||||||
|
|
||||||
Some config values can be reloaded on SIGHUP signal.
|
Some config values can be reloaded on SIGHUP signal.
|
||||||
Such parameters have special mark in tables below.
|
Such parameters have special mark in tables below.
|
||||||
|
|
||||||
You can send SIGHUP signal to app using the following command:
|
You can send SIGHUP signal to app using the following command:
|
||||||
|
@ -141,7 +141,7 @@ $ kill -s SIGHUP <app_pid>
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ./bin/frostfs-s3-gw --config config.yaml &> s3.log &
|
$ ./bin/frostfs-s3-gw --config config.yml &> s3.log &
|
||||||
[1] 998346
|
[1] 998346
|
||||||
|
|
||||||
$ cat s3.log
|
$ cat s3.log
|
||||||
|
@ -207,7 +207,7 @@ pool_error_threshold: 100
|
||||||
max_clients_count: 100
|
max_clients_count: 100
|
||||||
max_clients_deadline: 30s
|
max_clients_deadline: 30s
|
||||||
|
|
||||||
allowed_access_key_id_prefixes:
|
allowed_access_key_id_prefixes:
|
||||||
- Ck9BHsgKcnwfCTUSFm6pxhoNS4cBqgN2NQ8zVgPjqZDX
|
- Ck9BHsgKcnwfCTUSFm6pxhoNS4cBqgN2NQ8zVgPjqZDX
|
||||||
- 3stjWenX15YwYzczMr88gy3CQr4NYFBQ8P7keGzH5QFn
|
- 3stjWenX15YwYzczMr88gy3CQr4NYFBQ8P7keGzH5QFn
|
||||||
```
|
```
|
||||||
|
@ -296,7 +296,7 @@ File for `region_mapping` must contain something like this:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** on SIGHUP reload policies will be updated only if both parameters are valid.
|
**Note:** on SIGHUP reload policies will be updated only if both parameters are valid.
|
||||||
So if you change `default` to some valid value and set invalid path in `region_mapping` the `default` value won't be changed.
|
So if you change `default` to some valid value and set invalid path in `region_mapping` the `default` value won't be changed.
|
||||||
|
|
||||||
### `server` section
|
### `server` section
|
||||||
|
@ -469,7 +469,7 @@ prometheus:
|
||||||
|
|
||||||
# `frostfs` section
|
# `frostfs` section
|
||||||
|
|
||||||
Contains parameters of requests to FrostFS.
|
Contains parameters of requests to FrostFS.
|
||||||
This value can be overridden with `X-Amz-Meta-Frostfs-Copies-Number` header for `PutObject`, `CopyObject`, `CreateMultipartUpload`.
|
This value can be overridden with `X-Amz-Meta-Frostfs-Copies-Number` header for `PutObject`, `CopyObject`, `CreateMultipartUpload`.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
@ -77,72 +77,72 @@ Compatibility: 30/25/29 out of 33
|
||||||
|
|
||||||
Compatibility: 33/43/37 out of 64
|
Compatibility: 33/43/37 out of 64
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|----|------------------------------------------------------------------------------------------------|-------------|-------|--------|
|
|-----|------------------------------------------------------------------------------------------------|-------------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_good | ok | ok | ERROR |
|
| 1 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_good | ok | ok | ERROR |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_failed | FAIL | FAIL | FAIL |
|
| 2 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_failed | FAIL | FAIL | FAIL |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_overwrite_existed_good | ok | ok | ERROR |
|
| 3 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_overwrite_existed_good | ok | ok | ERROR |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_nonexisted_failed | FAIL | FAIL | FAIL |
|
| 4 | s3tests_boto3.functional.test_s3.test_put_object_ifmatch_nonexisted_failed | FAIL | FAIL | FAIL |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_good | ok | ok | ERROR |
|
| 5 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_good | ok | ok | ERROR |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_failed | FAIL | FAIL | FAIL |
|
| 6 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_failed | FAIL | FAIL | FAIL |
|
||||||
| 7 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_nonexisted_good | ok | ok | ERROR |
|
| 7 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_nonexisted_good | ok | ok | ERROR |
|
||||||
| 8 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_overwrite_existed_failed | FAIL | FAIL | FAIL |
|
| 8 | s3tests_boto3.functional.test_s3.test_put_object_ifnonmatch_overwrite_existed_failed | FAIL | FAIL | FAIL |
|
||||||
| 9 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_invalid_short | UNSUPPORTED | ok | ok |
|
| 9 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_invalid_short | UNSUPPORTED | ok | ok |
|
||||||
| 10 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_bad | UNSUPPORTED | ok | ok |
|
| 10 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_bad | UNSUPPORTED | ok | ok |
|
||||||
| 11 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_empty | UNSUPPORTED | ok | ok |
|
| 11 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_empty | UNSUPPORTED | ok | ok |
|
||||||
| 12 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_none | ok | ok | ok |
|
| 12 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_none | ok | ok | ok |
|
||||||
| 13 | s3tests_boto3.functional.test_headers.test_object_create_bad_expect_mismatch | ERROR | ERROR | ok |
|
| 13 | s3tests_boto3.functional.test_headers.test_object_create_bad_expect_mismatch | ERROR | ERROR | ok |
|
||||||
| 14 | s3tests_boto3.functional.test_headers.test_object_create_bad_expect_empty | ok | ok | ok |
|
| 14 | s3tests_boto3.functional.test_headers.test_object_create_bad_expect_empty | ok | ok | ok |
|
||||||
| 15 | s3tests_boto3.functional.test_headers.test_object_create_bad_expect_none | ok | ok | ok |
|
| 15 | s3tests_boto3.functional.test_headers.test_object_create_bad_expect_none | ok | ok | ok |
|
||||||
| 16 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_empty | FAIL | FAIL | ok |
|
| 16 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_empty | FAIL | FAIL | ok |
|
||||||
| 17 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_negative | ok | ok | ok |
|
| 17 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_negative | ok | ok | ok |
|
||||||
| 18 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_none | FAIL | FAIL | FAIL |
|
| 18 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_none | FAIL | FAIL | FAIL |
|
||||||
| 19 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_mismatch_above | ERROR | ERROR | ERROR |
|
| 19 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_mismatch_above | ERROR | ERROR | ERROR |
|
||||||
| 20 | s3tests_boto3.functional.test_headers.test_object_create_bad_contenttype_invalid | ok | ok | ok |
|
| 20 | s3tests_boto3.functional.test_headers.test_object_create_bad_contenttype_invalid | ok | ok | ok |
|
||||||
| 21 | s3tests_boto3.functional.test_headers.test_object_create_bad_contenttype_empty | ok | ok | ok |
|
| 21 | s3tests_boto3.functional.test_headers.test_object_create_bad_contenttype_empty | ok | ok | ok |
|
||||||
| 22 | s3tests_boto3.functional.test_headers.test_object_create_bad_contenttype_none | ok | ok | ok |
|
| 22 | s3tests_boto3.functional.test_headers.test_object_create_bad_contenttype_none | ok | ok | ok |
|
||||||
| 23 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_empty | FAIL | FAIL | FAIL |
|
| 23 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_empty | FAIL | FAIL | FAIL |
|
||||||
| 24 | s3tests_boto3.functional.test_headers.test_object_create_date_and_amz_date | ERROR | ERROR | ERROR |
|
| 24 | s3tests_boto3.functional.test_headers.test_object_create_date_and_amz_date | ERROR | ERROR | ERROR |
|
||||||
| 25 | s3tests_boto3.functional.test_headers.test_object_create_amz_date_and_no_date | ERROR | ERROR | ERROR |
|
| 25 | s3tests_boto3.functional.test_headers.test_object_create_amz_date_and_no_date | ERROR | ERROR | ERROR |
|
||||||
| 26 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_none | FAIL | FAIL | FAIL |
|
| 26 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_none | FAIL | FAIL | FAIL |
|
||||||
| 27 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_invalid_garbage_aws2 | UNSUPPORTED | ok | ok |
|
| 27 | s3tests_boto3.functional.test_headers.test_object_create_bad_md5_invalid_garbage_aws2 | UNSUPPORTED | ok | ok |
|
||||||
| 28 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_mismatch_below_aws2 | FAIL | FAIL | ok |
|
| 28 | s3tests_boto3.functional.test_headers.test_object_create_bad_contentlength_mismatch_below_aws2 | FAIL | FAIL | ok |
|
||||||
| 29 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_incorrect_aws2 | FAIL | FAIL | FAIL |
|
| 29 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_incorrect_aws2 | FAIL | FAIL | FAIL |
|
||||||
| 30 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_invalid_aws2 | FAIL | FAIL | FAIL |
|
| 30 | s3tests_boto3.functional.test_headers.test_object_create_bad_authorization_invalid_aws2 | FAIL | FAIL | FAIL |
|
||||||
| 31 | s3tests_boto3.functional.test_headers.test_object_create_bad_ua_empty_aws2 | ERROR | ok | ok |
|
| 31 | s3tests_boto3.functional.test_headers.test_object_create_bad_ua_empty_aws2 | ERROR | ok | ok |
|
||||||
| 32 | s3tests_boto3.functional.test_headers.test_object_create_bad_ua_none_aws2 | ERROR | ok | ok |
|
| 32 | s3tests_boto3.functional.test_headers.test_object_create_bad_ua_none_aws2 | ERROR | ok | ok |
|
||||||
| 33 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_invalid_aws2 | FAIL | FAIL | ok |
|
| 33 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_invalid_aws2 | FAIL | FAIL | ok |
|
||||||
| 34 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_empty_aws2 | FAIL | FAIL | ok |
|
| 34 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_empty_aws2 | FAIL | FAIL | ok |
|
||||||
| 35 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_none_aws2 | FAIL | FAIL | FAIL |
|
| 35 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_none_aws2 | FAIL | FAIL | FAIL |
|
||||||
| 36 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_before_today_aws2 | FAIL | ok | ok |
|
| 36 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_before_today_aws2 | FAIL | ok | ok |
|
||||||
| 37 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_before_epoch_aws2 | FAIL | FAIL | ok |
|
| 37 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_before_epoch_aws2 | FAIL | FAIL | ok |
|
||||||
| 38 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_after_end_aws2 | FAIL | ok | ok |
|
| 38 | s3tests_boto3.functional.test_headers.test_object_create_bad_date_after_end_aws2 | FAIL | ok | ok |
|
||||||
| 39 | s3tests_boto3.functional.test_s3.test_object_anon_put | ok | ok | ok |
|
| 39 | s3tests_boto3.functional.test_s3.test_object_anon_put | ok | ok | ok |
|
||||||
| 40 | s3tests_boto3.functional.test_s3.test_object_put_authenticated | ok | ok | ok |
|
| 40 | s3tests_boto3.functional.test_s3.test_object_put_authenticated | ok | ok | ok |
|
||||||
| 41 | s3tests_boto3.functional.test_s3.test_object_raw_put_authenticated_expired | ok | FAIL | FAIL |
|
| 41 | s3tests_boto3.functional.test_s3.test_object_raw_put_authenticated_expired | ok | FAIL | FAIL |
|
||||||
| 42 | s3tests_boto3.functional.test_s3.test_object_write_file | ok | ok | ok |
|
| 42 | s3tests_boto3.functional.test_s3.test_object_write_file | ok | ok | ok |
|
||||||
| 43 | s3tests_boto3.functional.test_s3.test_object_write_check_etag | FAIL | ok | ok |
|
| 43 | s3tests_boto3.functional.test_s3.test_object_write_check_etag | FAIL | ok | ok |
|
||||||
| 44 | s3tests_boto3.functional.test_s3.test_object_write_cache_control | ok | ok | ok |
|
| 44 | s3tests_boto3.functional.test_s3.test_object_write_cache_control | ok | ok | ok |
|
||||||
| 45 | s3tests_boto3.functional.test_s3.test_object_write_expires | ok | ok | ok |
|
| 45 | s3tests_boto3.functional.test_s3.test_object_write_expires | ok | ok | ok |
|
||||||
| 46 | s3tests_boto3.functional.test_s3.test_object_write_read_update_read_delete | ok | ok | ok |
|
| 46 | s3tests_boto3.functional.test_s3.test_object_write_read_update_read_delete | ok | ok | ok |
|
||||||
| 47 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_none_to_good | ok | ok | ok |
|
| 47 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_none_to_good | ok | ok | ok |
|
||||||
| 48 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_none_to_empty | ERROR | ok | ok |
|
| 48 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_none_to_empty | ERROR | ok | ok |
|
||||||
| 49 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_overwrite_to_empty | ERROR | ok | ok |
|
| 49 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_overwrite_to_empty | ERROR | ok | ok |
|
||||||
| 50 | s3tests_boto3.functional.test_s3.test_object_set_get_non_utf8_metadata | ok | ok | FAIL |
|
| 50 | s3tests_boto3.functional.test_s3.test_object_set_get_non_utf8_metadata | ok | ok | FAIL |
|
||||||
| 51 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_prefix | ok | ok | FAIL |
|
| 51 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_prefix | ok | ok | FAIL |
|
||||||
| 52 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_suffix | ok | ok | FAIL |
|
| 52 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_suffix | ok | ok | FAIL |
|
||||||
| 53 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_infix | ok | ok | FAIL |
|
| 53 | s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_infix | ok | ok | FAIL |
|
||||||
| 54 | s3tests_boto3.functional.test_s3.test_object_metadata_replaced_on_put | ok | ok | ok |
|
| 54 | s3tests_boto3.functional.test_s3.test_object_metadata_replaced_on_put | ok | ok | ok |
|
||||||
| 55 | s3tests_boto3.functional.test_s3.test_object_write_to_nonexist_bucket | ok | ok | ok |
|
| 55 | s3tests_boto3.functional.test_s3.test_object_write_to_nonexist_bucket | ok | ok | ok |
|
||||||
| 56 | s3tests_boto3.functional.test_s3.test_atomic_write_1mb | ok | ok | ok |
|
| 56 | s3tests_boto3.functional.test_s3.test_atomic_write_1mb | ok | ok | ok |
|
||||||
| 57 | s3tests_boto3.functional.test_s3.test_atomic_write_4mb | ok | ok | ok |
|
| 57 | s3tests_boto3.functional.test_s3.test_atomic_write_4mb | ok | ok | ok |
|
||||||
| 58 | s3tests_boto3.functional.test_s3.test_atomic_write_8mb | ok | ok | ok |
|
| 58 | s3tests_boto3.functional.test_s3.test_atomic_write_8mb | ok | ok | ok |
|
||||||
| 59 | s3tests_boto3.functional.test_s3.test_atomic_dual_write_1mb | ok | ok | ERROR |
|
| 59 | s3tests_boto3.functional.test_s3.test_atomic_dual_write_1mb | ok | ok | ERROR |
|
||||||
| 60 | s3tests_boto3.functional.test_s3.test_atomic_dual_write_4mb | ok | ok | ERROR |
|
| 60 | s3tests_boto3.functional.test_s3.test_atomic_dual_write_4mb | ok | ok | ERROR |
|
||||||
| 61 | s3tests_boto3.functional.test_s3.test_atomic_dual_write_8mb | ok | ok | ERROR |
|
| 61 | s3tests_boto3.functional.test_s3.test_atomic_dual_write_8mb | ok | ok | ERROR |
|
||||||
| 62 | s3tests_boto3.functional.test_s3.test_atomic_conditional_write_1mb | ok | ok | ERROR |
|
| 62 | s3tests_boto3.functional.test_s3.test_atomic_conditional_write_1mb | ok | ok | ERROR |
|
||||||
| 63 | s3tests_boto3.functional.test_s3.test_atomic_dual_conditional_write_1mb | FAIL | FAIL | FAIL |
|
| 63 | s3tests_boto3.functional.test_s3.test_atomic_dual_conditional_write_1mb | FAIL | FAIL | FAIL |
|
||||||
| 64 | s3tests_boto3.functional.test_s3.test_atomic_write_bucket_gone | ok | ok | ok |
|
| 64 | s3tests_boto3.functional.test_s3.test_atomic_write_bucket_gone | ok | ok | ok |
|
||||||
|
|
||||||
## PostObject
|
## PostObject
|
||||||
|
|
||||||
|
@ -342,30 +342,30 @@ Compatibility: 4/5/29 out of 29
|
||||||
|
|
||||||
Compatibility: 19/15/19 out of 22
|
Compatibility: 19/15/19 out of 22
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|----|----------------------------------------------------------------------------------|-------|-------|--------|
|
|-----|----------------------------------------------------------------------------------|-------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_multipart_upload_empty | ok | FAIL | FAIL |
|
| 1 | s3tests_boto3.functional.test_s3.test_multipart_upload_empty | ok | FAIL | FAIL |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_multipart_upload_small | ERROR | ERROR | ok |
|
| 2 | s3tests_boto3.functional.test_s3.test_multipart_upload_small | ERROR | ERROR | ok |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_multipart_copy_small | ok | ok | ok |
|
| 3 | s3tests_boto3.functional.test_s3.test_multipart_copy_small | ok | ok | ok |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_multipart_copy_invalid_range | ok | FAIL | FAIL |
|
| 4 | s3tests_boto3.functional.test_s3.test_multipart_copy_invalid_range | ok | FAIL | FAIL |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_multipart_copy_improper_range | ok | ok | ok |
|
| 5 | s3tests_boto3.functional.test_s3.test_multipart_copy_improper_range | ok | ok | ok |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_multipart_copy_without_range | ok | ok | ok |
|
| 6 | s3tests_boto3.functional.test_s3.test_multipart_copy_without_range | ok | ok | ok |
|
||||||
| 7 | s3tests_boto3.functional.test_s3.test_multipart_copy_special_names | ok | ok | ok |
|
| 7 | s3tests_boto3.functional.test_s3.test_multipart_copy_special_names | ok | ok | ok |
|
||||||
| 8 | s3tests_boto3.functional.test_s3.test_multipart_upload | ERROR | ERROR | ok |
|
| 8 | s3tests_boto3.functional.test_s3.test_multipart_upload | ERROR | ERROR | ok |
|
||||||
| 9 | s3tests_boto3.functional.test_s3.test_multipart_upload_resend_part | ok | ok | ok |
|
| 9 | s3tests_boto3.functional.test_s3.test_multipart_upload_resend_part | ok | ok | ok |
|
||||||
| 10 | s3tests_boto3.functional.test_s3.test_multipart_upload_multiple_sizes | ok | ok | ok |
|
| 10 | s3tests_boto3.functional.test_s3.test_multipart_upload_multiple_sizes | ok | ok | ok |
|
||||||
| 11 | s3tests_boto3.functional.test_s3.test_multipart_copy_multiple_sizes | ok | ok | ok |
|
| 11 | s3tests_boto3.functional.test_s3.test_multipart_copy_multiple_sizes | ok | ok | ok |
|
||||||
| 12 | s3tests_boto3.functional.test_s3.test_multipart_upload_size_too_small | ok | ok | ok |
|
| 12 | s3tests_boto3.functional.test_s3.test_multipart_upload_size_too_small | ok | ok | ok |
|
||||||
| 13 | s3tests_boto3.functional.test_s3.test_multipart_upload_contents | ok | ok | ok |
|
| 13 | s3tests_boto3.functional.test_s3.test_multipart_upload_contents | ok | ok | ok |
|
||||||
| 14 | s3tests_boto3.functional.test_s3.test_multipart_upload_overwrite_existing_object | ok | ok | ok |
|
| 14 | s3tests_boto3.functional.test_s3.test_multipart_upload_overwrite_existing_object | ok | ok | ok |
|
||||||
| 15 | s3tests_boto3.functional.test_s3.test_abort_multipart_upload | ok | ok | ok |
|
| 15 | s3tests_boto3.functional.test_s3.test_abort_multipart_upload | ok | ok | ok |
|
||||||
| 16 | s3tests_boto3.functional.test_s3.test_abort_multipart_upload_not_found | ok | ok | ok |
|
| 16 | s3tests_boto3.functional.test_s3.test_abort_multipart_upload_not_found | ok | ok | ok |
|
||||||
| 17 | s3tests_boto3.functional.test_s3.test_list_multipart_upload | ok | ERROR | ok |
|
| 17 | s3tests_boto3.functional.test_s3.test_list_multipart_upload | ok | ERROR | ok |
|
||||||
| 18 | s3tests_boto3.functional.test_s3.test_multipart_upload_missing_part | ok | ok | ok |
|
| 18 | s3tests_boto3.functional.test_s3.test_multipart_upload_missing_part | ok | ok | ok |
|
||||||
| 19 | s3tests_boto3.functional.test_s3.test_multipart_upload_incorrect_etag | ok | ok | ok |
|
| 19 | s3tests_boto3.functional.test_s3.test_multipart_upload_incorrect_etag | ok | ok | ok |
|
||||||
| 20 | s3tests_boto3.functional.test_s3.test_multipart_resend_first_finishes_last | ERROR | ERROR | ERROR |
|
| 20 | s3tests_boto3.functional.test_s3.test_multipart_resend_first_finishes_last | ERROR | ERROR | ERROR |
|
||||||
| 21 | s3tests_boto3.functional.test_s3.test_atomic_multipart_upload_write | ok | ok | ok |
|
| 21 | s3tests_boto3.functional.test_s3.test_atomic_multipart_upload_write | ok | ok | ok |
|
||||||
| 22 | s3tests_boto3.functional.test_s3.test_multipart_copy_versioned | ok | ERROR | ok |
|
| 22 | s3tests_boto3.functional.test_s3.test_multipart_copy_versioned | ok | ERROR | ok |
|
||||||
|
|
||||||
Comments: in [PR](https://github.com/nspcc-dev/s3-tests/pull/5)
|
Comments: in [PR](https://github.com/nspcc-dev/s3-tests/pull/5)
|
||||||
|
|
||||||
|
@ -373,118 +373,118 @@ Comments: in [PR](https://github.com/nspcc-dev/s3-tests/pull/5)
|
||||||
|
|
||||||
Compatibility: 9/6/8 out of 11
|
Compatibility: 9/6/8 out of 11
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|----|------------------------------------------------------------|-------|-------|--------|
|
|-----|------------------------------------------------------------|-------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_set_bucket_tagging | FAIL | FAIL | FAIL |
|
| 1 | s3tests_boto3.functional.test_s3.test_set_bucket_tagging | FAIL | FAIL | FAIL |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_get_obj_tagging | ok | ok | ok |
|
| 2 | s3tests_boto3.functional.test_s3.test_get_obj_tagging | ok | ok | ok |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_get_obj_head_tagging | ok | ok | ok |
|
| 3 | s3tests_boto3.functional.test_s3.test_get_obj_head_tagging | ok | ok | ok |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_put_max_tags | ok | FAIL | ok |
|
| 4 | s3tests_boto3.functional.test_s3.test_put_max_tags | ok | FAIL | ok |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_put_excess_tags | FAIL | FAIL | FAIL |
|
| 5 | s3tests_boto3.functional.test_s3.test_put_excess_tags | FAIL | FAIL | FAIL |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_put_max_kvsize_tags | ok | ok | ok |
|
| 6 | s3tests_boto3.functional.test_s3.test_put_max_kvsize_tags | ok | ok | ok |
|
||||||
| 7 | s3tests_boto3.functional.test_s3.test_put_excess_key_tags | ok | ok | ok |
|
| 7 | s3tests_boto3.functional.test_s3.test_put_excess_key_tags | ok | ok | ok |
|
||||||
| 8 | s3tests_boto3.functional.test_s3.test_put_excess_val_tags | ok | ok | ok |
|
| 8 | s3tests_boto3.functional.test_s3.test_put_excess_val_tags | ok | ok | ok |
|
||||||
| 9 | s3tests_boto3.functional.test_s3.test_put_modify_tags | ok | FAIL | FAIL |
|
| 9 | s3tests_boto3.functional.test_s3.test_put_modify_tags | ok | FAIL | FAIL |
|
||||||
| 10 | s3tests_boto3.functional.test_s3.test_put_delete_tags | ok | ok | ok |
|
| 10 | s3tests_boto3.functional.test_s3.test_put_delete_tags | ok | ok | ok |
|
||||||
| 11 | s3tests_boto3.functional.test_s3.test_put_obj_with_tags | ok | FAIL | ok |
|
| 11 | s3tests_boto3.functional.test_s3.test_put_obj_with_tags | ok | FAIL | ok |
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
Compatibility: 23/19/24 out of 26
|
Compatibility: 23/19/24 out of 26
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|----|---------------------------------------------------------------------------------------------|-------|-------|--------|
|
|-----|---------------------------------------------------------------------------------------------|-------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_versioning_bucket_create_suspend | ok | ok | ok |
|
| 1 | s3tests_boto3.functional.test_s3.test_versioning_bucket_create_suspend | ok | ok | ok |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove | ok | ok | ok |
|
| 2 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove | ok | ok | ok |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove_head | ok | ok | ok |
|
| 3 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove_head | ok | ok | ok |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_removal | ok | ok | ok |
|
| 4 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_removal | ok | ok | ok |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite | ok | ok | ok |
|
| 5 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite | ok | ok | ok |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite_suspended | ok | ok | ok |
|
| 6 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite_suspended | ok | ok | ok |
|
||||||
| 7 | s3tests_boto3.functional.test_s3.test_versioning_obj_suspend_versions | ok | ok | ok |
|
| 7 | s3tests_boto3.functional.test_s3.test_versioning_obj_suspend_versions | ok | ok | ok |
|
||||||
| 8 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_all | ok | ok | ok |
|
| 8 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_all | ok | ok | ok |
|
||||||
| 9 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_special_names | ok | ok | ok |
|
| 9 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_special_names | ok | ok | ok |
|
||||||
| 10 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_overwrite_multipart | ok | ok | ok |
|
| 10 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_overwrite_multipart | ok | ok | ok |
|
||||||
| 11 | s3tests_boto3.functional.test_s3.test_versioning_obj_list_marker | ok | ok | ok |
|
| 11 | s3tests_boto3.functional.test_s3.test_versioning_obj_list_marker | ok | ok | ok |
|
||||||
| 12 | s3tests_boto3.functional.test_s3.test_versioning_copy_obj_version | ok | ok | ok |
|
| 12 | s3tests_boto3.functional.test_s3.test_versioning_copy_obj_version | ok | ok | ok |
|
||||||
| 13 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete | ok | ok | ok |
|
| 13 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete | ok | ok | ok |
|
||||||
| 14 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker | ok | ok | ok |
|
| 14 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker | ok | ok | ok |
|
||||||
| 15 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker_create | ok | ERROR | ok |
|
| 15 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker_create | ok | ERROR | ok |
|
||||||
| 16 | s3tests_boto3.functional.test_s3.test_versioned_object_acl | FAIL | FAIL | FAIL |
|
| 16 | s3tests_boto3.functional.test_s3.test_versioned_object_acl | FAIL | FAIL | FAIL |
|
||||||
| 17 | s3tests_boto3.functional.test_s3.test_versioned_object_acl_no_version_specified | FAIL | FAIL | FAIL |
|
| 17 | s3tests_boto3.functional.test_s3.test_versioned_object_acl_no_version_specified | FAIL | FAIL | FAIL |
|
||||||
| 18 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_concurrent_remove | ok | ok | ok |
|
| 18 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_concurrent_remove | ok | ok | ok |
|
||||||
| 19 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_and_remove | ok | ok | ok |
|
| 19 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_and_remove | ok | ok | ok |
|
||||||
| 20 | s3tests_boto3.functional.test_s3.test_versioning_bucket_atomic_upload_return_version_id | ok | FAIL | ok |
|
| 20 | s3tests_boto3.functional.test_s3.test_versioning_bucket_atomic_upload_return_version_id | ok | FAIL | ok |
|
||||||
| 21 | s3tests_boto3.functional.test_s3.test_versioning_bucket_multipart_upload_return_version_id | ok | FAIL | ok |
|
| 21 | s3tests_boto3.functional.test_s3.test_versioning_bucket_multipart_upload_return_version_id | ok | FAIL | ok |
|
||||||
| 22 | s3tests_boto3.functional.test_s3.test_bucket_list_return_data_versioning | ERROR | ERROR | ok |
|
| 22 | s3tests_boto3.functional.test_s3.test_bucket_list_return_data_versioning | ERROR | ERROR | ok |
|
||||||
| 23 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_bucket | ok | ok | ok |
|
| 23 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_bucket | ok | ok | ok |
|
||||||
| 24 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_url_encoding | ok | ok | ok |
|
| 24 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_url_encoding | ok | ok | ok |
|
||||||
| 25 | s3tests_boto3.functional.test_s3.test_object_copy_versioning_multipart_upload | ok | ok | ok |
|
| 25 | s3tests_boto3.functional.test_s3.test_object_copy_versioning_multipart_upload | ok | ok | ok |
|
||||||
| 26 | s3tests_boto3.functional.test_s3.test_multipart_copy_versioned | ok | ERROR | ok |
|
| 26 | s3tests_boto3.functional.test_s3.test_multipart_copy_versioned | ok | ERROR | ok |
|
||||||
|
|
||||||
## Bucket
|
## Bucket
|
||||||
|
|
||||||
Compatibility: 38/38/45 out of 59
|
Compatibility: 38/38/45 out of 59
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|----|----------------------------------------------------------------------------------------------|-------|-------|--------|
|
|-----|----------------------------------------------------------------------------------------------|-------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_authorization_invalid_aws2 | FAIL | FAIL | FAIL |
|
| 1 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_authorization_invalid_aws2 | FAIL | FAIL | FAIL |
|
||||||
| 2 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_ua_empty_aws2 | ERROR | ok | ok |
|
| 2 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_ua_empty_aws2 | ERROR | ok | ok |
|
||||||
| 3 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_ua_none_aws2 | ERROR | ok | ok |
|
| 3 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_ua_none_aws2 | ERROR | ok | ok |
|
||||||
| 4 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_invalid_aws2 | FAIL | FAIL | ok |
|
| 4 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_invalid_aws2 | FAIL | FAIL | ok |
|
||||||
| 5 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_empty_aws2 | FAIL | FAIL | ok |
|
| 5 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_empty_aws2 | FAIL | FAIL | ok |
|
||||||
| 6 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_none_aws2 | FAIL | FAIL | FAIL |
|
| 6 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_none_aws2 | FAIL | FAIL | FAIL |
|
||||||
| 7 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_before_today_aws2 | FAIL | ok | ok |
|
| 7 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_before_today_aws2 | FAIL | ok | ok |
|
||||||
| 8 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_after_today_aws2 | FAIL | ok | ok |
|
| 8 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_after_today_aws2 | FAIL | ok | ok |
|
||||||
| 9 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_before_epoch_aws2 | FAIL | FAIL | ok |
|
| 9 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_date_before_epoch_aws2 | FAIL | FAIL | ok |
|
||||||
| 10 | s3tests_boto3.functional.test_headers.test_bucket_create_contentlength_none | ok | ok | ok |
|
| 10 | s3tests_boto3.functional.test_headers.test_bucket_create_contentlength_none | ok | ok | ok |
|
||||||
| 11 | s3tests_boto3.functional.test_headers.test_bucket_put_bad_canned_acl | ok | FAIL | ok |
|
| 11 | s3tests_boto3.functional.test_headers.test_bucket_put_bad_canned_acl | ok | FAIL | ok |
|
||||||
| 12 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_expect_mismatch | ERROR | ERROR | ok |
|
| 12 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_expect_mismatch | ERROR | ERROR | ok |
|
||||||
| 13 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_expect_empty | ok | ok | ok |
|
| 13 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_expect_empty | ok | ok | ok |
|
||||||
| 14 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_contentlength_empty | FAIL | FAIL | ok |
|
| 14 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_contentlength_empty | FAIL | FAIL | ok |
|
||||||
| 15 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_contentlength_negative | ok | ok | ok |
|
| 15 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_contentlength_negative | ok | ok | ok |
|
||||||
| 16 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_contentlength_none | ok | ok | ok |
|
| 16 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_contentlength_none | ok | ok | ok |
|
||||||
| 17 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_authorization_empty | FAIL | FAIL | FAIL |
|
| 17 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_authorization_empty | FAIL | FAIL | FAIL |
|
||||||
| 18 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_authorization_none | FAIL | FAIL | FAIL |
|
| 18 | s3tests_boto3.functional.test_headers.test_bucket_create_bad_authorization_none | FAIL | FAIL | FAIL |
|
||||||
| 19 | s3tests_boto3.functional.test_s3.test_bucket_notexist | ok | ok | ok |
|
| 19 | s3tests_boto3.functional.test_s3.test_bucket_notexist | ok | ok | ok |
|
||||||
| 20 | s3tests_boto3.functional.test_s3.test_bucketv2_notexist | ok | ok | ok |
|
| 20 | s3tests_boto3.functional.test_s3.test_bucketv2_notexist | ok | ok | ok |
|
||||||
| 21 | s3tests_boto3.functional.test_s3.test_bucket_delete_notexist | ok | ok | ok |
|
| 21 | s3tests_boto3.functional.test_s3.test_bucket_delete_notexist | ok | ok | ok |
|
||||||
| 22 | s3tests_boto3.functional.test_s3.test_bucket_delete_nonempty | ok | ok | ok |
|
| 22 | s3tests_boto3.functional.test_s3.test_bucket_delete_nonempty | ok | ok | ok |
|
||||||
| 23 | s3tests_boto3.functional.test_s3.test_bucket_concurrent_set_canned_acl | ok | FAIL | FAIL |
|
| 23 | s3tests_boto3.functional.test_s3.test_bucket_concurrent_set_canned_acl | ok | FAIL | FAIL |
|
||||||
| 24 | s3tests_boto3.functional.test_s3.test_bucket_create_delete | ok | ok | ok |
|
| 24 | s3tests_boto3.functional.test_s3.test_bucket_create_delete | ok | ok | ok |
|
||||||
| 25 | s3tests_boto3.functional.test_s3.test_bucket_head | ok | ok | ok |
|
| 25 | s3tests_boto3.functional.test_s3.test_bucket_head | ok | ok | ok |
|
||||||
| 26 | s3tests_boto3.functional.test_s3.test_bucket_head_notexist | ok | ok | ok |
|
| 26 | s3tests_boto3.functional.test_s3.test_bucket_head_notexist | ok | ok | ok |
|
||||||
| 27 | s3tests_boto3.functional.test_s3.test_bucket_head_extended | ERROR | ERROR | ERROR |
|
| 27 | s3tests_boto3.functional.test_s3.test_bucket_head_extended | ERROR | ERROR | ERROR |
|
||||||
| 28 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_starts_nonalpha | ok | ok | ok |
|
| 28 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_starts_nonalpha | ok | ok | ok |
|
||||||
| 29 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_short_empty | ERROR | ERROR | ERROR |
|
| 29 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_short_empty | ERROR | ERROR | ERROR |
|
||||||
| 30 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_short_one | ok | ok | ok |
|
| 30 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_short_one | ok | ok | ok |
|
||||||
| 31 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_short_two | ok | ok | ok |
|
| 31 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_short_two | ok | ok | ok |
|
||||||
| 32 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_long | ERROR | ERROR | ERROR |
|
| 32 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_long | ERROR | ERROR | ERROR |
|
||||||
| 33 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_60 | ok | ok | ok |
|
| 33 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_60 | ok | ok | ok |
|
||||||
| 34 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_61 | ok | ok | ok |
|
| 34 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_61 | ok | ok | ok |
|
||||||
| 35 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_62 | ok | ok | ok |
|
| 35 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_62 | ok | ok | ok |
|
||||||
| 36 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_63 | ok | ok | ok |
|
| 36 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_long_63 | ok | ok | ok |
|
||||||
| 37 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_ip | ok | ok | FAIL |
|
| 37 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_ip | ok | ok | FAIL |
|
||||||
| 38 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_punctuation | ERROR | ERROR | ERROR |
|
| 38 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_bad_punctuation | ERROR | ERROR | ERROR |
|
||||||
| 39 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_underscore | ok | ok | ok |
|
| 39 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_underscore | ok | ok | ok |
|
||||||
| 40 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_long | ok | ok | ok |
|
| 40 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_long | ok | ok | ok |
|
||||||
| 41 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dash_at_end | ok | ok | ok |
|
| 41 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dash_at_end | ok | ok | ok |
|
||||||
| 42 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dot_dot | ok | ok | ok |
|
| 42 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dot_dot | ok | ok | ok |
|
||||||
| 43 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dot_dash | ok | ok | ok |
|
| 43 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dot_dash | ok | ok | ok |
|
||||||
| 44 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dash_dot | ok | ok | ok |
|
| 44 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_dns_dash_dot | ok | ok | ok |
|
||||||
| 45 | s3tests_boto3.functional.test_s3.test_bucket_create_exists | ERROR | ERROR | ok |
|
| 45 | s3tests_boto3.functional.test_s3.test_bucket_create_exists | ERROR | ERROR | ok |
|
||||||
| 46 | s3tests_boto3.functional.test_s3.test_bucket_get_location | ok | FAIL | ERROR |
|
| 46 | s3tests_boto3.functional.test_s3.test_bucket_get_location | ok | FAIL | ERROR |
|
||||||
| 47 | s3tests_boto3.functional.test_s3.test_bucket_create_exists_nonowner | ok | FAIL | ok |
|
| 47 | s3tests_boto3.functional.test_s3.test_bucket_create_exists_nonowner | ok | FAIL | ok |
|
||||||
| 48 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_starts_alpha | ok | ok | ok |
|
| 48 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_starts_alpha | ok | ok | ok |
|
||||||
| 49 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_starts_digit | ok | ok | ok |
|
| 49 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_starts_digit | ok | ok | ok |
|
||||||
| 50 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_contains_period | ERROR | ok | ok |
|
| 50 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_contains_period | ERROR | ok | ok |
|
||||||
| 51 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_contains_hyphen | ok | ok | ok |
|
| 51 | s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_contains_hyphen | ok | ok | ok |
|
||||||
| 52 | s3tests_boto3.functional.test_s3.test_bucket_recreate_not_overriding | ERROR | ERROR | ok |
|
| 52 | s3tests_boto3.functional.test_s3.test_bucket_recreate_not_overriding | ERROR | ERROR | ok |
|
||||||
| 53 | s3tests_boto3.functional.test_s3.test_bucket_create_special_key_names | ok | ok | ok |
|
| 53 | s3tests_boto3.functional.test_s3.test_bucket_create_special_key_names | ok | ok | ok |
|
||||||
| 54 | s3tests_boto3.functional.test_s3.test_bucket_policy_set_condition_operator_end_with_IfExists | ERROR | ERROR | FAIL |
|
| 54 | s3tests_boto3.functional.test_s3.test_bucket_policy_set_condition_operator_end_with_IfExists | ERROR | ERROR | FAIL |
|
||||||
| 55 | s3tests_boto3.functional.test_s3.test_buckets_create_then_list | ok | ok | ok |
|
| 55 | s3tests_boto3.functional.test_s3.test_buckets_create_then_list | ok | ok | ok |
|
||||||
| 56 | s3tests_boto3.functional.test_s3.test_buckets_list_ctime | ok | ok | FAIL |
|
| 56 | s3tests_boto3.functional.test_s3.test_buckets_list_ctime | ok | ok | FAIL |
|
||||||
| 57 | s3tests_boto3.functional.test_s3.test_list_buckets_anonymous | ok | ERROR | ERROR |
|
| 57 | s3tests_boto3.functional.test_s3.test_list_buckets_anonymous | ok | ERROR | ERROR |
|
||||||
| 58 | s3tests_boto3.functional.test_s3.test_list_buckets_invalid_auth | ok | ok | ok |
|
| 58 | s3tests_boto3.functional.test_s3.test_list_buckets_invalid_auth | ok | ok | ok |
|
||||||
| 59 | s3tests_boto3.functional.test_s3.test_list_buckets_bad_auth | ok | ok | ok |
|
| 59 | s3tests_boto3.functional.test_s3.test_list_buckets_bad_auth | ok | ok | ok |
|
||||||
|
|
||||||
## Bucket ACL
|
## Bucket ACL
|
||||||
|
|
||||||
|
@ -581,90 +581,90 @@ This group is not explicitly supported by s3-gw, but some other tests may pass.
|
||||||
Compatibility: 0/10/18 out of 29
|
Compatibility: 0/10/18 out of 29
|
||||||
This group is not explicitly supported by s3-gw, but some tests may pass.
|
This group is not explicitly supported by s3-gw, but some tests may pass.
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|----|---------------------------------------------------------------------------------|-------|-------|--------|
|
|-----|---------------------------------------------------------------------------------|-------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_lifecycle_set | ERROR | ok | ok |
|
| 1 | s3tests_boto3.functional.test_s3.test_lifecycle_set | ERROR | ok | ok |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_lifecycle_get | ERROR | FAIL | ok |
|
| 2 | s3tests_boto3.functional.test_s3.test_lifecycle_get | ERROR | FAIL | ok |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_lifecycle_get_no_id | ERROR | ERROR | ok |
|
| 3 | s3tests_boto3.functional.test_s3.test_lifecycle_get_no_id | ERROR | ERROR | ok |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration | ERROR | FAIL | FAIL |
|
| 4 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration | ERROR | FAIL | FAIL |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_lifecyclev2_expiration | ERROR | FAIL | FAIL |
|
| 5 | s3tests_boto3.functional.test_s3.test_lifecyclev2_expiration | ERROR | FAIL | FAIL |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_versioning_enabled | ERROR | ERROR | ok |
|
| 6 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_versioning_enabled | ERROR | ERROR | ok |
|
||||||
| 7 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_tags1 | ERROR | ERROR | ERROR |
|
| 7 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_tags1 | ERROR | ERROR | ERROR |
|
||||||
| 8 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_tags2 | ERROR | ERROR | ERROR |
|
| 8 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_tags2 | ERROR | ERROR | ERROR |
|
||||||
| 9 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_versioned_tags2 | ERROR | ERROR | ERROR |
|
| 9 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_versioned_tags2 | ERROR | ERROR | ERROR |
|
||||||
| 10 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_noncur_tags1 | ERROR | ERROR | ERROR |
|
| 10 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_noncur_tags1 | ERROR | ERROR | ERROR |
|
||||||
| 11 | s3tests_boto3.functional.test_s3.test_lifecycle_id_too_long | FAIL | FAIL | ok |
|
| 11 | s3tests_boto3.functional.test_s3.test_lifecycle_id_too_long | FAIL | FAIL | ok |
|
||||||
| 12 | s3tests_boto3.functional.test_s3.test_lifecycle_same_id | FAIL | FAIL | ok |
|
| 12 | s3tests_boto3.functional.test_s3.test_lifecycle_same_id | FAIL | FAIL | ok |
|
||||||
| 13 | s3tests_boto3.functional.test_s3.test_lifecycle_invalid_status | FAIL | FAIL | ok |
|
| 13 | s3tests_boto3.functional.test_s3.test_lifecycle_invalid_status | FAIL | FAIL | ok |
|
||||||
| 14 | s3tests_boto3.functional.test_s3.test_lifecycle_set_date | ERROR | ok | ok |
|
| 14 | s3tests_boto3.functional.test_s3.test_lifecycle_set_date | ERROR | ok | ok |
|
||||||
| 15 | s3tests_boto3.functional.test_s3.test_lifecycle_set_invalid_date | FAIL | ok | ok |
|
| 15 | s3tests_boto3.functional.test_s3.test_lifecycle_set_invalid_date | FAIL | ok | ok |
|
||||||
| 16 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_date | ERROR | FAIL | FAIL |
|
| 16 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_date | ERROR | FAIL | FAIL |
|
||||||
| 17 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_days0 | FAIL | FAIL | ok |
|
| 17 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_days0 | FAIL | FAIL | ok |
|
||||||
| 18 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_put | ERROR | ok | ok |
|
| 18 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_put | ERROR | ok | ok |
|
||||||
| 19 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_head | ERROR | ok | ok |
|
| 19 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_head | ERROR | ok | ok |
|
||||||
| 20 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_tags_head | ERROR | ok | FAIL |
|
| 20 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_tags_head | ERROR | ok | FAIL |
|
||||||
| 21 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_and_tags_head | ERROR | ERROR | ok |
|
| 21 | s3tests_boto3.functional.test_s3.test_lifecycle_expiration_header_and_tags_head | ERROR | ERROR | ok |
|
||||||
| 22 | s3tests_boto3.functional.test_s3.test_lifecycle_set_noncurrent | ERROR | ok | ok |
|
| 22 | s3tests_boto3.functional.test_s3.test_lifecycle_set_noncurrent | ERROR | ok | ok |
|
||||||
| 23 | s3tests_boto3.functional.test_s3.test_lifecycle_noncur_expiration | ERROR | ERROR | FAIL |
|
| 23 | s3tests_boto3.functional.test_s3.test_lifecycle_noncur_expiration | ERROR | ERROR | FAIL |
|
||||||
| 24 | s3tests_boto3.functional.test_s3.test_lifecycle_set_deletemarker | ERROR | ok | ok |
|
| 24 | s3tests_boto3.functional.test_s3.test_lifecycle_set_deletemarker | ERROR | ok | ok |
|
||||||
| 25 | s3tests_boto3.functional.test_s3.test_lifecycle_set_filter | ERROR | ok | ok |
|
| 25 | s3tests_boto3.functional.test_s3.test_lifecycle_set_filter | ERROR | ok | ok |
|
||||||
| 26 | s3tests_boto3.functional.test_s3.test_lifecycle_set_empty_filter | ERROR | ok | ok |
|
| 26 | s3tests_boto3.functional.test_s3.test_lifecycle_set_empty_filter | ERROR | ok | ok |
|
||||||
| 27 | s3tests_boto3.functional.test_s3.test_lifecycle_deletemarker_expiration | ERROR | ERROR | FAIL |
|
| 27 | s3tests_boto3.functional.test_s3.test_lifecycle_deletemarker_expiration | ERROR | ERROR | FAIL |
|
||||||
| 28 | s3tests_boto3.functional.test_s3.test_lifecycle_set_multipart | ERROR | ERROR | ok |
|
| 28 | s3tests_boto3.functional.test_s3.test_lifecycle_set_multipart | ERROR | ERROR | ok |
|
||||||
| 29 | s3tests_boto3.functional.test_s3.test_lifecycle_multipart_expiration | ERROR | ERROR | FAIL |
|
| 29 | s3tests_boto3.functional.test_s3.test_lifecycle_multipart_expiration | ERROR | ERROR | FAIL |
|
||||||
|
|
||||||
## Policy and replication
|
## Policy and replication
|
||||||
|
|
||||||
Compatibility: 0/7/20 out of 35
|
Compatibility: 0/7/20 out of 35
|
||||||
This group is not explicitly supported by s3-gw, but some tests may pass.
|
This group is not explicitly supported by s3-gw, but some tests may pass.
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|----|-------------------------------------------------------------------------------------|-------|-------|--------|
|
|-----|-------------------------------------------------------------------------------------|-------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_bucket_policy | ERROR | ok | ok |
|
| 1 | s3tests_boto3.functional.test_s3.test_bucket_policy | ERROR | ok | ok |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_bucketv2_policy | ERROR | ok | ok |
|
| 2 | s3tests_boto3.functional.test_s3.test_bucketv2_policy | ERROR | ok | ok |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_bucket_policy_acl | ERROR | ERROR | ok |
|
| 3 | s3tests_boto3.functional.test_s3.test_bucket_policy_acl | ERROR | ERROR | ok |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_bucketv2_policy_acl | ERROR | ERROR | ok |
|
| 4 | s3tests_boto3.functional.test_s3.test_bucketv2_policy_acl | ERROR | ERROR | ok |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_bucket_policy_different_tenant | ERROR | ERROR | ERROR |
|
| 5 | s3tests_boto3.functional.test_s3.test_bucket_policy_different_tenant | ERROR | ERROR | ERROR |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_bucketv2_policy_different_tenant | ERROR | ERROR | ERROR |
|
| 6 | s3tests_boto3.functional.test_s3.test_bucketv2_policy_different_tenant | ERROR | ERROR | ERROR |
|
||||||
| 7 | s3tests_boto3.functional.test_s3.test_bucket_policy_another_bucket | ERROR | ok | ERROR |
|
| 7 | s3tests_boto3.functional.test_s3.test_bucket_policy_another_bucket | ERROR | ok | ERROR |
|
||||||
| 8 | s3tests_boto3.functional.test_s3.test_bucketv2_policy_another_bucket | ERROR | ok | ERROR |
|
| 8 | s3tests_boto3.functional.test_s3.test_bucketv2_policy_another_bucket | ERROR | ok | ERROR |
|
||||||
| 9 | s3tests_boto3.functional.test_s3.test_bucket_policy_get_obj_existing_tag | ERROR | ERROR | ok |
|
| 9 | s3tests_boto3.functional.test_s3.test_bucket_policy_get_obj_existing_tag | ERROR | ERROR | ok |
|
||||||
| 10 | s3tests_boto3.functional.test_s3.test_bucket_policy_get_obj_tagging_existing_tag | ERROR | ERROR | ok |
|
| 10 | s3tests_boto3.functional.test_s3.test_bucket_policy_get_obj_tagging_existing_tag | ERROR | ERROR | ok |
|
||||||
| 11 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_tagging_existing_tag | ERROR | ERROR | ok |
|
| 11 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_tagging_existing_tag | ERROR | ERROR | ok |
|
||||||
| 12 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_copy_source | ERROR | FAIL | ok |
|
| 12 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_copy_source | ERROR | FAIL | ok |
|
||||||
| 13 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_copy_source_meta | ERROR | FAIL | ok |
|
| 13 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_copy_source_meta | ERROR | FAIL | ok |
|
||||||
| 14 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_acl | ERROR | ERROR | ok |
|
| 14 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_acl | ERROR | ERROR | ok |
|
||||||
| 15 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_grant | ERROR | ERROR | ok |
|
| 15 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_grant | ERROR | ERROR | ok |
|
||||||
| 16 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_enc | ERROR | FAIL | ERROR |
|
| 16 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_enc | ERROR | FAIL | ERROR |
|
||||||
| 17 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_request_obj_tag | ERROR | ERROR | FAIL |
|
| 17 | s3tests_boto3.functional.test_s3.test_bucket_policy_put_obj_request_obj_tag | ERROR | ERROR | FAIL |
|
||||||
| 18 | s3tests_boto3.functional.test_s3.test_bucket_policy_get_obj_acl_existing_tag | ERROR | ERROR | ok |
|
| 18 | s3tests_boto3.functional.test_s3.test_bucket_policy_get_obj_acl_existing_tag | ERROR | ERROR | ok |
|
||||||
| 19 | s3tests_boto3.functional.test_s3.test_user_policy | ERROR | ERROR | ERROR |
|
| 19 | s3tests_boto3.functional.test_s3.test_user_policy | ERROR | ERROR | ERROR |
|
||||||
| 20 | s3tests_boto3.functional.test_s3.test_get_bucket_policy_status | ERROR | ok | ERROR |
|
| 20 | s3tests_boto3.functional.test_s3.test_get_bucket_policy_status | ERROR | ok | ERROR |
|
||||||
| 21 | s3tests_boto3.functional.test_s3.test_get_public_acl_bucket_policy_status | ERROR | ERROR | ERROR |
|
| 21 | s3tests_boto3.functional.test_s3.test_get_public_acl_bucket_policy_status | ERROR | ERROR | ERROR |
|
||||||
| 22 | s3tests_boto3.functional.test_s3.test_get_authpublic_acl_bucket_policy_status | ERROR | ERROR | ERROR |
|
| 22 | s3tests_boto3.functional.test_s3.test_get_authpublic_acl_bucket_policy_status | ERROR | ERROR | ERROR |
|
||||||
| 23 | s3tests_boto3.functional.test_s3.test_get_publicpolicy_acl_bucket_policy_status | ERROR | FAIL | ERROR |
|
| 23 | s3tests_boto3.functional.test_s3.test_get_publicpolicy_acl_bucket_policy_status | ERROR | FAIL | ERROR |
|
||||||
| 24 | s3tests_boto3.functional.test_s3.test_get_nonpublicpolicy_acl_bucket_policy_status | ERROR | ok | ERROR |
|
| 24 | s3tests_boto3.functional.test_s3.test_get_nonpublicpolicy_acl_bucket_policy_status | ERROR | ok | ERROR |
|
||||||
| 25 | s3tests_boto3.functional.test_s3.test_get_nonpublicpolicy_deny_bucket_policy_status | ERROR | ERROR | ERROR |
|
| 25 | s3tests_boto3.functional.test_s3.test_get_nonpublicpolicy_deny_bucket_policy_status | ERROR | ERROR | ERROR |
|
||||||
| 26 | s3tests_boto3.functional.test_s3.test_get_default_public_block | ERROR | ERROR | ERROR |
|
| 26 | s3tests_boto3.functional.test_s3.test_get_default_public_block | ERROR | ERROR | ERROR |
|
||||||
| 27 | s3tests_boto3.functional.test_s3.test_put_public_block | ERROR | ERROR | ok |
|
| 27 | s3tests_boto3.functional.test_s3.test_put_public_block | ERROR | ERROR | ok |
|
||||||
| 28 | s3tests_boto3.functional.test_s3.test_block_public_put_bucket_acls | ERROR | ERROR | ok |
|
| 28 | s3tests_boto3.functional.test_s3.test_block_public_put_bucket_acls | ERROR | ERROR | ok |
|
||||||
| 29 | s3tests_boto3.functional.test_s3.test_block_public_object_canned_acls | ERROR | ERROR | ok |
|
| 29 | s3tests_boto3.functional.test_s3.test_block_public_object_canned_acls | ERROR | ERROR | ok |
|
||||||
| 30 | s3tests_boto3.functional.test_s3.test_block_public_policy | ERROR | ERROR | ok |
|
| 30 | s3tests_boto3.functional.test_s3.test_block_public_policy | ERROR | ERROR | ok |
|
||||||
| 31 | s3tests_boto3.functional.test_s3.test_ignore_public_acls | ERROR | ERROR | FAIL |
|
| 31 | s3tests_boto3.functional.test_s3.test_ignore_public_acls | ERROR | ERROR | FAIL |
|
||||||
| 32 | s3tests_boto3.functional.test_s3.test_get_tags_acl_public | ERROR | FAIL | ok |
|
| 32 | s3tests_boto3.functional.test_s3.test_get_tags_acl_public | ERROR | FAIL | ok |
|
||||||
| 33 | s3tests_boto3.functional.test_s3.test_put_tags_acl_public | ERROR | FAIL | ok |
|
| 33 | s3tests_boto3.functional.test_s3.test_put_tags_acl_public | ERROR | FAIL | ok |
|
||||||
| 34 | s3tests_boto3.functional.test_s3.test_delete_tags_obj_public | ERROR | ok | ok |
|
| 34 | s3tests_boto3.functional.test_s3.test_delete_tags_obj_public | ERROR | ok | ok |
|
||||||
| 35 | s3tests_boto3.functional.test_s3.test_multipart_upload_on_a_bucket_with_policy | ERROR | ERROR | ok |
|
| 35 | s3tests_boto3.functional.test_s3.test_multipart_upload_on_a_bucket_with_policy | ERROR | ERROR | ok |
|
||||||
|
|
||||||
## Others
|
## Others
|
||||||
|
|
||||||
Compatibility: 2/2/3 out of 6
|
Compatibility: 2/2/3 out of 6
|
||||||
|
|
||||||
| | Test | s3-gw | minio | aws s3 |
|
| | Test | s3-gw | minio | aws s3 |
|
||||||
|---|-------------------------------------------------------------|-------|-------|--------|
|
|-----|-------------------------------------------------------------|-------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_100_continue | FAIL | ERROR | ok |
|
| 1 | s3tests_boto3.functional.test_s3.test_100_continue | FAIL | ERROR | ok |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_account_usage | ERROR | ERROR | ERROR |
|
| 2 | s3tests_boto3.functional.test_s3.test_account_usage | ERROR | ERROR | ERROR |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_head_bucket_usage | ERROR | ERROR | ERROR |
|
| 3 | s3tests_boto3.functional.test_s3.test_head_bucket_usage | ERROR | ERROR | ERROR |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_logging_toggle | ERROR | ERROR | ERROR |
|
| 4 | s3tests_boto3.functional.test_s3.test_logging_toggle | ERROR | ERROR | ERROR |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_multi_object_delete | ok | ok | ok |
|
| 5 | s3tests_boto3.functional.test_s3.test_multi_object_delete | ok | ok | ok |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_multi_objectv2_delete | ok | ok | ok |
|
| 6 | s3tests_boto3.functional.test_s3.test_multi_objectv2_delete | ok | ok | ok |
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
# Tree service
|
# Tree service
|
||||||
|
|
||||||
To get objects' metadata and system information, the S3 GW makes requests to the Tree service.
|
To get objects' metadata and system information, the S3 GW makes requests to the Tree service.
|
||||||
This is a service in FrostFS storage that keeps different information as a tree structure.
|
This is a service in FrostFS storage that keeps different information as a tree structure.
|
||||||
|
|
||||||
Each node keeps one of the types of data as a set of **key-value pairs**:
|
Each node keeps one of the types of data as a set of **key-value pairs**:
|
||||||
* Bucket settings: lock configuration and versioning mode
|
* Bucket settings: lock configuration and versioning mode
|
||||||
* Bucket tagging
|
* Bucket tagging
|
||||||
* Object tagging
|
* Object tagging
|
||||||
* Object metadata: OID, name, creation time, system metadata
|
* Object metadata: OID, name, creation time, system metadata
|
||||||
* Object locking settings
|
* Object locking settings
|
||||||
* Active multipart upload info
|
* Active multipart upload info
|
||||||
|
|
||||||
Some data takes up a lot of memory, so we store it in FrostFS nodes as an object with payload.
|
Some data takes up a lot of memory, so we store it in FrostFS nodes as an object with payload.
|
||||||
But we keep these objects' metadata in the Tree service too:
|
But we keep these objects' metadata in the Tree service too:
|
||||||
* Notification configuration
|
* Notification configuration
|
||||||
* CORS
|
* CORS
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.11.2-0.20230315095236-9dc375346703
|
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.11.2-0.20230315095236-9dc375346703
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230316081442-bec77f280a85
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230329125804-552219b8e130
|
||||||
github.com/aws/aws-sdk-go v1.44.6
|
github.com/aws/aws-sdk-go v1.44.6
|
||||||
github.com/bluele/gcache v0.0.2
|
github.com/bluele/gcache v0.0.2
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -42,8 +42,8 @@ git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02f
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb/go.mod h1:nkR5gaGeez3Zv2SE7aceP0YwxG2FzIB5cGKpQO2vV2o=
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb/go.mod h1:nkR5gaGeez3Zv2SE7aceP0YwxG2FzIB5cGKpQO2vV2o=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230316081442-bec77f280a85 h1:TUcJ5A0C1gWi3bAhw4b+V+iVM3E9mbBOdJIWWkAPNxo=
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230329125804-552219b8e130 h1:V+3dGwEXwEvvSvseMKn8S6ZEMNhxBBYrcyx+F7VaptM=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230316081442-bec77f280a85/go.mod h1:23fUGlEv/ImaOi3vck6vZj0v0b4hteOhLLPnVWHSQeA=
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230329125804-552219b8e130/go.mod h1:23fUGlEv/ImaOi3vck6vZj0v0b4hteOhLLPnVWHSQeA=
|
||||||
git.frostfs.info/TrueCloudLab/hrw v1.2.0 h1:KvAES7xIqmQBGd2q8KanNosD9+4BhU/zqD5Kt5KSflk=
|
git.frostfs.info/TrueCloudLab/hrw v1.2.0 h1:KvAES7xIqmQBGd2q8KanNosD9+4BhU/zqD5Kt5KSflk=
|
||||||
git.frostfs.info/TrueCloudLab/hrw v1.2.0/go.mod h1:mq2sbvYfO+BB6iFZwYBkgC0yc6mJNx+qZi4jW918m+Y=
|
git.frostfs.info/TrueCloudLab/hrw v1.2.0/go.mod h1:mq2sbvYfO+BB6iFZwYBkgC0yc6mJNx+qZi4jW918m+Y=
|
||||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
||||||
|
|
File diff suppressed because it is too large
Load diff
311
pkg/service/tree/tree_client_grpc.go
Normal file
311
pkg/service/tree/tree_client_grpc.go
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
package tree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/services/tree"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetNodeByPathResponseInfoWrapper struct {
|
||||||
|
response *tree.GetNodeByPathResponse_Info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetNodeByPathResponseInfoWrapper) GetNodeID() uint64 {
|
||||||
|
return n.response.GetNodeId()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetNodeByPathResponseInfoWrapper) GetParentID() uint64 {
|
||||||
|
return n.response.GetParentId()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetNodeByPathResponseInfoWrapper) GetTimestamp() uint64 {
|
||||||
|
return n.response.GetTimestamp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetNodeByPathResponseInfoWrapper) GetMeta() []Meta {
|
||||||
|
res := make([]Meta, len(n.response.Meta))
|
||||||
|
for i, value := range n.response.Meta {
|
||||||
|
res[i] = value
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSubTreeResponseBodyWrapper struct {
|
||||||
|
response *tree.GetSubTreeResponse_Body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetSubTreeResponseBodyWrapper) GetNodeID() uint64 {
|
||||||
|
return n.response.GetNodeId()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetSubTreeResponseBodyWrapper) GetParentID() uint64 {
|
||||||
|
return n.response.GetParentId()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetSubTreeResponseBodyWrapper) GetTimestamp() uint64 {
|
||||||
|
return n.response.GetTimestamp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n GetSubTreeResponseBodyWrapper) GetMeta() []Meta {
|
||||||
|
res := make([]Meta, len(n.response.Meta))
|
||||||
|
for i, value := range n.response.Meta {
|
||||||
|
res[i] = value
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceClientGRPC struct {
|
||||||
|
key *keys.PrivateKey
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
service tree.TreeServiceClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTreeServiceClientGRPC(ctx context.Context, addr string, key *keys.PrivateKey, grpcOpts ...grpc.DialOption) (*ServiceClientGRPC, error) {
|
||||||
|
conn, err := grpc.Dial(addr, grpcOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("did not connect: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := tree.NewTreeServiceClient(conn)
|
||||||
|
if _, err = c.Healthcheck(ctx, &tree.HealthcheckRequest{}); err != nil {
|
||||||
|
return nil, fmt.Errorf("healthcheck: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ServiceClientGRPC{
|
||||||
|
key: key,
|
||||||
|
conn: conn,
|
||||||
|
service: c,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientGRPC) GetNodes(ctx context.Context, p *GetNodesParams) ([]NodeResponse, error) {
|
||||||
|
request := &tree.GetNodeByPathRequest{
|
||||||
|
Body: &tree.GetNodeByPathRequest_Body{
|
||||||
|
ContainerId: p.BktInfo.CID[:],
|
||||||
|
TreeId: p.TreeID,
|
||||||
|
Path: p.Path,
|
||||||
|
Attributes: p.Meta,
|
||||||
|
PathAttribute: FileNameKey,
|
||||||
|
LatestOnly: p.LatestOnly,
|
||||||
|
AllAttributes: p.AllAttrs,
|
||||||
|
BearerToken: getBearer(ctx, p.BktInfo),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.signRequest(request.Body, func(key, sign []byte) {
|
||||||
|
request.Signature = &tree.Signature{
|
||||||
|
Key: key,
|
||||||
|
Sign: sign,
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.service.GetNodeByPath(ctx, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, handleError("failed to get node by path", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make([]NodeResponse, len(resp.GetBody().GetNodes()))
|
||||||
|
for i, info := range resp.GetBody().GetNodes() {
|
||||||
|
res[i] = GetNodeByPathResponseInfoWrapper{info}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientGRPC) GetSubTree(ctx context.Context, bktInfo *data.BucketInfo, treeID string, rootID uint64, depth uint32) ([]NodeResponse, error) {
|
||||||
|
request := &tree.GetSubTreeRequest{
|
||||||
|
Body: &tree.GetSubTreeRequest_Body{
|
||||||
|
ContainerId: bktInfo.CID[:],
|
||||||
|
TreeId: treeID,
|
||||||
|
RootId: rootID,
|
||||||
|
Depth: depth,
|
||||||
|
BearerToken: getBearer(ctx, bktInfo),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.signRequest(request.Body, func(key, sign []byte) {
|
||||||
|
request.Signature = &tree.Signature{
|
||||||
|
Key: key,
|
||||||
|
Sign: sign,
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, err := c.service.GetSubTree(ctx, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, handleError("failed to get sub tree client", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var subtree []NodeResponse
|
||||||
|
for {
|
||||||
|
resp, err := cli.Recv()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, handleError("failed to get sub tree", err)
|
||||||
|
}
|
||||||
|
subtree = append(subtree, GetSubTreeResponseBodyWrapper{resp.Body})
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtree, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientGRPC) AddNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, parent uint64, meta map[string]string) (uint64, error) {
|
||||||
|
request := &tree.AddRequest{
|
||||||
|
Body: &tree.AddRequest_Body{
|
||||||
|
ContainerId: bktInfo.CID[:],
|
||||||
|
TreeId: treeID,
|
||||||
|
ParentId: parent,
|
||||||
|
Meta: metaToKV(meta),
|
||||||
|
BearerToken: getBearer(ctx, bktInfo),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := c.signRequest(request.Body, func(key, sign []byte) {
|
||||||
|
request.Signature = &tree.Signature{
|
||||||
|
Key: key,
|
||||||
|
Sign: sign,
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.service.Add(ctx, request)
|
||||||
|
if err != nil {
|
||||||
|
return 0, handleError("failed to add node", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.GetBody().GetNodeId(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientGRPC) AddNodeByPath(ctx context.Context, bktInfo *data.BucketInfo, treeID string, path []string, meta map[string]string) (uint64, error) {
|
||||||
|
request := &tree.AddByPathRequest{
|
||||||
|
Body: &tree.AddByPathRequest_Body{
|
||||||
|
ContainerId: bktInfo.CID[:],
|
||||||
|
TreeId: treeID,
|
||||||
|
Path: path,
|
||||||
|
Meta: metaToKV(meta),
|
||||||
|
PathAttribute: FileNameKey,
|
||||||
|
BearerToken: getBearer(ctx, bktInfo),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.signRequest(request.Body, func(key, sign []byte) {
|
||||||
|
request.Signature = &tree.Signature{
|
||||||
|
Key: key,
|
||||||
|
Sign: sign,
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.service.AddByPath(ctx, request)
|
||||||
|
if err != nil {
|
||||||
|
return 0, handleError("failed to add node by path", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := resp.GetBody()
|
||||||
|
if body == nil {
|
||||||
|
return 0, errors.New("nil body in tree service response")
|
||||||
|
} else if len(body.Nodes) == 0 {
|
||||||
|
return 0, errors.New("empty list of added nodes in tree service response")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first node is the leaf that we add, according to tree service docs.
|
||||||
|
return body.Nodes[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientGRPC) MoveNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, nodeID, parentID uint64, meta map[string]string) error {
|
||||||
|
request := &tree.MoveRequest{
|
||||||
|
Body: &tree.MoveRequest_Body{
|
||||||
|
ContainerId: bktInfo.CID[:],
|
||||||
|
TreeId: treeID,
|
||||||
|
NodeId: nodeID,
|
||||||
|
ParentId: parentID,
|
||||||
|
Meta: metaToKV(meta),
|
||||||
|
BearerToken: getBearer(ctx, bktInfo),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.signRequest(request.Body, func(key, sign []byte) {
|
||||||
|
request.Signature = &tree.Signature{
|
||||||
|
Key: key,
|
||||||
|
Sign: sign,
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := c.service.Move(ctx, request); err != nil {
|
||||||
|
return handleError("failed to move node", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientGRPC) RemoveNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, nodeID uint64) error {
|
||||||
|
request := &tree.RemoveRequest{
|
||||||
|
Body: &tree.RemoveRequest_Body{
|
||||||
|
ContainerId: bktInfo.CID[:],
|
||||||
|
TreeId: treeID,
|
||||||
|
NodeId: nodeID,
|
||||||
|
BearerToken: getBearer(ctx, bktInfo),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := c.signRequest(request.Body, func(key, sign []byte) {
|
||||||
|
request.Signature = &tree.Signature{
|
||||||
|
Key: key,
|
||||||
|
Sign: sign,
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := c.service.Remove(ctx, request); err != nil {
|
||||||
|
return handleError("failed to remove node", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func metaToKV(meta map[string]string) []*tree.KeyValue {
|
||||||
|
result := make([]*tree.KeyValue, 0, len(meta))
|
||||||
|
|
||||||
|
for key, value := range meta {
|
||||||
|
result = append(result, &tree.KeyValue{Key: key, Value: []byte(value)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBearer(ctx context.Context, bktInfo *data.BucketInfo) []byte {
|
||||||
|
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil {
|
||||||
|
if bd.Gate.BearerToken != nil {
|
||||||
|
if bktInfo.Owner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) {
|
||||||
|
return bd.Gate.BearerToken.Marshal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(msg string, err error) error {
|
||||||
|
if strings.Contains(err.Error(), "not found") {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNodeNotFound, err.Error())
|
||||||
|
} else if strings.Contains(err.Error(), "is denied by") {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNodeAccessDenied, err.Error())
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s: %w", msg, err)
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
/*REMOVE THIS AFTER SIGNATURE WILL BE AVAILABLE IN TREE CLIENT FROM FROSTFS NODE*/
|
/*REMOVE THIS AFTER SIGNATURE WILL BE AVAILABLE IN TREE CLIENT FROM FROSTFS NODE*/
|
||||||
package frostfs
|
package tree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto"
|
crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *TreeClient) signData(buf []byte, f func(key, sign []byte)) error {
|
func (c *ServiceClientGRPC) signData(buf []byte, f func(key, sign []byte)) error {
|
||||||
// crypto package should not be used outside of API libraries (see neofs-node#491).
|
// crypto package should not be used outside of API libraries (see neofs-node#491).
|
||||||
// For now tree service does not include into SDK Client nor SDK Pool, so there is no choice.
|
// For now tree service does not include into SDK Client nor SDK Pool, so there is no choice.
|
||||||
// When SDK library adopts Tree service client, this should be dropped.
|
// When SDK library adopts Tree service client, this should be dropped.
|
||||||
|
@ -19,7 +19,7 @@ func (c *TreeClient) signData(buf []byte, f func(key, sign []byte)) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TreeClient) signRequest(requestBody proto.Message, f func(key, sign []byte)) error {
|
func (c *ServiceClientGRPC) signRequest(requestBody proto.Message, f func(key, sign []byte)) error {
|
||||||
buf, err := proto.Marshal(requestBody)
|
buf, err := proto.Marshal(requestBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
35
pkg/service/tree/tree_client_grpc_test.go
Normal file
35
pkg/service/tree/tree_client_grpc_test.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package tree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleError(t *testing.T) {
|
||||||
|
defaultError := errors.New("default error")
|
||||||
|
for _, tc := range []struct {
|
||||||
|
err error
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
err: defaultError,
|
||||||
|
expectedError: defaultError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
err: errors.New("something not found"),
|
||||||
|
expectedError: layer.ErrNodeNotFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
err: errors.New("something is denied by some acl rule"),
|
||||||
|
expectedError: layer.ErrNodeAccessDenied,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
err := handleError("err message", tc.err)
|
||||||
|
require.True(t, errors.Is(err, tc.expectedError))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
393
pkg/service/tree/tree_client_in_memory.go
Normal file
393
pkg/service/tree/tree_client_in_memory.go
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
package tree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
type nodeMeta struct {
|
||||||
|
key string
|
||||||
|
value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nodeMeta) GetKey() string {
|
||||||
|
return m.key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m nodeMeta) GetValue() []byte {
|
||||||
|
return m.value
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeResponse struct {
|
||||||
|
meta []nodeMeta
|
||||||
|
nodeID uint64
|
||||||
|
parentID uint64
|
||||||
|
timestamp uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nodeResponse) GetNodeID() uint64 {
|
||||||
|
return n.nodeID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nodeResponse) GetParentID() uint64 {
|
||||||
|
return n.parentID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nodeResponse) GetTimestamp() uint64 {
|
||||||
|
return n.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nodeResponse) GetMeta() []Meta {
|
||||||
|
res := make([]Meta, len(n.meta))
|
||||||
|
for i, value := range n.meta {
|
||||||
|
res[i] = value
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nodeResponse) getValue(key string) string {
|
||||||
|
for _, value := range n.meta {
|
||||||
|
if value.key == key {
|
||||||
|
return string(value.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceClientMemory struct {
|
||||||
|
containers map[string]containerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerInfo struct {
|
||||||
|
bkt *data.BucketInfo
|
||||||
|
trees map[string]memoryTree
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryTree struct {
|
||||||
|
idCounter uint64
|
||||||
|
treeData *treeNodeMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
type treeNodeMemory struct {
|
||||||
|
data nodeResponse
|
||||||
|
parent *treeNodeMemory
|
||||||
|
children []*treeNodeMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *treeNodeMemory) getNode(nodeID uint64) *treeNodeMemory {
|
||||||
|
if t.data.nodeID == nodeID {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range t.children {
|
||||||
|
if node := child.getNode(nodeID); node != nil {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *memoryTree) getNodesByPath(path []string) []nodeResponse {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []nodeResponse
|
||||||
|
for _, child := range t.treeData.children {
|
||||||
|
res = child.listNodesByPath(res, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *treeNodeMemory) listNodesByPath(res []nodeResponse, path []string) []nodeResponse {
|
||||||
|
if len(path) == 0 || t.data.getValue(FileNameKey) != path[0] {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(path) == 1 {
|
||||||
|
return append(res, t.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ch := range t.children {
|
||||||
|
res = ch.listNodesByPath(res, path[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *memoryTree) createPathIfNotExist(parent *treeNodeMemory, path []string) *treeNodeMemory {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
|
var node *treeNodeMemory
|
||||||
|
for _, child := range parent.children {
|
||||||
|
if len(child.data.meta) == 1 && child.data.getValue(FileNameKey) == path[0] {
|
||||||
|
node = child
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if node == nil {
|
||||||
|
node = &treeNodeMemory{
|
||||||
|
data: nodeResponse{
|
||||||
|
meta: []nodeMeta{{key: FileNameKey, value: []byte(path[0])}},
|
||||||
|
nodeID: t.idCounter,
|
||||||
|
parentID: parent.data.nodeID,
|
||||||
|
timestamp: uint64(time.Now().UnixMicro()),
|
||||||
|
},
|
||||||
|
parent: parent,
|
||||||
|
}
|
||||||
|
t.idCounter++
|
||||||
|
parent.children = append(parent.children, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.createPathIfNotExist(node, path[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *treeNodeMemory) removeChild(nodeID uint64) {
|
||||||
|
ind := -1
|
||||||
|
for i, ch := range t.children {
|
||||||
|
if ch.data.nodeID == nodeID {
|
||||||
|
ind = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ind != -1 {
|
||||||
|
t.children = append(t.children[:ind], t.children[ind+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *treeNodeMemory) listNodes(res []NodeResponse, depth uint32) []NodeResponse {
|
||||||
|
res = append(res, t.data)
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ch := range t.children {
|
||||||
|
res = ch.listNodes(res, depth-1)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTreeServiceClientMemory() (*ServiceClientMemory, error) {
|
||||||
|
return &ServiceClientMemory{
|
||||||
|
containers: make(map[string]containerInfo),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientMemory) GetNodes(ctx context.Context, p *GetNodesParams) ([]NodeResponse, error) {
|
||||||
|
cnr, ok := c.containers[p.BktInfo.CID.EncodeToString()]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tr, ok := cnr.trees[p.TreeID]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res := tr.getNodesByPath(p.Path)
|
||||||
|
sort.Slice(res, func(i, j int) bool {
|
||||||
|
return res[i].timestamp < res[j].timestamp
|
||||||
|
})
|
||||||
|
|
||||||
|
if p.LatestOnly && len(res) != 0 {
|
||||||
|
res = res[len(res)-1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
res2 := make([]NodeResponse, len(res))
|
||||||
|
for i, n := range res {
|
||||||
|
res2[i] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
return res2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientMemory) GetSubTree(ctx context.Context, bktInfo *data.BucketInfo, treeID string, rootID uint64, depth uint32) ([]NodeResponse, error) {
|
||||||
|
cnr, ok := c.containers[bktInfo.CID.EncodeToString()]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tr, ok := cnr.trees[treeID]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
node := tr.treeData.getNode(rootID)
|
||||||
|
if node == nil {
|
||||||
|
return nil, ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.listNodes(nil, depth-1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContainerInfo(bktInfo *data.BucketInfo, treeID string) containerInfo {
|
||||||
|
return containerInfo{
|
||||||
|
bkt: bktInfo,
|
||||||
|
trees: map[string]memoryTree{
|
||||||
|
treeID: {
|
||||||
|
idCounter: 1,
|
||||||
|
treeData: &treeNodeMemory{
|
||||||
|
data: nodeResponse{
|
||||||
|
timestamp: uint64(time.Now().UnixMicro()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemoryTree() memoryTree {
|
||||||
|
return memoryTree{
|
||||||
|
idCounter: 1,
|
||||||
|
treeData: &treeNodeMemory{
|
||||||
|
data: nodeResponse{
|
||||||
|
timestamp: uint64(time.Now().UnixMicro()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientMemory) AddNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, parent uint64, meta map[string]string) (uint64, error) {
|
||||||
|
cnr, ok := c.containers[bktInfo.CID.EncodeToString()]
|
||||||
|
if !ok {
|
||||||
|
cnr = newContainerInfo(bktInfo, treeID)
|
||||||
|
c.containers[bktInfo.CID.EncodeToString()] = cnr
|
||||||
|
}
|
||||||
|
|
||||||
|
tr, ok := cnr.trees[treeID]
|
||||||
|
if !ok {
|
||||||
|
tr = newMemoryTree()
|
||||||
|
cnr.trees[treeID] = tr
|
||||||
|
}
|
||||||
|
|
||||||
|
parentNode := tr.treeData.getNode(parent)
|
||||||
|
if parentNode == nil {
|
||||||
|
return 0, ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
newID := tr.idCounter
|
||||||
|
tr.idCounter++
|
||||||
|
|
||||||
|
tn := &treeNodeMemory{
|
||||||
|
data: nodeResponse{
|
||||||
|
meta: metaToNodeMeta(meta),
|
||||||
|
nodeID: newID,
|
||||||
|
parentID: parent,
|
||||||
|
timestamp: uint64(time.Now().UnixMicro()),
|
||||||
|
},
|
||||||
|
parent: parentNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
parentNode.children = append(parentNode.children, tn)
|
||||||
|
cnr.trees[treeID] = tr
|
||||||
|
|
||||||
|
return newID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientMemory) AddNodeByPath(ctx context.Context, bktInfo *data.BucketInfo, treeID string, path []string, meta map[string]string) (uint64, error) {
|
||||||
|
cnr, ok := c.containers[bktInfo.CID.EncodeToString()]
|
||||||
|
if !ok {
|
||||||
|
cnr = newContainerInfo(bktInfo, treeID)
|
||||||
|
c.containers[bktInfo.CID.EncodeToString()] = cnr
|
||||||
|
}
|
||||||
|
|
||||||
|
tr, ok := cnr.trees[treeID]
|
||||||
|
if !ok {
|
||||||
|
tr = newMemoryTree()
|
||||||
|
cnr.trees[treeID] = tr
|
||||||
|
}
|
||||||
|
|
||||||
|
parentNode := tr.createPathIfNotExist(tr.treeData, path)
|
||||||
|
if parentNode == nil {
|
||||||
|
return 0, fmt.Errorf("create path '%s'", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
newID := tr.idCounter
|
||||||
|
tr.idCounter++
|
||||||
|
|
||||||
|
tn := &treeNodeMemory{
|
||||||
|
data: nodeResponse{
|
||||||
|
meta: metaToNodeMeta(meta),
|
||||||
|
nodeID: newID,
|
||||||
|
parentID: parentNode.data.nodeID,
|
||||||
|
timestamp: uint64(time.Now().UnixMicro()),
|
||||||
|
},
|
||||||
|
parent: parentNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
parentNode.children = append(parentNode.children, tn)
|
||||||
|
cnr.trees[treeID] = tr
|
||||||
|
|
||||||
|
return newID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientMemory) MoveNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, nodeID, parentID uint64, meta map[string]string) error {
|
||||||
|
cnr, ok := c.containers[bktInfo.CID.EncodeToString()]
|
||||||
|
if !ok {
|
||||||
|
return ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
tr, ok := cnr.trees[treeID]
|
||||||
|
if !ok {
|
||||||
|
return ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
node := tr.treeData.getNode(nodeID)
|
||||||
|
if node == nil {
|
||||||
|
return ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
newParent := tr.treeData.getNode(parentID)
|
||||||
|
if newParent == nil {
|
||||||
|
return ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
node.data.meta = metaToNodeMeta(meta)
|
||||||
|
node.data.parentID = parentID
|
||||||
|
|
||||||
|
newParent.children = append(newParent.children, node)
|
||||||
|
node.parent.removeChild(nodeID)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServiceClientMemory) RemoveNode(ctx context.Context, bktInfo *data.BucketInfo, treeID string, nodeID uint64) error {
|
||||||
|
cnr, ok := c.containers[bktInfo.CID.EncodeToString()]
|
||||||
|
if !ok {
|
||||||
|
return ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
tr, ok := cnr.trees[treeID]
|
||||||
|
if !ok {
|
||||||
|
return ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
node := tr.treeData.getNode(nodeID)
|
||||||
|
if node == nil {
|
||||||
|
return ErrNodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
node.parent.removeChild(nodeID)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func metaToNodeMeta(m map[string]string) []nodeMeta {
|
||||||
|
result := make([]nodeMeta, 0, len(m))
|
||||||
|
|
||||||
|
for key, value := range m {
|
||||||
|
result = append(result, nodeMeta{key: key, value: []byte(value)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
package frostfs
|
package tree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
|
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,28 +97,73 @@ func TestLockConfigurationEncoding(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleError(t *testing.T) {
|
func TestTreeServiceSettings(t *testing.T) {
|
||||||
defaultError := errors.New("default error")
|
ctx := context.Background()
|
||||||
for _, tc := range []struct {
|
|
||||||
err error
|
memCli, err := NewTreeServiceClientMemory()
|
||||||
expectedError error
|
require.NoError(t, err)
|
||||||
}{
|
treeService := NewTree(memCli)
|
||||||
{
|
|
||||||
err: defaultError,
|
bktInfo := &data.BucketInfo{
|
||||||
expectedError: defaultError,
|
CID: cidtest.ID(),
|
||||||
},
|
|
||||||
{
|
|
||||||
err: errors.New("something not found"),
|
|
||||||
expectedError: layer.ErrNodeNotFound,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
err: errors.New("something is denied by some acl rule"),
|
|
||||||
expectedError: layer.ErrNodeAccessDenied,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run("", func(t *testing.T) {
|
|
||||||
err := handleError("err message", tc.err)
|
|
||||||
require.True(t, errors.Is(err, tc.expectedError))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings := &data.BucketSettings{
|
||||||
|
Versioning: "Versioning",
|
||||||
|
LockConfiguration: &data.ObjectLockConfiguration{
|
||||||
|
ObjectLockEnabled: "Enabled",
|
||||||
|
Rule: &data.ObjectLockRule{
|
||||||
|
DefaultRetention: &data.DefaultRetention{
|
||||||
|
Days: 1,
|
||||||
|
Mode: "mode",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = treeService.PutSettingsNode(ctx, bktInfo, settings)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storedSettings, err := treeService.GetSettingsNode(ctx, bktInfo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, settings, storedSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeServiceAddVersion(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
memCli, err := NewTreeServiceClientMemory()
|
||||||
|
require.NoError(t, err)
|
||||||
|
treeService := NewTree(memCli)
|
||||||
|
|
||||||
|
bktInfo := &data.BucketInfo{
|
||||||
|
CID: cidtest.ID(),
|
||||||
|
}
|
||||||
|
|
||||||
|
version := &data.NodeVersion{
|
||||||
|
BaseNodeVersion: data.BaseNodeVersion{
|
||||||
|
OID: oidtest.ID(),
|
||||||
|
Size: 10,
|
||||||
|
ETag: "etag",
|
||||||
|
FilePath: "path/to/version",
|
||||||
|
},
|
||||||
|
IsUnversioned: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeID, err := treeService.AddVersion(ctx, bktInfo, version)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storedNode, err := treeService.GetUnversioned(ctx, bktInfo, "path/to/version")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, nodeID, storedNode.ID)
|
||||||
|
require.Equal(t, version.BaseNodeVersion.Size, storedNode.Size)
|
||||||
|
require.Equal(t, version.BaseNodeVersion.ETag, storedNode.ETag)
|
||||||
|
require.Equal(t, version.BaseNodeVersion.ETag, storedNode.ETag)
|
||||||
|
require.Equal(t, version.BaseNodeVersion.FilePath, storedNode.FilePath)
|
||||||
|
require.Equal(t, version.BaseNodeVersion.OID, storedNode.OID)
|
||||||
|
|
||||||
|
versions, err := treeService.GetVersions(ctx, bktInfo, "path/to/version")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, versions, 1)
|
||||||
|
require.Equal(t, storedNode, versions[0])
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ fi
|
||||||
|
|
||||||
RESULT_FILE=docs/s3_test_results.md
|
RESULT_FILE=docs/s3_test_results.md
|
||||||
|
|
||||||
get_adjusted_result () {
|
get_adjusted_result() {
|
||||||
local OLD_RESULT=$1
|
local OLD_RESULT=$1
|
||||||
local NEW_RESULT=$2
|
local NEW_RESULT=$2
|
||||||
local OLD_RESULT_LEN=${#OLD_RESULT}
|
local OLD_RESULT_LEN=${#OLD_RESULT}
|
||||||
|
@ -23,13 +23,11 @@ get_adjusted_result () {
|
||||||
printf "%s%*s" "$NEW_RESULT" $ADDITIONAL_SPACES ''
|
printf "%s%*s" "$NEW_RESULT" $ADDITIONAL_SPACES ''
|
||||||
}
|
}
|
||||||
|
|
||||||
while read -r line;
|
while read -r line; do
|
||||||
do
|
|
||||||
RES_LINE=$(echo "$line" | sed -nE '/^s3tests_boto3/p')
|
RES_LINE=$(echo "$line" | sed -nE '/^s3tests_boto3/p')
|
||||||
if [ -n "$RES_LINE" ]
|
if [ -n "$RES_LINE" ]; then
|
||||||
then
|
TEST=${RES_LINE%%[[:space:]]*}
|
||||||
TEST=$(echo "$RES_LINE" | sed -e 's/[[:space:]]*\.\.\..*//')
|
RESULT=${RES_LINE##*[[:space:]]}
|
||||||
RESULT=$(echo "$RES_LINE" | sed -e 's/^.*\.\.\.[[:space:]]*//')
|
|
||||||
|
|
||||||
# beautify trailing spaces
|
# beautify trailing spaces
|
||||||
OLD_RESULT_S3GW=$(sed -n "s/^.*${TEST}[[:space:]]*|[[:space:]]\(.*\)[[:space:]]|.*|.*|$/\1/p" "$RESULT_FILE" | head -1)
|
OLD_RESULT_S3GW=$(sed -n "s/^.*${TEST}[[:space:]]*|[[:space:]]\(.*\)[[:space:]]|.*|.*|$/\1/p" "$RESULT_FILE" | head -1)
|
||||||
|
@ -49,4 +47,4 @@ do
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
done < "$INPUT_FILE"
|
done <"$INPUT_FILE"
|
||||||
|
|
Loading…
Reference in a new issue