Compare commits
4 commits
Author | SHA1 | Date | |
---|---|---|---|
8e950d1cf4 | |||
2faadc0057 | |||
b1cc53056e | |||
901893a6fc |
10 changed files with 58 additions and 16 deletions
|
@ -16,7 +16,7 @@ jobs:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.23.6'
|
||||||
|
|
||||||
- name: Install govulncheck
|
- name: Install govulncheck
|
||||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -4,15 +4,23 @@ This document outlines major changes between releases.
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.32.8] - 2025-02-11
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Return 404 instead of 500 when object is missing in object storage and available in the tree (#626)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `tree_stream_timeout` configuration parameter (#627)
|
||||||
|
|
||||||
## [0.32.7] - 2025-02-06
|
## [0.32.7] - 2025-02-06
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Corrct passing copies number during multipart upload (#623)
|
- Correct passing copies number during multipart upload (#623)
|
||||||
|
|
||||||
## [0.32.6] - 2025-02-05
|
## [0.32.6] - 2025-02-05
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Connection leak when `feature.tree_pool_netmap_support` is enabled.
|
- Connection leak when `feature.tree_pool_netmap_support` is enabled (#622)
|
||||||
|
|
||||||
## [0.32.5] - 2025-02-04
|
## [0.32.5] - 2025-02-04
|
||||||
|
|
||||||
|
@ -436,5 +444,6 @@ To see CHANGELOG for older versions, refer to https://github.com/nspcc-dev/neofs
|
||||||
[0.32.4]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.3...v0.32.4
|
[0.32.4]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.3...v0.32.4
|
||||||
[0.32.5]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.4...v0.32.5
|
[0.32.5]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.4...v0.32.5
|
||||||
[0.32.6]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.5...v0.32.6
|
[0.32.6]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.5...v0.32.6
|
||||||
[0.32.6]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.6...v0.32.7
|
[0.32.7]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.6...v0.32.7
|
||||||
[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.7...master
|
[0.32.8]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.7...v0.32.8
|
||||||
|
[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/compare/v0.32.8...master
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
v0.32.7
|
v0.32.8
|
||||||
|
|
|
@ -197,6 +197,33 @@ func TestGetObject(t *testing.T) {
|
||||||
getObjectAssertS3Error(hc, bktName, objName, emptyVersion, apierr.ErrNoSuchKey)
|
getObjectAssertS3Error(hc, bktName, objName, emptyVersion, apierr.ErrNoSuchKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetDeletedObject(t *testing.T) {
|
||||||
|
hc := prepareHandlerContextWithMinCache(t)
|
||||||
|
bktName, objName := "bucket", "obj"
|
||||||
|
bktInfo, objInfo := createVersionedBucketAndObject(hc.t, hc, bktName, objName)
|
||||||
|
|
||||||
|
putObject(hc, bktName, objName)
|
||||||
|
|
||||||
|
checkFound(hc.t, hc, bktName, objName, objInfo.VersionID())
|
||||||
|
checkFound(hc.t, hc, bktName, objName, emptyVersion)
|
||||||
|
|
||||||
|
addr := getAddressOfLastVersion(hc, bktInfo, objName)
|
||||||
|
t.Run("not found error", func(_ *testing.T) {
|
||||||
|
hc.tp.SetObjectError(addr, &apistatus.ObjectNotFound{})
|
||||||
|
hc.tp.SetObjectError(objInfo.Address(), &apistatus.ObjectNotFound{})
|
||||||
|
|
||||||
|
getObjectAssertS3Error(hc, bktName, objName, objInfo.VersionID(), apierr.ErrNoSuchVersion)
|
||||||
|
getObjectAssertS3Error(hc, bktName, objName, emptyVersion, apierr.ErrNoSuchKey)
|
||||||
|
})
|
||||||
|
t.Run("already removed error", func(_ *testing.T) {
|
||||||
|
hc.tp.SetObjectError(addr, &apistatus.ObjectAlreadyRemoved{})
|
||||||
|
hc.tp.SetObjectError(objInfo.Address(), &apistatus.ObjectAlreadyRemoved{})
|
||||||
|
|
||||||
|
getObjectAssertS3Error(hc, bktName, objName, objInfo.VersionID(), apierr.ErrNoSuchVersion)
|
||||||
|
getObjectAssertS3Error(hc, bktName, objName, emptyVersion, apierr.ErrNoSuchKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetObjectEnabledMD5(t *testing.T) {
|
func TestGetObjectEnabledMD5(t *testing.T) {
|
||||||
hc := prepareHandlerContext(t)
|
hc := prepareHandlerContext(t)
|
||||||
bktName, objName := "bucket", "obj"
|
bktName, objName := "bucket", "obj"
|
||||||
|
|
|
@ -410,7 +410,7 @@ func (n *Layer) headLastVersionIfNotDeleted(ctx context.Context, bkt *data.Bucke
|
||||||
|
|
||||||
meta, err := n.objectHead(ctx, bkt, node.OID)
|
meta, err := n.objectHead(ctx, bkt, node.OID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if client.IsErrObjectNotFound(err) {
|
if client.IsErrObjectNotFound(err) || client.IsErrObjectAlreadyRemoved(err) {
|
||||||
return nil, fmt.Errorf("%w: %s; %s", apierr.GetAPIError(apierr.ErrNoSuchKey), err.Error(), node.OID.EncodeToString())
|
return nil, fmt.Errorf("%w: %s; %s", apierr.GetAPIError(apierr.ErrNoSuchKey), err.Error(), node.OID.EncodeToString())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -467,7 +467,7 @@ func (n *Layer) headVersion(ctx context.Context, bkt *data.BucketInfo, p *HeadOb
|
||||||
|
|
||||||
meta, err := n.objectHead(ctx, bkt, foundVersion.OID)
|
meta, err := n.objectHead(ctx, bkt, foundVersion.OID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if client.IsErrObjectNotFound(err) {
|
if client.IsErrObjectNotFound(err) || client.IsErrObjectAlreadyRemoved(err) {
|
||||||
return nil, fmt.Errorf("%w: %s", apierr.GetAPIError(apierr.ErrNoSuchVersion), err.Error())
|
return nil, fmt.Errorf("%w: %s", apierr.GetAPIError(apierr.ErrNoSuchVersion), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -779,9 +779,8 @@ func (a *App) initPools(ctx context.Context) {
|
||||||
prm.SetNodeDialTimeout(connTimeout)
|
prm.SetNodeDialTimeout(connTimeout)
|
||||||
prmTree.SetNodeDialTimeout(connTimeout)
|
prmTree.SetNodeDialTimeout(connTimeout)
|
||||||
|
|
||||||
streamTimeout := fetchStreamTimeout(a.cfg)
|
prm.SetNodeStreamTimeout(fetchStreamTimeout(a.cfg, cfgStreamTimeout))
|
||||||
prm.SetNodeStreamTimeout(streamTimeout)
|
prmTree.SetNodeStreamTimeout(fetchStreamTimeout(a.cfg, cfgTreeStreamTimeout))
|
||||||
prmTree.SetNodeStreamTimeout(streamTimeout)
|
|
||||||
|
|
||||||
healthCheckTimeout := fetchHealthCheckTimeout(a.cfg)
|
healthCheckTimeout := fetchHealthCheckTimeout(a.cfg)
|
||||||
prm.SetHealthcheckTimeout(healthCheckTimeout)
|
prm.SetHealthcheckTimeout(healthCheckTimeout)
|
||||||
|
|
|
@ -122,6 +122,7 @@ const ( // Settings.
|
||||||
// Pool config.
|
// Pool config.
|
||||||
cfgConnectTimeout = "connect_timeout"
|
cfgConnectTimeout = "connect_timeout"
|
||||||
cfgStreamTimeout = "stream_timeout"
|
cfgStreamTimeout = "stream_timeout"
|
||||||
|
cfgTreeStreamTimeout = "tree_stream_timeout"
|
||||||
cfgHealthcheckTimeout = "healthcheck_timeout"
|
cfgHealthcheckTimeout = "healthcheck_timeout"
|
||||||
cfgRebalanceInterval = "rebalance_interval"
|
cfgRebalanceInterval = "rebalance_interval"
|
||||||
cfgPoolErrorThreshold = "pool_error_threshold"
|
cfgPoolErrorThreshold = "pool_error_threshold"
|
||||||
|
@ -319,8 +320,8 @@ func fetchReconnectInterval(cfg *viper.Viper) time.Duration {
|
||||||
return reconnect
|
return reconnect
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchStreamTimeout(cfg *viper.Viper) time.Duration {
|
func fetchStreamTimeout(cfg *viper.Viper, cfgEntry string) time.Duration {
|
||||||
streamTimeout := cfg.GetDuration(cfgStreamTimeout)
|
streamTimeout := cfg.GetDuration(cfgEntry)
|
||||||
if streamTimeout <= 0 {
|
if streamTimeout <= 0 {
|
||||||
streamTimeout = defaultStreamTimeout
|
streamTimeout = defaultStreamTimeout
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,8 +80,10 @@ S3_GW_PROMETHEUS_ADDRESS=localhost:8086
|
||||||
|
|
||||||
# Timeout to connect to a node
|
# Timeout to connect to a node
|
||||||
S3_GW_CONNECT_TIMEOUT=10s
|
S3_GW_CONNECT_TIMEOUT=10s
|
||||||
# Timeout for individual operations in streaming RPC.
|
# Timeout for individual operations in object pool streaming RPC.
|
||||||
S3_GW_STREAM_TIMEOUT=10s
|
S3_GW_STREAM_TIMEOUT=10s
|
||||||
|
# Timeout for individual operations in tree pool streaming RPC.
|
||||||
|
S3_GW_TREE_STREAM_TIMEOUT=10s
|
||||||
# Timeout to check node health during rebalance.
|
# Timeout to check node health during rebalance.
|
||||||
S3_GW_HEALTHCHECK_TIMEOUT=15s
|
S3_GW_HEALTHCHECK_TIMEOUT=15s
|
||||||
# Interval to check node health
|
# Interval to check node health
|
||||||
|
|
|
@ -100,8 +100,10 @@ tracing:
|
||||||
|
|
||||||
# Timeout to connect to a node
|
# Timeout to connect to a node
|
||||||
connect_timeout: 10s
|
connect_timeout: 10s
|
||||||
# Timeout for individual operations in streaming RPC.
|
# Timeout for individual operations in object pool streaming RPC.
|
||||||
stream_timeout: 10s
|
stream_timeout: 10s
|
||||||
|
# Timeout for individual operations in tree pool streaming RPC.
|
||||||
|
tree_stream_timeout: 10s
|
||||||
# Timeout to check node health during rebalance
|
# Timeout to check node health during rebalance
|
||||||
healthcheck_timeout: 15s
|
healthcheck_timeout: 15s
|
||||||
# Interval to check node health
|
# Interval to check node health
|
||||||
|
|
|
@ -213,6 +213,7 @@ resolve_order:
|
||||||
|
|
||||||
connect_timeout: 10s
|
connect_timeout: 10s
|
||||||
stream_timeout: 10s
|
stream_timeout: 10s
|
||||||
|
tree_stream_timeout: 10s
|
||||||
healthcheck_timeout: 15s
|
healthcheck_timeout: 15s
|
||||||
rebalance_interval: 60s
|
rebalance_interval: 60s
|
||||||
pool_error_threshold: 100
|
pool_error_threshold: 100
|
||||||
|
@ -235,7 +236,8 @@ source_ip_header: "Source-Ip"
|
||||||
| `rpc_endpoint` | `string` | no | | The address of the RPC host to which the gateway connects to resolve bucket names and interact with frostfs contracts (required to use the `nns` resolver and `frostfsid` contract). |
|
| `rpc_endpoint` | `string` | no | | The address of the RPC host to which the gateway connects to resolve bucket names and interact with frostfs contracts (required to use the `nns` resolver and `frostfsid` contract). |
|
||||||
| `resolve_order` | `[]string` | yes | `[dns]` | Order of bucket name resolvers to use. Available resolvers: `dns`, `nns`. |
|
| `resolve_order` | `[]string` | yes | `[dns]` | Order of bucket name resolvers to use. Available resolvers: `dns`, `nns`. |
|
||||||
| `connect_timeout` | `duration` | no | `10s` | Timeout to connect to a node. |
|
| `connect_timeout` | `duration` | no | `10s` | Timeout to connect to a node. |
|
||||||
| `stream_timeout` | `duration` | no | `10s` | Timeout for individual operations in streaming RPC. |
|
| `stream_timeout` | `duration` | no | `10s` | Timeout for individual operations in object pool streaming RPC. |
|
||||||
|
| `tree_stream_timeout` | `duration` | no | `10s` | Timeout for individual operations in tree pool streaming RPC. |
|
||||||
| `healthcheck_timeout` | `duration` | no | `15s` | Timeout to check node health during rebalance. |
|
| `healthcheck_timeout` | `duration` | no | `15s` | Timeout to check node health during rebalance. |
|
||||||
| `rebalance_interval` | `duration` | no | `60s` | Interval to check node health. |
|
| `rebalance_interval` | `duration` | no | `60s` | Interval to check node health. |
|
||||||
| `pool_error_threshold` | `uint32` | no | `100` | The number of errors on connection after which node is considered as unhealthy. |
|
| `pool_error_threshold` | `uint32` | no | `100` | The number of errors on connection after which node is considered as unhealthy. |
|
||||||
|
|
Loading…
Add table
Reference in a new issue