---
{.github => .forgejo}/CODEOWNERS | 0
{.github => .forgejo}/ISSUE_TEMPLATE/bug_report.md | 0
{.github => .forgejo}/ISSUE_TEMPLATE/config.yml | 0
{.github => .forgejo}/ISSUE_TEMPLATE/feature_request.md | 0
{.github => .forgejo}/logo.svg | 0
{.github => .forgejo}/workflows/builds.yml | 0
{.github => .forgejo}/workflows/codeql-analysis.yml | 0
{.github => .forgejo}/workflows/dco.yml | 0
{.github => .forgejo}/workflows/tests.yml | 0
README.md | 2 +-
10 files changed, 1 insertion(+), 1 deletion(-)
rename {.github => .forgejo}/CODEOWNERS (100%)
rename {.github => .forgejo}/ISSUE_TEMPLATE/bug_report.md (100%)
rename {.github => .forgejo}/ISSUE_TEMPLATE/config.yml (100%)
rename {.github => .forgejo}/ISSUE_TEMPLATE/feature_request.md (100%)
rename {.github => .forgejo}/logo.svg (100%)
rename {.github => .forgejo}/workflows/builds.yml (100%)
rename {.github => .forgejo}/workflows/codeql-analysis.yml (100%)
rename {.github => .forgejo}/workflows/dco.yml (100%)
rename {.github => .forgejo}/workflows/tests.yml (100%)
diff --git a/.github/CODEOWNERS b/.forgejo/CODEOWNERS
similarity index 100%
rename from .github/CODEOWNERS
rename to .forgejo/CODEOWNERS
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.forgejo/ISSUE_TEMPLATE/bug_report.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/bug_report.md
rename to .forgejo/ISSUE_TEMPLATE/bug_report.md
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.forgejo/ISSUE_TEMPLATE/config.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/config.yml
rename to .forgejo/ISSUE_TEMPLATE/config.yml
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.forgejo/ISSUE_TEMPLATE/feature_request.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/feature_request.md
rename to .forgejo/ISSUE_TEMPLATE/feature_request.md
diff --git a/.github/logo.svg b/.forgejo/logo.svg
similarity index 100%
rename from .github/logo.svg
rename to .forgejo/logo.svg
diff --git a/.github/workflows/builds.yml b/.forgejo/workflows/builds.yml
similarity index 100%
rename from .github/workflows/builds.yml
rename to .forgejo/workflows/builds.yml
diff --git a/.github/workflows/codeql-analysis.yml b/.forgejo/workflows/codeql-analysis.yml
similarity index 100%
rename from .github/workflows/codeql-analysis.yml
rename to .forgejo/workflows/codeql-analysis.yml
diff --git a/.github/workflows/dco.yml b/.forgejo/workflows/dco.yml
similarity index 100%
rename from .github/workflows/dco.yml
rename to .forgejo/workflows/dco.yml
diff --git a/.github/workflows/tests.yml b/.forgejo/workflows/tests.yml
similarity index 100%
rename from .github/workflows/tests.yml
rename to .forgejo/workflows/tests.yml
diff --git a/README.md b/README.md
index 1e050af..e2b697a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
FrostFS is a decentralized distributed object storage integrated with the NEO Blockchain.
From 01b9df83e6188a06cccc031e7d042a752a2ec6ce Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 31 May 2023 16:06:00 +0300
Subject: [PATCH 003/161] [#54] Update actions for forgejo ci runner
Signed-off-by: Alex Vanin
---
.forgejo/workflows/builds.yml | 73 +++++--------------------
.forgejo/workflows/codeql-analysis.yml | 67 -----------------------
.forgejo/workflows/dco.yml | 34 ++++++------
.forgejo/workflows/tests.yml | 76 +++++---------------------
4 files changed, 44 insertions(+), 206 deletions(-)
delete mode 100644 .forgejo/workflows/codeql-analysis.yml
diff --git a/.forgejo/workflows/builds.yml b/.forgejo/workflows/builds.yml
index 64e2e5e..3df5081 100644
--- a/.forgejo/workflows/builds.yml
+++ b/.forgejo/workflows/builds.yml
@@ -1,67 +1,20 @@
-name: Builds
-
-on:
- pull_request:
- branches:
- - master
- - 'support/*'
- types: [opened, synchronize]
- paths-ignore:
- - '**/*.md'
+on: [pull_request]
jobs:
- build_cli:
- name: Build CLI
- runs-on: ubuntu-20.04
-
+ builds:
+ name: Builds
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go_versions: [ '1.19', '1.20' ]
+ fail-fast: false
steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0
-
- - name: Setup Go
- uses: actions/setup-go@v2
- with:
- go-version: 1.19
-
- - name: Restore Go modules from cache
- uses: actions/cache@v2
- with:
- path: /home/runner/go/pkg/mod
- key: deps-${{ hashFiles('go.sum') }}
-
- - name: Update Go modules
- run: make dep
-
- - name: Build CLI
- run: make
-
- - name: Check version
- run: if [[ $(make version) == *"dirty"* ]]; then exit 1; fi
-
- build_image:
- needs: build_cli
- name: Build Docker image
- runs-on: ubuntu-20.04
-
- steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0
+ - uses: actions/checkout@v3
- name: Set up Go
- uses: actions/setup-go@v2
+ uses: actions/setup-go@v3
with:
- go-version: 1.19
+ go-version: '${{ matrix.go_versions }}'
- - name: Restore Go modules from cache
- uses: actions/cache@v2
- with:
- path: /home/runner/go/pkg/mod
- key: deps-${{ hashFiles('go.sum') }}
-
- - name: Update Go modules
- run: make dep
-
- - name: Build Docker image
- run: make image
+ - name: Build binary
+ run: make
diff --git a/.forgejo/workflows/codeql-analysis.yml b/.forgejo/workflows/codeql-analysis.yml
deleted file mode 100644
index 8598516..0000000
--- a/.forgejo/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches: [ master, 'support/*' ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ master, 'support/*' ]
- schedule:
- - cron: '35 8 * * 1'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'go' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
- # Learn more:
- # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v2
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v2
-
- # ℹ️ Command-line programs to run using the OS shell.
- # 📚 https://git.io/JvXDl
-
- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml
index 397e961..b1ccd0e 100644
--- a/.forgejo/workflows/dco.yml
+++ b/.forgejo/workflows/dco.yml
@@ -1,22 +1,20 @@
-name: DCO check
-
-on:
- pull_request:
- branches:
- - master
- - 'support/*'
+on: [pull_request]
jobs:
- commits_check_job:
+ dco:
+ name: DCO
runs-on: ubuntu-latest
- name: Commits Check
steps:
- - name: Get PR Commits
- id: 'get-pr-commits'
- uses: tim-actions/get-pr-commits@master
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- - name: DCO Check
- uses: tim-actions/dco@master
- with:
- commits: ${{ steps.get-pr-commits.outputs.commits }}
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Setup Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: '1.20'
+
+ - name: Run commit format checker
+ uses: https://git.alexvan.in/alexvanin/dco-go@v1
+ with:
+ from: adb95642d
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index e4c210f..f64b816 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -1,86 +1,40 @@
-name: Tests
-
-on:
- pull_request:
- branches:
- - master
- - 'support/*'
- types: [opened, synchronize]
- paths-ignore:
- - '**/*.md'
+on: [pull_request]
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
-
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
+
+ - name: Sync tree service
+ run: make sync-tree
+
- name: golangci-lint
- uses: golangci/golangci-lint-action@v2
+ uses: https://github.com/golangci/golangci-lint-action@v2
with:
version: latest
- cover:
- name: Coverage
- runs-on: ubuntu-20.04
-
- env:
- CGO_ENABLED: 1
- steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0
-
- - name: Set up Go
- uses: actions/setup-go@v2
- with:
- go-version: 1.19
-
- - name: Restore Go modules from cache
- uses: actions/cache@v2
- with:
- path: /home/runner/go/pkg/mod
- key: deps-${{ hashFiles('go.sum') }}
-
- - name: Update Go modules
- run: make dep
-
- - name: Test and write coverage profile
- run: make cover
-
- - name: Upload coverage results to Codecov
- uses: codecov/codecov-action@v1
- with:
- fail_ci_if_error: false
- path_to_write_report: ./coverage.txt
- verbose: true
-
tests:
name: Tests
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.18', '1.19' ]
+ go_versions: [ '1.19', '1.20' ]
fail-fast: false
steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0
+ - uses: actions/checkout@v3
- name: Set up Go
- uses: actions/setup-go@v2
+ uses: actions/setup-go@v3
with:
go-version: '${{ matrix.go_versions }}'
-
- - name: Restore Go modules from cache
- uses: actions/cache@v2
- with:
- path: /home/runner/go/pkg/mod
- key: deps-${{ hashFiles('go.sum') }}
+
+ - name: Sync tree service
+ run: make sync-tree
- name: Update Go modules
run: make dep
- name: Run tests
- run: make test
+ run: make test
\ No newline at end of file
From f17f6747c44a6493a478c23768dce052429f9a0b Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 31 May 2023 16:34:04 +0300
Subject: [PATCH 004/161] [#54] Fix linter warnings
Signed-off-by: Alex Vanin
---
app.go | 42 ++++++++++++++++++-------------------
settings.go | 6 +-----
tokens/bearer-token.go | 12 ++++++-----
tokens/bearer-token_test.go | 4 ++--
4 files changed, 31 insertions(+), 33 deletions(-)
diff --git a/app.go b/app.go
index 6a52a55..f43d187 100644
--- a/app.go
+++ b/app.go
@@ -494,41 +494,41 @@ func (a *app) configureRouter(uploadRoutes *uploader.Uploader, downloadRoutes *d
a.webServer.Handler = r.Handler
}
-func (a *app) logger(req fasthttp.RequestHandler) fasthttp.RequestHandler {
- return func(ctx *fasthttp.RequestCtx) {
- a.log.Info("request", zap.String("remote", ctx.RemoteAddr().String()),
- zap.ByteString("method", ctx.Method()),
- zap.ByteString("path", ctx.Path()),
- zap.ByteString("query", ctx.QueryArgs().QueryString()),
- zap.Uint64("id", ctx.ID()))
- req(ctx)
+func (a *app) logger(h fasthttp.RequestHandler) fasthttp.RequestHandler {
+ return func(req *fasthttp.RequestCtx) {
+ a.log.Info("request", zap.String("remote", req.RemoteAddr().String()),
+ zap.ByteString("method", req.Method()),
+ zap.ByteString("path", req.Path()),
+ zap.ByteString("query", req.QueryArgs().QueryString()),
+ zap.Uint64("id", req.ID()))
+ h(req)
}
}
-func (a *app) tokenizer(req fasthttp.RequestHandler) fasthttp.RequestHandler {
- return func(ctx *fasthttp.RequestCtx) {
- appCtx, err := tokens.StoreBearerTokenAppCtx(ctx, a.ctx)
+func (a *app) tokenizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
+ return func(req *fasthttp.RequestCtx) {
+ appCtx, err := tokens.StoreBearerTokenAppCtx(a.ctx, req)
if err != nil {
a.log.Error("could not fetch and store bearer token", zap.Error(err))
- response.Error(ctx, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
+ response.Error(req, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
}
- utils.SetContextToRequest(appCtx, ctx)
- req(ctx)
+ utils.SetContextToRequest(appCtx, req)
+ h(req)
}
}
-func (a *app) tracer(req fasthttp.RequestHandler) fasthttp.RequestHandler {
- return func(ctx *fasthttp.RequestCtx) {
- appCtx := utils.GetContextFromRequest(ctx)
+func (a *app) tracer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
+ return func(req *fasthttp.RequestCtx) {
+ appCtx := utils.GetContextFromRequest(req)
- appCtx, span := utils.StartHTTPServerSpan(appCtx, ctx, "REQUEST")
+ appCtx, span := utils.StartHTTPServerSpan(appCtx, req, "REQUEST")
defer func() {
- utils.SetHTTPTraceInfo(appCtx, span, ctx)
+ utils.SetHTTPTraceInfo(appCtx, span, req)
span.End()
}()
- utils.SetContextToRequest(appCtx, ctx)
- req(ctx)
+ utils.SetContextToRequest(appCtx, req)
+ h(req)
}
}
diff --git a/settings.go b/settings.go
index 87c56bc..7a78c53 100644
--- a/settings.go
+++ b/settings.go
@@ -320,11 +320,7 @@ func mergeConfig(v *viper.Viper, fileName string) error {
}
}()
- if err = v.MergeConfig(cfgFile); err != nil {
- return err
- }
-
- return nil
+ return v.MergeConfig(cfgFile)
}
// newLogger constructs a zap.Logger instance for current application.
diff --git a/tokens/bearer-token.go b/tokens/bearer-token.go
index fd4404b..b01860d 100644
--- a/tokens/bearer-token.go
+++ b/tokens/bearer-token.go
@@ -13,9 +13,11 @@ import (
type fromHandler = func(h *fasthttp.RequestHeader) []byte
+type ctxKey string
+
const (
- bearerTokenHdr = "Bearer"
- bearerTokenKey = "__context_bearer_token_key"
+ bearerTokenHdr = "Bearer"
+ bearerTokenKey ctxKey = "__context_bearer_token_key"
)
// BearerToken usage:
@@ -50,12 +52,12 @@ func BearerTokenFromCookie(h *fasthttp.RequestHeader) []byte {
// StoreBearerTokenAppCtx extracts a bearer token from the header or cookie and stores
// it in the application context.
-func StoreBearerTokenAppCtx(ctx *fasthttp.RequestCtx, appCtx context.Context) (context.Context, error) {
- tkn, err := fetchBearerToken(ctx)
+func StoreBearerTokenAppCtx(ctx context.Context, req *fasthttp.RequestCtx) (context.Context, error) {
+ tkn, err := fetchBearerToken(req)
if err != nil {
return nil, err
}
- newCtx := context.WithValue(appCtx, bearerTokenKey, tkn)
+ newCtx := context.WithValue(ctx, bearerTokenKey, tkn)
return newCtx, nil
}
diff --git a/tokens/bearer-token_test.go b/tokens/bearer-token_test.go
index 170246c..cc54e74 100644
--- a/tokens/bearer-token_test.go
+++ b/tokens/bearer-token_test.go
@@ -151,10 +151,10 @@ func Test_checkAndPropagateBearerToken(t *testing.T) {
t64 := base64.StdEncoding.EncodeToString(tkn.Marshal())
require.NotEmpty(t, t64)
- ctx := makeTestRequest(t64, "")
+ req := makeTestRequest(t64, "")
// Expect to see the token within the context.
- appCtx, err := StoreBearerTokenAppCtx(ctx, context.Background())
+ appCtx, err := StoreBearerTokenAppCtx(context.Background(), req)
require.NoError(t, err)
// Expect to see the same token without errors.
From f24f39ec92c05e19b7eab165beef3844435c5092 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Mon, 5 Jun 2023 13:03:21 +0300
Subject: [PATCH 005/161] [#56] Increase golangci-lint timeout
For slow actions runners.
Signed-off-by: Alex Vanin
---
.golangci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.golangci.yml b/.golangci.yml
index 67f93e7..a271450 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -4,7 +4,7 @@
# options for analysis running
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
- timeout: 5m
+ timeout: 15m
# include test files or not, default is true
tests: true
From b8944adb65a7d87e6379b592fd7f6134fddc62ae Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 6 Jun 2023 17:28:52 +0300
Subject: [PATCH 006/161] [#55] Add govulncheck in CI
Check dependency issues on every PR.
Signed-off-by: Alex Vanin
---
.forgejo/workflows/vulncheck.yml | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 .forgejo/workflows/vulncheck.yml
diff --git a/.forgejo/workflows/vulncheck.yml b/.forgejo/workflows/vulncheck.yml
new file mode 100644
index 0000000..34692c9
--- /dev/null
+++ b/.forgejo/workflows/vulncheck.yml
@@ -0,0 +1,24 @@
+on: [pull_request]
+
+jobs:
+ vulncheck:
+ name: Vulncheck
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Sync tree service
+ run: make sync-tree
+
+ - name: Setup Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: '1.20'
+
+ - name: Install govulncheck
+ run: go install golang.org/x/vuln/cmd/govulncheck@latest
+
+ - name: Run govulncheck
+ run: govulncheck ./...
From 9765adf844644896dd43361c582586b904c70a41 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 6 Jun 2023 15:30:32 +0300
Subject: [PATCH 007/161] [#2] Update CODEOWNERS
Codeownders feature isn't supported yet in forgejo nor
gitea. Looking for github.com/go-gitea/gitea/pull/24910
Signed-off-by: Alex Vanin
---
.forgejo/CODEOWNERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index 37417c9..c280648 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -1 +1 @@
-* @alexvanin @KirillovDenis
+* @alexvanin @dkirillov
From 61d152ee6a3307bc6c8fcb4beef779456bb58c01 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 6 Jun 2023 15:43:41 +0300
Subject: [PATCH 008/161] [#2] Update CHANGELOG
Replace changelog history before the fork
with the link to the fork source.
Signed-off-by: Alex Vanin
---
CHANGELOG.md | 274 +--------------------------------------------------
1 file changed, 4 insertions(+), 270 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddb5845..11d43e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,7 +20,7 @@ This document outlines major changes between releases.
- Separate integration tests with build tags (#24)
- Changed values for `frostfs_http_gw_state_health` metric (#32)
-### Updating from v0.26.0
+### Updating from neofs-http-gw v0.26.0
To set system attributes use updated headers
(you can use old ones for now, but their support will be dropped in the future releases):
@@ -29,276 +29,10 @@ To set system attributes use updated headers
* `X-Attribute-NEOFS-*` -> `X-Attribute-SYSTEM-*`
* `X-Attribute-neofs-*` -> `X-Attribute-system-*`
-## [0.26.0] - 2022-12-28
-
-### Fixed
-- ENV config example (#236)
-
-### Added
-- Support the `Date` header on upload (#214)
-- Available routes specification (#216)
-- Mention caching strategy in docs (#215)
-- Add error response on attribute duplicates (#221)
-- Multiple server listeners (#228)
-
-### Removed
-- Deprecated linters (#239)
-
-### Updating from v0.25.1
-Make sure your configuration is valid:
-
-If you configure application using environment variables change:
-* `HTTP_GW_LISTEN_ADDRESS` -> `HTTP_GW_SERVER_0_ADDRESS`
-* `HTTP_GW_TLS_CERT_FILE` -> `HTTP_GW_SERVER_0_TLS_CERT_FILE` (and set `HTTP_GW_SERVER_0_TLS_ENABLED=true`)
-* `HTTP_GW_TLS_KEY_FILE` -> `HTTP_GW_SERVER_0_TLS_KEY_FILE` (and set `HTTP_GW_SERVER_0_TLS_ENABLED=true`)
-
-If you configure application using `.yaml` file change:
-* `listen_address` -> `server.0.address`
-* `tls.cert_file` -> `server.0.tls.cert_file` (and set `server.0.tls.enabled: true`)
-* `tls.key_file` -> `server.0.tls.key_file` (and set `server.0.tls.enabled: true`)
-
-## [0.25.1] - 2022-11-30
-
-### Fixed
-- Download zip archive when `FilePath` is invalid (#222)
-- Only one peer must be healthy to init pool (#233)
-
-### Added
-- Debian packaging (#223)
-- Timeout for individual operations in streaming RPC (#234)
-
-## [0.25.0] - 2022-10-31
-
-### Added
-- Config reloading on SIGHUP (#200, #208)
-- Stop pool dial on SIGINT (#212)
-- Makefile help (#213)
-
-### Changed
-- Update NeoFS error handling (#206)
-- GitHub actions updates (#205, #209)
-- Unified system attribute format for GET and HEAD (#213)
-
-## [0.24.0] - 2022-09-14
-
-### Fixed
-- Fix expiration epoch calculation (#198)
-- Fix panic on go1.19 (#188)
-
-### Added
-- Exposure of pool metrics (#179, #194)
-
-### Changed
-- Help doesn't print empty parameters (#186)
-- Update version calculation (#190, #199)
-- Update neofs-sdk-go (#196)
-- Update go version in CI and docker (#197, #202)
-
-## [0.23.0] - 2022-08-02
-
-### Added
-- New param to configure pool error threshold (#184)
-
-### Changed
-- Pprof and prometheus metrics configuration (#171)
-- Drop GO111MODULES from builds (#182)
-
-### Updating from v0.22.0
-1. To enable pprof use `pprof.enabled` instead of `pprof` 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.
-
-## [0.22.0] - 2022-07-25
-
-### Added
-- Default params documentation (#172)
-- Health metric (#175)
-
-### Changed
-- Version output (#169)
-- Updated SDK Version (#178)
-
-## [0.21.0] - 2022-06-20
-
-### Fixed
-- Downloading ZIP archive using streaming (#163)
-
-### Added
-- New make target to build app in docker (#159)
-
-### Changed
-- Increased buffer size for file uploading (#148)
-- Updated linter version to v1.46.2 (#161)
-- Updated CodeQL version to v2 (#158)
-
-
-## [0.20.0] - 2022-04-29
-
-### Fixed
-- Get rid of data race on server shutdown (#145)
-- Improved English in docs and comments (#153)
-- Use `FilePath` to download zip (#150)
-
-### Added
-- Support container name NNS resolving (#142)
-
-### Changed
-- Updated docs (#133, #140)
-- Increased default read/write timeouts (#154)
-- Updated SDK (#137, #139)
-- Updated go version to 1.17 (#143)
-- Improved error messages (#144)
-
-## [0.19.0] - 2022-03-16
-
-### Fixed
-- Uploading object with zero payload (#122)
-- Different headers format in GET and HEAD (#125)
-- Fixed project name in docs (#120)
-
-### Added
-- Support object attributes with spaces (#123)
-
-### Changed
-- Updated fasthttp to v1.34.0 (#129)
-- Updated NeoFS SDK to v1.0.0-rc.3 (#126, #132)
-- Refactored content type detecting (#128)
-
-
-## [0.18.0] - 2021-12-10
-
-### Fixed
-- System headers format (#111)
-
-### Added
-- Different formats to set object's expiration: in epoch, duration, timestamp,
- RFC3339 (#108)
-- Support of nodes priority (#115)
-
-### Changed
-- Updated testcontainers dependency (#100)
-
-## [0.17.0] - 2021-11-15
-
-Support of bulk file download with zip streams and various bug fixes.
-
-### Fixed
-- Allow canonical `X-Attribute-Neofs-*` headers (#87)
-- Responses with error message now end with `\n` character (#105)
-- Application does not require all neofs endpoints to be healthy at start now
- (#103)
-- Application now tracks session token errors and recreates tokens in runtime
- (#95)
-
-### Added
-- Integration tests with [all-in-one](https://github.com/nspcc-dev/neofs-aio/)
- test containers (#85, #94)
-- Bulk download support with zip streams (#92, #96)
-
-## 0.16.1 (28 Jul 2021)
-
-New features:
-* logging requests (#77)
-* HEAD methods for download routes (#76)
-
-Improvements:
-* updated sdk-go dependency (#82)
-
-Bugs fixed:
-* wrong NotFound status was used (#30)
-
-## 0.16.0 (29 Jun 2021)
-
-We update HTTP gateway with NEP-6 wallets support, YAML configuration files
-and small fixes.
-
-New features:
- * YAML configuration file (#71)
-
-Behavior changes:
- * gateway key needs to be stored in a proper NEP-6 wallet now, `-k` option is
- no longer available, see `-w` and `-a` (#68)
-
-Bugs fixed:
- * downloads were not streamed leading to excessive memory usage (#67)
- * Last-Modified header incorrectly used local time (#75)
-
-## 0.15.2 (22 Jun 2021)
-
-New features:
- * Content-Type returned for object GET requests can now be taken from
- attributes (overriding autodetection, #65)
-
-Behavior changes:
- * grpc keepalive options can no longer be changed (#60)
-
-Improvements:
- * code refactoring (more reuse between different gateways, moved some code to
- sdk-go, #47, #46, #51, #62, #63)
- * documentation updates and fixes (#53, #49, #55, #59)
- * updated api-go dependency (#57)
-
-Bugs fixed:
- * `-k` option wasn't accepted for key although it was documented (#50)
-
-## 0.15.1 (24 May 2021)
-
-This important release makes HTTP gateway compatible with NeoFS node version
-0.20.0.
-
-Behavior changes:
- * neofs-api-go was updated to 1.26.1, which contains some incompatible
- changes in underlying components (#39, #44)
- * `neofs-http-gw` is consistently used now for repository, binary and image
- names (#43)
-
-Improvements:
- * minor code cleanups based on stricter set of linters (#41)
- * updated README (#42)
-
-## 0.15.0 (30 Apr 2021)
-
-This is the first public release incorporating latest NeoFS protocol support
-and fixing some bugs.
-
-New features:
- * upload support (#14, #13, #29)
- * ephemeral keys (#26)
- * TLS server support (#28)
-
-Behavior changes:
- * node weights can now be specified as simple numbers instead of percentages
- and gateway will calculate the proportion automatically (#27)
- * attributes are converted now to `X-Attribute-*` headers when retrieving
- object from gate instead of `X-*` (#29)
-
-Improvements:
- * better Makefile (#16, #24, #33, #34)
- * updated documentation (#16, #29, #35, #36)
- * updated neofs-api-go to v1.25.0 (#17, #20)
- * updated fasthttp to v1.23.0+ (#17, #29)
- * refactoring, eliminating some dependencies (#20, #29)
-
-Bugs fixed:
- * gateway attempted to work with no NeoFS peers configured (#29)
- * some invalid headers could be sent for attributes using non-ASCII or
- non-printable characters (#29)
## Older versions
-Please refer to [Github
-releases](https://github.com/nspcc-dev/neofs-http-gw/releases/) for older
-releases.
+This project is a fork of [NeoFS HTTP Gateway](https://github.com/nspcc-dev/neofs-http-gw) from version v0.26.0.
+To see CHANGELOG for older versions, refer to https://github.com/nspcc-dev/neofs-http-gw/blob/master/CHANGELOG.md.
-[0.17.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.16.1...v0.17.0
-[0.18.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.17.0...v0.18.0
-[0.19.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.18.0...v0.19.0
-[0.20.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.19.0...v0.20.0
-[0.21.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.20.0...v0.21.0
-[0.22.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.21.0...v0.22.0
-[0.23.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.22.0...v0.23.0
-[0.24.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.23.0...v0.24.0
-[0.25.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.24.0...v0.25.0
-[0.25.1]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.25.0...v0.25.1
-[0.26.0]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.25.1...v0.26.0
-[Unreleased]: https://github.com/nspcc-dev/neofs-http-gw/compare/v0.26.0...master
+[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/72734ab4...master
From d7dbff12553c4943198174c8a2c719a6651e2b1e Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 6 Jun 2023 15:44:56 +0300
Subject: [PATCH 009/161] [#2] Use latest AIO image in integration test
Latest version provides more
stability during startup stage.
Signed-off-by: Alex Vanin
---
integration_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/integration_test.go b/integration_test.go
index 549a09d..014f923 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -46,7 +46,7 @@ func TestIntegration(t *testing.T) {
rootCtx := context.Background()
aioImage := "truecloudlab/frostfs-aio:"
versions := []string{
- "1.2.5", // frostfs-storage v0.36.0 RC
+ "1.2.7", // frostfs-storage v0.36.0 RC
}
key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb")
require.NoError(t, err)
From d0f6baa44bc75dc7bb8c1291add08bbc7bd4a58d Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 6 Jun 2023 16:00:31 +0300
Subject: [PATCH 010/161] [#2] Return shields
Signed-off-by: Alex Vanin
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index e2b697a..52506bd 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,8 @@
---
[](https://goreportcard.com/report/git.frostfs.info/TrueCloudLab/frostfs-http-gw)
+
+
# FrostFS HTTP Gateway
From 2c706bec71cd5595bf7d75fd250a02d558c3d48a Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 6 Jun 2023 17:15:07 +0300
Subject: [PATCH 011/161] [#2] Update CONTRIBUTING
Signed-off-by: Alex Vanin
---
CONTRIBUTING.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b365b64..ffd587d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,8 +3,8 @@
First, thank you for contributing! We love and encourage pull requests from
everyone. Please follow the guidelines:
-- Check the open [issues](https://github.com/TrueCloudLab/frostfs-http-gw/issues) and
- [pull requests](https://github.com/TrueCloudLab/frostfs-http-gw/pulls) for existing
+- Check the open [issues](https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/issues) and
+ [pull requests](https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/pulls) for existing
discussions.
- Open an issue first, to discuss a new feature or enhancement.
@@ -27,20 +27,20 @@ Start by forking the `frostfs-http-gw` repository, make changes in a branch and
send a pull request. We encourage pull requests to discuss code changes. Here
are the steps in details:
-### Set up your GitHub Repository
+### Set up your git repository
Fork [FrostFS HTTP Gateway
-upstream](https://github.com/TrueCloudLab/frostfs-http-gw/fork) source repository
+upstream](https://git.frostfs.info/repo/fork/8) source repository
to your own personal repository. Copy the URL of your fork (you will need it for
the `git clone` command below).
```sh
-$ git clone https://github.com/TrueCloudLab/frostfs-http-gw
+$ git clone https://git.frostfs.info//frostfs-http-gw.git
```
### Set up git remote as ``upstream``
```sh
$ cd frostfs-http-gw
-$ git remote add upstream https://github.com/TrueCloudLab/frostfs-http-gw
+$ git remote add upstream https://git.frostfs.info/TrueCloudLab/frostfs-http-gw.git
$ git fetch upstream
$ git merge upstream/master
...
@@ -90,8 +90,8 @@ $ git push origin feature/123-something_awesome
```
### Create a Pull Request
-Pull requests can be created via GitHub. Refer to [this
-document](https://help.github.com/articles/creating-a-pull-request/) for
+Pull requests can be created via Forgejo. Refer to [this
+document](https://docs.codeberg.org/collaborating/pull-requests-and-git-flow/) for
detailed steps on how to create a pull request. After a Pull Request gets peer
reviewed and approved, it will be merged.
From 5be537321bf4c74a5ba269cd60146cd4e32ff320 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 7 Jun 2023 14:18:31 +0300
Subject: [PATCH 012/161] [#2] Keep issue templates in .github
The only thing supported in .forgejo dir is a workflows.
It might be useful to keep .github dir for repo mirrors.
Signed-off-by: Alex Vanin
---
{.forgejo => .github}/CODEOWNERS | 0
{.forgejo => .github}/ISSUE_TEMPLATE/bug_report.md | 0
{.forgejo => .github}/ISSUE_TEMPLATE/config.yml | 0
{.forgejo => .github}/ISSUE_TEMPLATE/feature_request.md | 0
{.forgejo => .github}/logo.svg | 0
README.md | 2 +-
6 files changed, 1 insertion(+), 1 deletion(-)
rename {.forgejo => .github}/CODEOWNERS (100%)
rename {.forgejo => .github}/ISSUE_TEMPLATE/bug_report.md (100%)
rename {.forgejo => .github}/ISSUE_TEMPLATE/config.yml (100%)
rename {.forgejo => .github}/ISSUE_TEMPLATE/feature_request.md (100%)
rename {.forgejo => .github}/logo.svg (100%)
diff --git a/.forgejo/CODEOWNERS b/.github/CODEOWNERS
similarity index 100%
rename from .forgejo/CODEOWNERS
rename to .github/CODEOWNERS
diff --git a/.forgejo/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
similarity index 100%
rename from .forgejo/ISSUE_TEMPLATE/bug_report.md
rename to .github/ISSUE_TEMPLATE/bug_report.md
diff --git a/.forgejo/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
similarity index 100%
rename from .forgejo/ISSUE_TEMPLATE/config.yml
rename to .github/ISSUE_TEMPLATE/config.yml
diff --git a/.forgejo/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
similarity index 100%
rename from .forgejo/ISSUE_TEMPLATE/feature_request.md
rename to .github/ISSUE_TEMPLATE/feature_request.md
diff --git a/.forgejo/logo.svg b/.github/logo.svg
similarity index 100%
rename from .forgejo/logo.svg
rename to .github/logo.svg
diff --git a/README.md b/README.md
index 52506bd..6e19d31 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
FrostFS is a decentralized distributed object storage integrated with the NEO Blockchain.
From 1dfbe36eca62589b548d9c0037dd12a9015d49e6 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Fri, 9 Jun 2023 09:39:01 +0300
Subject: [PATCH 013/161] [#59] Use tree pool from SDK
Signed-off-by: Denis Kirillov
---
app.go | 80 ++------------
go.mod | 66 ++++++------
go.sum | 126 +++++++++++-----------
integration_test.go | 1 -
internal/frostfs/services/pool_wrapper.go | 115 ++++++++++++++++++++
settings.go | 114 +++++++++++++++++++-
utils/tracing.go | 2 +-
7 files changed, 338 insertions(+), 166 deletions(-)
create mode 100644 internal/frostfs/services/pool_wrapper.go
diff --git a/app.go b/app.go
index f43d187..88f9a07 100644
--- a/app.go
+++ b/app.go
@@ -6,12 +6,10 @@ import (
"net/http"
"os"
"os/signal"
- "strconv"
"sync"
"syscall"
"time"
- "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/downloader"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
@@ -21,7 +19,9 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/uploader"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
+ treepool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/fasthttp/router"
"github.com/nspcc-dev/neo-go/cli/flags"
@@ -32,8 +32,6 @@ import (
"github.com/spf13/viper"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials/insecure"
)
type (
@@ -42,6 +40,7 @@ type (
log *zap.Logger
logLevel zap.AtomicLevel
pool *pool.Pool
+ treePool *treepool.Pool
key *keys.PrivateKey
owner *user.ID
cfg *viper.Viper
@@ -98,10 +97,6 @@ func WithConfig(c *viper.Viper) Option {
}
func newApp(ctx context.Context, opt ...Option) App {
- var (
- err error
- )
-
a := &app{
ctx: ctx,
log: zap.L(),
@@ -126,51 +121,12 @@ func newApp(ctx context.Context, opt ...Option) App {
a.webServer.DisablePreParseMultipartForm = true
a.webServer.StreamRequestBody = a.cfg.GetBool(cfgWebStreamRequestBody)
// -- -- -- -- -- -- -- -- -- -- -- -- -- --
- a.key, err = getFrostFSKey(a)
- if err != nil {
- a.log.Fatal("failed to get frostfs credentials", zap.Error(err))
- }
+ a.pool, a.treePool, a.key = getPools(ctx, a.log, a.cfg)
var owner user.ID
user.IDFromKey(&owner, a.key.PrivateKey.PublicKey)
a.owner = &owner
- var prm pool.InitParameters
- prm.SetKey(&a.key.PrivateKey)
- prm.SetNodeDialTimeout(a.cfg.GetDuration(cfgConTimeout))
- prm.SetNodeStreamTimeout(a.cfg.GetDuration(cfgStreamTimeout))
- prm.SetHealthcheckTimeout(a.cfg.GetDuration(cfgReqTimeout))
- prm.SetClientRebalanceInterval(a.cfg.GetDuration(cfgRebalance))
- prm.SetErrorThreshold(a.cfg.GetUint32(cfgPoolErrorThreshold))
-
- for i := 0; ; i++ {
- address := a.cfg.GetString(cfgPeers + "." + strconv.Itoa(i) + ".address")
- weight := a.cfg.GetFloat64(cfgPeers + "." + strconv.Itoa(i) + ".weight")
- priority := a.cfg.GetInt(cfgPeers + "." + strconv.Itoa(i) + ".priority")
- if address == "" {
- break
- }
- if weight <= 0 { // unspecified or wrong
- weight = 1
- }
- if priority <= 0 { // unspecified or wrong
- priority = 1
- }
- prm.AddNode(pool.NewNodeParam(priority, address, weight))
- a.log.Info("add connection", zap.String("address", address),
- zap.Float64("weight", weight), zap.Int("priority", priority))
- }
-
- a.pool, err = pool.NewPool(prm)
- if err != nil {
- a.log.Fatal("failed to create connection pool", zap.Error(err))
- }
-
- err = a.pool.Dial(ctx)
- if err != nil {
- a.log.Fatal("failed to dial pool", zap.Error(err))
- }
-
a.initAppSettings()
a.initResolver()
a.initMetrics()
@@ -283,11 +239,11 @@ func remove(list []string, element string) []string {
return list
}
-func getFrostFSKey(a *app) (*keys.PrivateKey, error) {
- walletPath := a.cfg.GetString(cfgWalletPath)
+func getFrostFSKey(cfg *viper.Viper, log *zap.Logger) (*keys.PrivateKey, error) {
+ walletPath := cfg.GetString(cfgWalletPath)
if len(walletPath) == 0 {
- a.log.Info("no wallet path specified, creating ephemeral key automatically for this run")
+ log.Info("no wallet path specified, creating ephemeral key automatically for this run")
key, err := keys.NewPrivateKey()
if err != nil {
return nil, err
@@ -300,12 +256,12 @@ func getFrostFSKey(a *app) (*keys.PrivateKey, error) {
}
var password *string
- if a.cfg.IsSet(cfgWalletPassphrase) {
- pwd := a.cfg.GetString(cfgWalletPassphrase)
+ if cfg.IsSet(cfgWalletPassphrase) {
+ pwd := cfg.GetString(cfgWalletPassphrase)
password = &pwd
}
- address := a.cfg.GetString(cfgWalletAddress)
+ address := cfg.GetString(cfgWalletAddress)
return getKeyFromWallet(w, address, password)
}
@@ -357,9 +313,8 @@ func (a *app) setHealthStatus() {
}
func (a *app) Serve() {
- treeClient := a.initTree(a.ctx)
uploadRoutes := uploader.New(a.AppParams(), a.settings.Uploader)
- downloadRoutes := downloader.New(a.AppParams(), a.settings.Downloader, treeClient)
+ downloadRoutes := downloader.New(a.AppParams(), a.settings.Downloader, tree.NewTree(services.NewPoolWrapper(a.treePool)))
// Configure router.
a.configureRouter(uploadRoutes, downloadRoutes)
@@ -599,19 +554,6 @@ func (a *app) serverIndex(address string) int {
return -1
}
-func (a *app) initTree(ctx context.Context) *tree.Tree {
- treeServiceEndpoint := a.cfg.GetString(cfgTreeServiceEndpoint)
- grpcDialOpt := grpc.WithTransportCredentials(insecure.NewCredentials())
- treeGRPCClient, err := services.NewTreeServiceClientGRPC(ctx, treeServiceEndpoint, a.key, grpcDialOpt)
- if err != nil {
- 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))
-
- return treeService
-}
-
func (a *app) initTracing(ctx context.Context) {
instanceID := ""
if len(a.servers) > 0 {
diff --git a/go.mod b/go.mod
index 4532b1b..dd89afb 100644
--- a/go.mod
+++ b/go.mod
@@ -3,60 +3,60 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.19
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230418080822-bd44a3f47b85
- git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230505094539-15b4287092bd
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230531114046-62edd68f47ac
+ git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe
github.com/fasthttp/router v1.4.1
- github.com/nspcc-dev/neo-go v0.101.0
- github.com/prometheus/client_golang v1.15.0
+ github.com/nspcc-dev/neo-go v0.101.1
+ github.com/prometheus/client_golang v1.15.1
github.com/prometheus/client_model v0.3.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
- github.com/stretchr/testify v1.8.2
+ github.com/stretchr/testify v1.8.3
github.com/testcontainers/testcontainers-go v0.13.0
github.com/valyala/fasthttp v1.34.0
- go.opentelemetry.io/otel v1.14.0
- go.opentelemetry.io/otel/trace v1.14.0
+ go.opentelemetry.io/otel v1.16.0
+ go.opentelemetry.io/otel/trace v1.16.0
go.uber.org/atomic v1.10.0
go.uber.org/zap v1.24.0
- google.golang.org/grpc v1.53.0
- google.golang.org/protobuf v1.30.0
+ google.golang.org/grpc v1.55.0
)
require (
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb // indirect
- git.frostfs.info/TrueCloudLab/hrw v1.2.0 // indirect
+ git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
+ git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Microsoft/hcsshim v0.9.2 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
- github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 // indirect
+ github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v4 v4.2.0 // indirect
+ github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/cgroups v1.0.3 // indirect
github.com/containerd/containerd v1.6.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.14+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
- github.com/go-logr/logr v1.2.3 // indirect
+ github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
- github.com/gorilla/websocket v1.4.2 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
+ github.com/gorilla/websocket v1.5.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
github.com/hashicorp/golang-lru v0.6.0 // indirect
- github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
+ github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/klauspost/compress v1.16.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
@@ -81,31 +81,33 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
- github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
+ github.com/twmb/murmur3 v1.1.8 // indirect
github.com/urfave/cli v1.22.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.opencensus.io v0.24.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect
- go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 // indirect
- go.opentelemetry.io/otel/sdk v1.14.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 // indirect
+ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 // indirect
+ go.opentelemetry.io/otel/metric v1.16.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
- go.uber.org/multierr v1.9.0 // indirect
- golang.org/x/crypto v0.8.0 // indirect
- golang.org/x/exp v0.0.0-20221227203929-1b447090c38c // indirect
- golang.org/x/net v0.9.0 // indirect
- golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.7.0 // indirect
- golang.org/x/term v0.7.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ golang.org/x/crypto v0.9.0 // indirect
+ golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
+ golang.org/x/net v0.10.0 // indirect
+ golang.org/x/sync v0.2.0 // indirect
+ golang.org/x/sys v0.8.0 // indirect
+ golang.org/x/term v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.3.0 // indirect
- google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
+ google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
+ google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 960fda0..6d0e55c 100644
--- a/go.sum
+++ b/go.sum
@@ -37,16 +37,18 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230418080822-bd44a3f47b85 h1:77lvdk0kMhnUgtnmqEcAPXPQaGlt24goMPu2+E5WRTk=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230418080822-bd44a3f47b85/go.mod h1:sPyITTmQT662ZI38ud2aoE1SUCAr1mO5xV8P4nzLkKI=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230531114046-62edd68f47ac h1:a6/Zc5BejflmguShwbllgJdEehnM9gshkLrLbKQHCU0=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230531114046-62edd68f47ac/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M=
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/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230505094539-15b4287092bd h1:HxacVl1Lc2RrfxAE13AGkp1tR/Mf4DDP6TgrgbLP5fQ=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230505094539-15b4287092bd/go.mod h1:TaJJOF3Uhuq8aqv2CrfuY2yhxePUinW35Xd3wfXLV/I=
-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/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
+git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe h1:47lrWXcl36ayN7AJ9IW7sDDnTj//RUyHoIZOsjbYAYA=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe/go.mod h1:w+s3ozlbFfTDFHhjX0A3Iif3BRtnTkwiACxFZD+Q0cQ=
+git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
+git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
@@ -119,8 +121,8 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
-github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4=
-github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
@@ -156,8 +158,8 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
-github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
@@ -312,9 +314,8 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@@ -392,8 +393,8 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
-github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
+github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
@@ -430,8 +431,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
+github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -524,8 +525,9 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -533,8 +535,9 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
@@ -544,8 +547,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4=
-github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
+github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU=
+github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
@@ -684,8 +687,8 @@ github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkP
github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg=
github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM=
github.com/nspcc-dev/neo-go v0.99.4/go.mod h1:mKTolfRUfKjFso5HPvGSQtUZc70n0VKBMs16eGuC5gA=
-github.com/nspcc-dev/neo-go v0.101.0 h1:JPT2DpZqVjho34TMR59dm6uxvCFttOp02Nm8qCjpfaU=
-github.com/nspcc-dev/neo-go v0.101.0/go.mod h1:Q0uWKivGc2mYgdKFmTNP49LeXwMu4x6pUzHm3OIsN2I=
+github.com/nspcc-dev/neo-go v0.101.1 h1:TVdcIpH/+bxQBTLRwWE3+Pw3j6j/JwguENbBSGAGid0=
+github.com/nspcc-dev/neo-go v0.101.1/go.mod h1:J4tspxWw7jknX06F+VSMsKvIiNpYGfVTb2IxVC005YU=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220927123257-24c107e3a262/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s=
@@ -779,8 +782,8 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
-github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
-github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
+github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
+github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -819,8 +822,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -845,7 +848,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -886,8 +888,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@@ -903,6 +905,8 @@ github.com/testcontainers/testcontainers-go v0.13.0/go.mod h1:z1abufU633Eb/FmSBT
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
+github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
+github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
@@ -957,20 +961,22 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
-go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
-go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho=
-go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0/go.mod h1:UFG7EBMRdXyFstOwH028U0sVf+AvukSGhF0g8+dmNG8=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0/go.mod h1:HrbCVv40OOLTABmOn1ZWty6CHXkU8DK/Urc43tHug70=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h1:5w41DY6S9gZrbjuq6Y+753e96WfPha5IcsOSZTtullM=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 h1:sEL90JjOO/4yhquXl5zTAkLLsZ5+MycAgX99SDsxGc8=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0/go.mod h1:oCslUcizYdpKYyS9e8srZEqM6BB8fq41VJBjLAE6z1w=
-go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
-go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
-go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
-go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
+go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
+go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
+go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0=
+go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0/go.mod h1:vLarbg68dH2Wa77g71zmKQqlQ8+8Rq3GRG31uc0WcWI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 h1:+XWJd3jf75RXJq29mxbuXhCXFDG3S3R4vBUeSI2P7tE=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0/go.mod h1:hqgzBPTf4yONMFgdZvL/bK42R/iinTyVQtiWihs3SZc=
+go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
+go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
+go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
+go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
+go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
+go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
@@ -985,8 +991,8 @@ go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
-go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
@@ -1012,8 +1018,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
-golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
+golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1024,8 +1030,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20221227203929-1b447090c38c h1:Govq2W3bnHJimHT2ium65kXcI7ZzTniZHcFATnLJM0Q=
-golang.org/x/exp v0.0.0-20221227203929-1b447090c38c/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
+golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1106,8 +1112,8 @@ golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1132,8 +1138,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
+golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1233,13 +1239,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1398,8 +1404,8 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
-google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1425,8 +1431,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
-google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
+google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
+google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/integration_test.go b/integration_test.go
index 014f923..34506c4 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -363,7 +363,6 @@ func getDefaultConfig() *viper.Viper {
v.SetDefault(cfgRPCEndpoint, "http://localhost:30333")
v.SetDefault("server.0.address", testListenAddress)
- v.SetDefault(cfgTreeServiceEndpoint, "localhost:8080")
return v
}
diff --git a/internal/frostfs/services/pool_wrapper.go b/internal/frostfs/services/pool_wrapper.go
new file mode 100644
index 0000000..039d575
--- /dev/null
+++ b/internal/frostfs/services/pool_wrapper.go
@@ -0,0 +1,115 @@
+package services
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
+ treepool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
+ grpcService "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree/service"
+)
+
+type GetNodeByPathResponseInfoWrapper struct {
+ response *grpcService.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() []tree.Meta {
+ res := make([]tree.Meta, len(n.response.Meta))
+ for i, value := range n.response.Meta {
+ res[i] = value
+ }
+ return res
+}
+
+type GetSubTreeResponseBodyWrapper struct {
+ response *grpcService.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() []tree.Meta {
+ res := make([]tree.Meta, len(n.response.Meta))
+ for i, value := range n.response.Meta {
+ res[i] = value
+ }
+ return res
+}
+
+type PoolWrapper struct {
+ p *treepool.Pool
+}
+
+func NewPoolWrapper(p *treepool.Pool) *PoolWrapper {
+ return &PoolWrapper{p: p}
+}
+
+func (w *PoolWrapper) GetNodes(ctx context.Context, prm *tree.GetNodesParams) ([]tree.NodeResponse, error) {
+ poolPrm := treepool.GetNodesParams{
+ CID: prm.CnrID,
+ TreeID: prm.TreeID,
+ Path: prm.Path,
+ Meta: prm.Meta,
+ PathAttribute: tree.FileNameKey,
+ LatestOnly: prm.LatestOnly,
+ AllAttrs: prm.AllAttrs,
+ BearerToken: getBearer(ctx),
+ }
+
+ nodes, err := w.p.GetNodes(ctx, poolPrm)
+ if err != nil {
+ return nil, handleError(err)
+ }
+
+ res := make([]tree.NodeResponse, len(nodes))
+ for i, info := range nodes {
+ res[i] = GetNodeByPathResponseInfoWrapper{info}
+ }
+
+ return res, nil
+}
+
+func getBearer(ctx context.Context) []byte {
+ token, err := tokens.LoadBearerToken(ctx)
+ if err != nil {
+ return nil
+ }
+ return token.Marshal()
+}
+
+func handleError(err error) error {
+ if err == nil {
+ return nil
+ }
+ if errors.Is(err, treepool.ErrNodeNotFound) {
+ return fmt.Errorf("%w: %s", tree.ErrNodeNotFound, err.Error())
+ }
+ if errors.Is(err, treepool.ErrNodeAccessDenied) {
+ return fmt.Errorf("%w: %s", tree.ErrNodeAccessDenied, err.Error())
+ }
+
+ return err
+}
diff --git a/settings.go b/settings.go
index 7a78c53..eec014c 100644
--- a/settings.go
+++ b/settings.go
@@ -1,6 +1,8 @@
package main
import (
+ "context"
+ "encoding/hex"
"fmt"
"os"
"path"
@@ -11,11 +13,16 @@ import (
"time"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
+ treepool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
+ "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials/insecure"
)
const (
@@ -59,9 +66,6 @@ const (
cfgRebalance = "rebalance_timer"
cfgPoolErrorThreshold = "pool_error_threshold"
- // Grpc path to tree service.
- cfgTreeServiceEndpoint = "tree.service"
-
// Logger.
cfgLoggerLevel = "logger.level"
@@ -395,3 +399,107 @@ func fetchServers(v *viper.Viper) []ServerInfo {
return servers
}
+
+func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.Pool, *treepool.Pool, *keys.PrivateKey) {
+ key, err := getFrostFSKey(cfg, logger)
+ if err != nil {
+ logger.Fatal("could not load FrostFS private key", zap.Error(err))
+ }
+
+ var prm pool.InitParameters
+ var prmTree treepool.InitParameters
+
+ prm.SetKey(&key.PrivateKey)
+ prmTree.SetKey(key)
+ logger.Info("using credentials", zap.String("FrostFS", hex.EncodeToString(key.PublicKey().Bytes())))
+
+ for _, peer := range fetchPeers(logger, cfg) {
+ prm.AddNode(peer)
+ prmTree.AddNode(peer)
+ }
+
+ connTimeout := cfg.GetDuration(cfgConTimeout)
+ if connTimeout <= 0 {
+ connTimeout = defaultConnectTimeout
+ }
+ prm.SetNodeDialTimeout(connTimeout)
+ prmTree.SetNodeDialTimeout(connTimeout)
+
+ streamTimeout := cfg.GetDuration(cfgStreamTimeout)
+ if streamTimeout <= 0 {
+ streamTimeout = defaultStreamTimeout
+ }
+ prm.SetNodeStreamTimeout(streamTimeout)
+ prmTree.SetNodeStreamTimeout(streamTimeout)
+
+ healthCheckTimeout := cfg.GetDuration(cfgReqTimeout)
+ if healthCheckTimeout <= 0 {
+ healthCheckTimeout = defaultRequestTimeout
+ }
+ prm.SetHealthcheckTimeout(healthCheckTimeout)
+ prmTree.SetHealthcheckTimeout(healthCheckTimeout)
+
+ rebalanceInterval := cfg.GetDuration(cfgRebalance)
+ if rebalanceInterval <= 0 {
+ rebalanceInterval = defaultRebalanceTimer
+ }
+ prm.SetClientRebalanceInterval(rebalanceInterval)
+ prmTree.SetClientRebalanceInterval(rebalanceInterval)
+
+ errorThreshold := cfg.GetUint32(cfgPoolErrorThreshold)
+ if errorThreshold <= 0 {
+ errorThreshold = defaultPoolErrorThreshold
+ }
+ prm.SetErrorThreshold(errorThreshold)
+ prm.SetLogger(logger)
+ prmTree.SetLogger(logger)
+
+ p, err := pool.NewPool(prm)
+ if err != nil {
+ logger.Fatal("failed to create connection pool", zap.Error(err))
+ }
+
+ if err = p.Dial(ctx); err != nil {
+ logger.Fatal("failed to dial connection pool", zap.Error(err))
+ }
+
+ prmTree.SetGRPCDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials()))
+ treePool, err := treepool.NewPool(prmTree)
+ if err != nil {
+ logger.Fatal("failed to create tree pool", zap.Error(err))
+ }
+ if err = treePool.Dial(ctx); err != nil {
+ logger.Fatal("failed to dial tree pool", zap.Error(err))
+ }
+
+ return p, treePool, key
+}
+
+func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.NodeParam {
+ var nodes []pool.NodeParam
+ for i := 0; ; i++ {
+ key := cfgPeers + "." + strconv.Itoa(i) + "."
+ address := v.GetString(key + "address")
+ weight := v.GetFloat64(key + "weight")
+ priority := v.GetInt(key + "priority")
+
+ if address == "" {
+ break
+ }
+ if weight <= 0 { // unspecified or wrong
+ weight = 1
+ }
+ if priority <= 0 { // unspecified or wrong
+ priority = 1
+ }
+
+ nodes = append(nodes, pool.NewNodeParam(priority, address, weight))
+
+ l.Info("added storage peer",
+ zap.Int("priority", priority),
+ zap.String("address", address),
+ zap.Float64("weight", weight))
+ }
+
+ return nodes
+}
diff --git a/utils/tracing.go b/utils/tracing.go
index 75a4486..14c059a 100644
--- a/utils/tracing.go
+++ b/utils/tracing.go
@@ -3,7 +3,7 @@ package utils
import (
"context"
- "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
+ "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
"github.com/valyala/fasthttp"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
From 202ef5cc54ef2d18fc9b1a3abd6c22521c4767cc Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Fri, 9 Jun 2023 09:39:25 +0300
Subject: [PATCH 014/161] [#59] Drop sync-tree
Signed-off-by: Denis Kirillov
---
.forgejo/workflows/tests.yml | 6 -
.forgejo/workflows/vulncheck.yml | 3 -
.gitignore | 1 -
Makefile | 8 +-
internal/frostfs/services/tree_client_grpc.go | 114 ------------------
.../services/tree_client_grpc_signature.go | 29 -----
syncTree.sh | 21 ----
7 files changed, 1 insertion(+), 181 deletions(-)
delete mode 100644 internal/frostfs/services/tree_client_grpc.go
delete mode 100644 internal/frostfs/services/tree_client_grpc_signature.go
delete mode 100755 syncTree.sh
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index f64b816..7a03020 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -7,9 +7,6 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: Sync tree service
- run: make sync-tree
-
- name: golangci-lint
uses: https://github.com/golangci/golangci-lint-action@v2
with:
@@ -29,9 +26,6 @@ jobs:
uses: actions/setup-go@v3
with:
go-version: '${{ matrix.go_versions }}'
-
- - name: Sync tree service
- run: make sync-tree
- name: Update Go modules
run: make dep
diff --git a/.forgejo/workflows/vulncheck.yml b/.forgejo/workflows/vulncheck.yml
index 34692c9..0c9e908 100644
--- a/.forgejo/workflows/vulncheck.yml
+++ b/.forgejo/workflows/vulncheck.yml
@@ -9,9 +9,6 @@ jobs:
with:
fetch-depth: 0
- - name: Sync tree service
- run: make sync-tree
-
- name: Setup Go
uses: actions/setup-go@v3
with:
diff --git a/.gitignore b/.gitignore
index c1ca465..c4a98d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@ bin
temp
/plugins/
/vendor/
-internal/frostfs/services/tree
.test.env
*~
diff --git a/Makefile b/Makefile
index aeea3a8..a4db526 100755
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,6 @@ METRICS_DUMP_OUT ?= ./metrics-dump.json
BINDIR = bin
DIRS = $(BINDIR)
BINS = $(BINDIR)/frostfs-http-gw
-SYNCDIR = internal/frostfs/services/tree
.PHONY: all $(BINS) $(DIRS) dep docker/ test cover fmt image image-push dirty-image lint docker/lint pre-commit unpre-commit version clean
@@ -28,7 +27,7 @@ PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
# Make all binaries
all: $(BINS)
-$(BINS): sync-tree $(DIRS) dep
+$(BINS): $(DIRS) dep
@echo "⇒ Build $@"
CGO_ENABLED=0 \
go build -v -trimpath \
@@ -39,10 +38,6 @@ $(DIRS):
@echo "⇒ Ensure dir: $@"
@mkdir -p $@
-# Synchronize tree service
-sync-tree:
- @./syncTree.sh
-
# Pull go dependencies
dep:
@printf "⇒ Download requirements: "
@@ -136,7 +131,6 @@ version:
clean:
rm -rf vendor
rm -rf $(BINDIR)
- rm -rf $(SYNCDIR)
# Package for Debian
debpackage:
diff --git a/internal/frostfs/services/tree_client_grpc.go b/internal/frostfs/services/tree_client_grpc.go
deleted file mode 100644
index f25588a..0000000
--- a/internal/frostfs/services/tree_client_grpc.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package services
-
-import (
- "context"
- "fmt"
- "strings"
-
- grpcService "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services/tree"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
- "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
- "google.golang.org/grpc"
-)
-
-type GetNodeByPathResponseInfoWrapper struct {
- response *grpcService.GetNodeByPathResponse_Info
-}
-
-func (n GetNodeByPathResponseInfoWrapper) GetMeta() []tree.Meta {
- res := make([]tree.Meta, len(n.response.Meta))
- for i, value := range n.response.Meta {
- res[i] = value
- }
- return res
-}
-
-type GetSubTreeResponseBodyWrapper struct {
- response *grpcService.GetSubTreeResponse_Body
-}
-
-func (n GetSubTreeResponseBodyWrapper) GetMeta() []tree.Meta {
- res := make([]tree.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 grpcService.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 := grpcService.NewTreeServiceClient(conn)
- if _, err = c.Healthcheck(ctx, &grpcService.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 *tree.GetNodesParams) ([]tree.NodeResponse, error) {
- request := &grpcService.GetNodeByPathRequest{
- Body: &grpcService.GetNodeByPathRequest_Body{
- ContainerId: p.CnrID[:],
- TreeId: p.TreeID,
- Path: p.Path,
- Attributes: p.Meta,
- PathAttribute: tree.FileNameKey,
- LatestOnly: p.LatestOnly,
- AllAttributes: p.AllAttrs,
- BearerToken: getBearer(ctx),
- },
- }
-
- if err := c.signRequest(request.Body, func(key, sign []byte) {
- request.Signature = &grpcService.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([]tree.NodeResponse, len(resp.GetBody().GetNodes()))
- for i, info := range resp.GetBody().GetNodes() {
- res[i] = GetNodeByPathResponseInfoWrapper{info}
- }
-
- return res, nil
-}
-
-func getBearer(ctx context.Context) []byte {
- token, err := tokens.LoadBearerToken(ctx)
- if err != nil {
- return nil
- }
- return token.Marshal()
-}
-
-func handleError(msg string, err error) error {
- if strings.Contains(err.Error(), "not found") {
- return fmt.Errorf("%w: %s", tree.ErrNodeNotFound, err.Error())
- } else if strings.Contains(err.Error(), "is denied by") {
- return fmt.Errorf("%w: %s", tree.ErrNodeAccessDenied, err.Error())
- }
- return fmt.Errorf("%s: %w", msg, err)
-}
diff --git a/internal/frostfs/services/tree_client_grpc_signature.go b/internal/frostfs/services/tree_client_grpc_signature.go
deleted file mode 100644
index 9dd38f9..0000000
--- a/internal/frostfs/services/tree_client_grpc_signature.go
+++ /dev/null
@@ -1,29 +0,0 @@
-/*REMOVE THIS AFTER SIGNATURE WILL BE AVAILABLE IN TREE CLIENT FROM FROSTFS NODE*/
-package services
-
-import (
- crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto"
- "google.golang.org/protobuf/proto"
-)
-
-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).
- // 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.
- sign, err := crypto.Sign(&c.key.PrivateKey, buf)
- if err != nil {
- return err
- }
-
- f(c.key.PublicKey().Bytes(), sign)
- return nil
-}
-
-func (c *ServiceClientGRPC) signRequest(requestBody proto.Message, f func(key, sign []byte)) error {
- buf, err := proto.Marshal(requestBody)
- if err != nil {
- return err
- }
-
- return c.signData(buf, f)
-}
diff --git a/syncTree.sh b/syncTree.sh
deleted file mode 100755
index 98d16a0..0000000
--- a/syncTree.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-
-mkdir -p internal/frostfs/services/tree 2>/dev/null
-
-REVISION="f07d4158f50ed5c7f44cc0bc224c3d03edf27f3b"
-
-echo "tree service revision ${REVISION}"
-
-# regexp below find all link to source code files which end with ".pb.go" and retrieve the file names
-# we use `[^.]*` as non greedy workaround for `.*`
-FILES=$(curl -s https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/${REVISION}/pkg/services/tree | sed -n "s,.*\"/TrueCloudLab/frostfs-node/src/commit/${REVISION}/pkg/services/tree/\([^.]*\.pb\.go\)\".*,\1,p")
-
-for file in $FILES; do
- if [[ $file == *"frostfs"* ]]; then
- echo "skip '$file'"
- continue
- else
- echo "sync '$file' in tree service"
- fi
- curl -s "https://git.frostfs.info/TrueCloudLab/frostfs-node/raw/commit/${REVISION}/pkg/services/tree/${file}" -o "./internal/frostfs/services/tree/${file}"
-done
\ No newline at end of file
From 2ccb43bc8cfcb38fc968c83679e7e2aef5bf29cc Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Fri, 9 Jun 2023 09:39:40 +0300
Subject: [PATCH 015/161] [#59] Update docs
Signed-off-by: Denis Kirillov
---
CHANGELOG.md | 3 +++
config/config.env | 5 +----
config/config.yaml | 4 ----
docs/gate-configuration.md | 2 +-
4 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 11d43e8..ff51ce8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,9 @@ This document outlines major changes between releases.
- Separate integration tests with build tags (#24)
- Changed values for `frostfs_http_gw_state_health` metric (#32)
+### Removed
+- Drop `tree.service` param (now endpoints from `peers` section are used) (#59)
+
### Updating from neofs-http-gw v0.26.0
To set system attributes use updated headers
diff --git a/config/config.env b/config/config.env
index 4dc9bb2..debdca2 100644
--- a/config/config.env
+++ b/config/config.env
@@ -93,9 +93,6 @@ HTTP_GW_POOL_ERROR_THRESHOLD=100
# Enable zip compression to download files by common prefix.
HTTP_GW_ZIP_COMPRESSION=false
-# Endpoint of the tree service. Must be provided. Can be one of the node address (from the `peers` section).
-HTTP_GW_TREE_SERVICE=grpc://s01.frostfs.devenv:8080
-
HTTP_GW_TRACING_ENABLED=true
HTTP_GW_TRACING_ENDPOINT="localhost:4317"
-HTTP_GW_TRACING_EXPORTER="otlp_grpc"
\ No newline at end of file
+HTTP_GW_TRACING_EXPORTER="otlp_grpc"
diff --git a/config/config.yaml b/config/config.yaml
index a71c69d..510cb43 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -93,10 +93,6 @@ resolve_order:
upload_header:
use_default_timestamp: false # Create timestamp for object if it isn't provided by header.
-# Endpoint of the tree service. Must be provided. Can be one of the node address (from the `peers` section).
-tree:
- service: 127.0.0.1:8080
-
connect_timeout: 5s # Timeout to dial node.
stream_timeout: 10s # Timeout for individual operations in streaming RPC.
request_timeout: 5s # Timeout to check node health during rebalance.
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 0d0504f..5b4edae 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -52,7 +52,7 @@ $ cat http.log
| `zip` | [ZIP configuration](#zip-section) |
| `pprof` | [Pprof configuration](#pprof-section) |
| `prometheus` | [Prometheus configuration](#prometheus-section) |
-| `tracing` | [Tracing configuration](#tracing-section) |
+| `tracing` | [Tracing configuration](#tracing-section) |
# General section
From 6f64557a4bf57d69aafe010cb8f0d05a8b572c07 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 14 Jun 2023 11:27:33 +0300
Subject: [PATCH 016/161] [#60] Use gRPC interceptor from observability package
Previous SDK implementation had implicit gRPC interceptor
for tracing. Now pool constructors allow any dial options,
so gateway should manually pass tracing interceptors from
observability package.
Signed-off-by: Alex Vanin
---
settings.go | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/settings.go b/settings.go
index eec014c..3b2cba9 100644
--- a/settings.go
+++ b/settings.go
@@ -13,6 +13,7 @@ import (
"time"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
+ grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
treepool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@@ -454,6 +455,19 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
prm.SetLogger(logger)
prmTree.SetLogger(logger)
+ var apiGRPCDialOpts []grpc.DialOption
+ var treeGRPCDialOpts = []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
+ if cfg.GetBool(cfgTracingEnabled) {
+ interceptors := []grpc.DialOption{
+ grpc.WithUnaryInterceptor(grpctracing.NewUnaryClientInteceptor()),
+ grpc.WithStreamInterceptor(grpctracing.NewStreamClientInterceptor()),
+ }
+ treeGRPCDialOpts = append(treeGRPCDialOpts, interceptors...)
+ apiGRPCDialOpts = append(apiGRPCDialOpts, interceptors...)
+ }
+ prm.SetGRPCDialOptions(apiGRPCDialOpts...)
+ prmTree.SetGRPCDialOptions(treeGRPCDialOpts...)
+
p, err := pool.NewPool(prm)
if err != nil {
logger.Fatal("failed to create connection pool", zap.Error(err))
@@ -463,7 +477,6 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
logger.Fatal("failed to dial connection pool", zap.Error(err))
}
- prmTree.SetGRPCDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials()))
treePool, err := treepool.NewPool(prmTree)
if err != nil {
logger.Fatal("failed to create tree pool", zap.Error(err))
From d9122e20939ace7833f572fcc19c57ec8a777d5e Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Fri, 7 Jul 2023 16:40:13 +0300
Subject: [PATCH 017/161] [#62] Update sdk to support grpc schemes in tree pool
Signed-off-by: Denis Kirillov
---
go.mod | 4 ++--
go.sum | 8 ++++----
settings.go | 3 +--
3 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/go.mod b/go.mod
index dd89afb..e51b7ed 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,9 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.19
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230531114046-62edd68f47ac
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230704092742-285516a94ebe
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230707115716-fe35373d8f1b
github.com/fasthttp/router v1.4.1
github.com/nspcc-dev/neo-go v0.101.1
github.com/prometheus/client_golang v1.15.1
diff --git a/go.sum b/go.sum
index 6d0e55c..0b13573 100644
--- a/go.sum
+++ b/go.sum
@@ -37,16 +37,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230531114046-62edd68f47ac h1:a6/Zc5BejflmguShwbllgJdEehnM9gshkLrLbKQHCU0=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230531114046-62edd68f47ac/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230704092742-285516a94ebe h1:SB102RiEg+4h9qcwyG97zHBtwduMRbedbtkwRDVSps8=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230704092742-285516a94ebe/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M=
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/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe h1:47lrWXcl36ayN7AJ9IW7sDDnTj//RUyHoIZOsjbYAYA=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230608140155-9d40228cecbe/go.mod h1:w+s3ozlbFfTDFHhjX0A3Iif3BRtnTkwiACxFZD+Q0cQ=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230707115716-fe35373d8f1b h1:nFcOeS2lMmuFdouEqA9SrmzvU/gt46VSjiBj2zZpMQ8=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230707115716-fe35373d8f1b/go.mod h1:r5Fir/4jCVXzdfOyCUbikSDB99nVqnHNq7mzVcidnlA=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
diff --git a/settings.go b/settings.go
index 3b2cba9..c402335 100644
--- a/settings.go
+++ b/settings.go
@@ -23,7 +23,6 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
- "google.golang.org/grpc/credentials/insecure"
)
const (
@@ -456,7 +455,7 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
prmTree.SetLogger(logger)
var apiGRPCDialOpts []grpc.DialOption
- var treeGRPCDialOpts = []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
+ var treeGRPCDialOpts []grpc.DialOption
if cfg.GetBool(cfgTracingEnabled) {
interceptors := []grpc.DialOption{
grpc.WithUnaryInterceptor(grpctracing.NewUnaryClientInteceptor()),
From 6fac6341c21d557677154b6953ddc5ff77542de1 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 12 Jul 2023 11:30:50 +0300
Subject: [PATCH 018/161] Release v0.27.0
Signed-off-by: Alex Vanin
---
CHANGELOG.md | 36 +++++++++++++++++++++++++++++-------
VERSION | 2 +-
2 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff51ce8..48463c1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,25 +4,46 @@ This document outlines major changes between releases.
## [Unreleased]
+### Fixed
+- `grpc` schemas in tree configuration (#62)
+
### Added
-- Multiple configs support (TrueCloudLab#12)
- Support dump metrics descriptions (#29)
-- Support impersonate bearer token (#40)
+- Support impersonate bearer token (#40, #45)
+- Tracing support (#20, #44, #60)
+- Object name resolving with tree service (#30)
### Changed
- Update prometheus to v1.15.0 (#35)
-- Update go version to 1.18 (TrueCloudLab#9)
- Update go version to 1.19 (#50)
+- Finish rebranding (#2)
+
+### Removed
+- Drop `tree.service` param (now endpoints from `peers` section are used) (#59)
+
+## [0.27.0] - Karpinsky - 2023-07-12
+
+This is a first FrostFS HTTP Gateway release named after
+[Karpinsky glacier](https://en.wikipedia.org/wiki/Karpinsky_Glacier).
+
+### Fixed
+- Require only one healthy storage server to start (#7)
+- Enable gate metrics (#38)
+- `Too many pings` error (#61)
+
+### Added
+- Multiple configs support (#12)
+
+### Changed
+- Repository rebranding (#1)
- Update neo-go to v0.101.0 (#8)
- Update viper to v1.15.0 (#8)
+- Update go version to 1.18 (#9)
- Errors have become more detailed (#18)
- Update system attribute names (#22)
- Separate integration tests with build tags (#24)
- Changed values for `frostfs_http_gw_state_health` metric (#32)
-### Removed
-- Drop `tree.service` param (now endpoints from `peers` section are used) (#59)
-
### Updating from neofs-http-gw v0.26.0
To set system attributes use updated headers
@@ -38,4 +59,5 @@ To set system attributes use updated headers
This project is a fork of [NeoFS HTTP Gateway](https://github.com/nspcc-dev/neofs-http-gw) from version v0.26.0.
To see CHANGELOG for older versions, refer to https://github.com/nspcc-dev/neofs-http-gw/blob/master/CHANGELOG.md.
-[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/72734ab4...master
+[0.27.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/72734ab4...v0.27.0
+[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.27.0...master
diff --git a/VERSION b/VERSION
index eaf8bae..0a8bf80 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.26.0
+v0.27.0
From 0882d344a2b9dce327b9cd0791031e2f613db0c7 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 11 Jul 2023 13:25:55 +0300
Subject: [PATCH 019/161] [#63] Use forked actions during workflows
Signed-off-by: Alex Vanin
---
.forgejo/workflows/dco.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml
index b1ccd0e..6cbe4dd 100644
--- a/.forgejo/workflows/dco.yml
+++ b/.forgejo/workflows/dco.yml
@@ -15,6 +15,6 @@ jobs:
go-version: '1.20'
- name: Run commit format checker
- uses: https://git.alexvan.in/alexvanin/dco-go@v1
+ uses: https://git.frostfs.info/TrueCloudLab/dco-go@v1
with:
from: adb95642d
From 97ac638dff829966d285c361198c85e4e5d74789 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 2 Aug 2023 17:11:44 +0300
Subject: [PATCH 020/161] [#67] Fix GetSubTree failures with updated SDK
Signed-off-by: Alex Vanin
---
go.mod | 8 ++++----
go.sum | 18 +++++++++---------
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/go.mod b/go.mod
index e51b7ed..632997f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,11 +3,11 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.19
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230704092742-285516a94ebe
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230707115716-fe35373d8f1b
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230802103237-363f153eafa6
github.com/fasthttp/router v1.4.1
- github.com/nspcc-dev/neo-go v0.101.1
+ github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc
github.com/prometheus/client_golang v1.15.1
github.com/prometheus/client_model v0.3.0
github.com/spf13/pflag v1.0.5
@@ -68,7 +68,7 @@ require (
github.com/morikuni/aec v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect
- github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb // indirect
+ github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce // indirect
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
diff --git a/go.sum b/go.sum
index 0b13573..f4c8c56 100644
--- a/go.sum
+++ b/go.sum
@@ -37,16 +37,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230704092742-285516a94ebe h1:SB102RiEg+4h9qcwyG97zHBtwduMRbedbtkwRDVSps8=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230704092742-285516a94ebe/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44 h1:v6JqBD/VzZx3QSxbaXnUwnnJ1KEYheU4LzLGr3IhsAE=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M=
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/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230707115716-fe35373d8f1b h1:nFcOeS2lMmuFdouEqA9SrmzvU/gt46VSjiBj2zZpMQ8=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230707115716-fe35373d8f1b/go.mod h1:r5Fir/4jCVXzdfOyCUbikSDB99nVqnHNq7mzVcidnlA=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230802103237-363f153eafa6 h1:u6lzNotV6MEMNEG/XeS7g+FjPrrf+j4gnOHtvun2KJc=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230802103237-363f153eafa6/go.mod h1:LI2GOj0pEx0jYTjB3QHja2PNhQFYL2pCm71RAFwDv0M=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
@@ -369,7 +369,7 @@ github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:r
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
-github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -687,11 +687,11 @@ github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkP
github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg=
github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM=
github.com/nspcc-dev/neo-go v0.99.4/go.mod h1:mKTolfRUfKjFso5HPvGSQtUZc70n0VKBMs16eGuC5gA=
-github.com/nspcc-dev/neo-go v0.101.1 h1:TVdcIpH/+bxQBTLRwWE3+Pw3j6j/JwguENbBSGAGid0=
-github.com/nspcc-dev/neo-go v0.101.1/go.mod h1:J4tspxWw7jknX06F+VSMsKvIiNpYGfVTb2IxVC005YU=
+github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc h1:fySIWvUQsitK5e5qYIHnTDCXuPpwzz89SEUEIyY11sg=
+github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc/go.mod h1:s9QhjMC784MWqTURovMbyYduIJc86mnCruxcMiAebpc=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220927123257-24c107e3a262/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb h1:GFxfkpXEYAbMIr69JpKOsQWeLOaGrd49HNAor8uDW+A=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20221202075445-cb5c18dc73eb/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce h1:vLGuUNDkmQrWMa4rr4vTd1u8ULqejWxVmNz1L7ocTEI=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg=
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
From cc69601b32a09290dae3ccdeab4eb39515251172 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Mon, 7 Aug 2023 12:08:34 +0300
Subject: [PATCH 021/161] [#66] Use gate key to form object owner
This is required because node check session token owner
https://git.frostfs.info/TrueCloudLab/frostfs-node/pulls/528
For client cut https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/issues/114
such owner will be gate owner
Signed-off-by: Denis Kirillov
---
CHANGELOG.md | 1 +
uploader/upload.go | 11 +++++------
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 48463c1..d323c89 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ This document outlines major changes between releases.
- Update prometheus to v1.15.0 (#35)
- Update go version to 1.19 (#50)
- Finish rebranding (#2)
+- Use gate key to form object owner (#66)
### Removed
- Drop `tree.service` param (now endpoints from `peers` section are used) (#59)
diff --git a/uploader/upload.go b/uploader/upload.go
index 43996ea..57e426d 100644
--- a/uploader/upload.go
+++ b/uploader/upload.go
@@ -145,17 +145,17 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
timestamp.SetValue(strconv.FormatInt(time.Now().Unix(), 10))
attributes = append(attributes, *timestamp)
}
- id, bt := u.fetchOwnerAndBearerToken(ctx)
obj := object.New()
obj.SetContainerID(*idCnr)
- obj.SetOwnerID(id)
+ obj.SetOwnerID(u.ownerID)
obj.SetAttributes(attributes...)
var prm pool.PrmObjectPut
prm.SetHeader(*obj)
prm.SetPayload(file)
+ bt := u.fetchBearerToken(ctx)
if bt != nil {
prm.UseBearer(*bt)
}
@@ -200,12 +200,11 @@ func (u *Uploader) handlePutFrostFSErr(r *fasthttp.RequestCtx, err error) {
response.Error(r, msg, statusCode)
}
-func (u *Uploader) fetchOwnerAndBearerToken(ctx context.Context) (*user.ID, *bearer.Token) {
+func (u *Uploader) fetchBearerToken(ctx context.Context) *bearer.Token {
if tkn, err := tokens.LoadBearerToken(ctx); err == nil && tkn != nil {
- issuer := bearer.ResolveIssuer(*tkn)
- return &issuer, tkn
+ return tkn
}
- return u.ownerID, nil
+ return nil
}
type putResponse struct {
From fa28f1ff824feb6635f44f49acf99d84bb5f6be6 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Sun, 27 Aug 2023 18:09:02 +0300
Subject: [PATCH 022/161] [#36] Move log messages to constants
Signed-off-by: Roman Loginov
---
app.go | 59 ++++++++++++++++----------------
downloader/download.go | 39 ++++++++++-----------
downloader/head.go | 3 +-
internal/logs/logs.go | 69 ++++++++++++++++++++++++++++++++++++++
metrics/service.go | 11 +++---
settings.go | 15 +++++----
uploader/filter.go | 3 +-
uploader/multipart.go | 5 +--
uploader/multipart_test.go | 5 +--
uploader/upload.go | 17 +++++-----
10 files changed, 152 insertions(+), 74 deletions(-)
create mode 100644 internal/logs/logs.go
diff --git a/app.go b/app.go
index 88f9a07..864f39c 100644
--- a/app.go
+++ b/app.go
@@ -12,6 +12,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/downloader"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
@@ -148,7 +149,7 @@ func (a *app) initResolver() {
var err error
a.resolver, err = resolver.NewContainerResolver(a.getResolverConfig())
if err != nil {
- a.log.Fatal("failed to create resolver", zap.Error(err))
+ a.log.Fatal(logs.FailedToCreateResolver, zap.Error(err))
}
}
@@ -161,11 +162,11 @@ func (a *app) getResolverConfig() ([]string, *resolver.Config) {
order := a.cfg.GetStringSlice(cfgResolveOrder)
if resolveCfg.RPCAddress == "" {
order = remove(order, resolver.NNSResolver)
- a.log.Warn(fmt.Sprintf("resolver '%s' won't be used since '%s' isn't provided", resolver.NNSResolver, cfgRPCEndpoint))
+ a.log.Warn(logs.ResolverNNSWontBeUsedSinceRPCEndpointIsntProvided)
}
if len(order) == 0 {
- a.log.Info("container resolver will be disabled because of resolvers 'resolver_order' is empty")
+ a.log.Info(logs.ContainerResolverWillBeDisabledBecauseOfResolversResolverOrderIsEmpty)
}
return order, resolveCfg
@@ -179,7 +180,7 @@ func (a *app) initMetrics() {
func newGateMetrics(logger *zap.Logger, provider *metrics.GateMetrics, enabled bool) *gateMetrics {
if !enabled {
- logger.Warn("metrics are disabled")
+ logger.Warn(logs.MetricsAreDisabled)
}
return &gateMetrics{
logger: logger,
@@ -190,7 +191,7 @@ func newGateMetrics(logger *zap.Logger, provider *metrics.GateMetrics, enabled b
func (m *gateMetrics) SetEnabled(enabled bool) {
if !enabled {
- m.logger.Warn("metrics are disabled")
+ m.logger.Warn(logs.MetricsAreDisabled)
}
m.mu.Lock()
@@ -243,7 +244,7 @@ func getFrostFSKey(cfg *viper.Viper, log *zap.Logger) (*keys.PrivateKey, error)
walletPath := cfg.GetString(cfgWalletPath)
if len(walletPath) == 0 {
- log.Info("no wallet path specified, creating ephemeral key automatically for this run")
+ log.Info(logs.NoWalletPathSpecifiedCreatingEphemeralKeyAutomaticallyForThisRun)
key, err := keys.NewPrivateKey()
if err != nil {
return nil, err
@@ -300,7 +301,7 @@ func getKeyFromWallet(w *wallet.Wallet, addrStr string, password *string) (*keys
}
func (a *app) Wait() {
- a.log.Info("starting application", zap.String("app_name", "frostfs-http-gw"), zap.String("version", Version))
+ a.log.Info(logs.StartingApplication, zap.String("app_name", "frostfs-http-gw"), zap.String("version", Version))
a.metrics.SetVersion(Version)
a.setHealthStatus()
@@ -324,9 +325,9 @@ func (a *app) Serve() {
for i := range a.servers {
go func(i int) {
- a.log.Info("starting server", zap.String("address", a.servers[i].Address()))
+ a.log.Info(logs.StartingServer, zap.String("address", a.servers[i].Address()))
if err := a.webServer.Serve(a.servers[i].Listener()); err != nil && err != http.ErrServerClosed {
- a.log.Fatal("listen and serve", zap.Error(err))
+ a.log.Fatal(logs.ListenAndServe, zap.Error(err))
}
}(i)
}
@@ -344,7 +345,7 @@ LOOP:
}
}
- a.log.Info("shutting down web server", zap.Error(a.webServer.Shutdown()))
+ a.log.Info(logs.ShuttingDownWebServer, zap.Error(a.webServer.Shutdown()))
a.metrics.Shutdown()
a.stopServices()
@@ -359,33 +360,33 @@ func (a *app) shutdownTracing() {
defer cancel()
if err := tracing.Shutdown(shdnCtx); err != nil {
- a.log.Warn("failed to shutdown tracing", zap.Error(err))
+ a.log.Warn(logs.FailedToShutdownTracing, zap.Error(err))
}
}
func (a *app) configReload(ctx context.Context) {
- a.log.Info("SIGHUP config reload started")
+ a.log.Info(logs.SIGHUPConfigReloadStarted)
if !a.cfg.IsSet(cmdConfig) && !a.cfg.IsSet(cmdConfigDir) {
- a.log.Warn("failed to reload config because it's missed")
+ a.log.Warn(logs.FailedToReloadConfigBecauseItsMissed)
return
}
if err := readInConfig(a.cfg); err != nil {
- a.log.Warn("failed to reload config", zap.Error(err))
+ a.log.Warn(logs.FailedToReloadConfig, zap.Error(err))
return
}
if lvl, err := getLogLevel(a.cfg); err != nil {
- a.log.Warn("log level won't be updated", zap.Error(err))
+ a.log.Warn(logs.LogLevelWontBeUpdated, zap.Error(err))
} else {
a.logLevel.SetLevel(lvl)
}
if err := a.resolver.UpdateResolvers(a.getResolverConfig()); err != nil {
- a.log.Warn("failed to update resolvers", zap.Error(err))
+ a.log.Warn(logs.FailedToUpdateResolvers, zap.Error(err))
}
if err := a.updateServers(); err != nil {
- a.log.Warn("failed to reload server parameters", zap.Error(err))
+ a.log.Warn(logs.FailedToReloadServerParameters, zap.Error(err))
}
a.stopServices()
@@ -397,7 +398,7 @@ func (a *app) configReload(ctx context.Context) {
a.initTracing(ctx)
a.setHealthStatus()
- a.log.Info("SIGHUP config reload completed")
+ a.log.Info(logs.SIGHUPConfigReloadCompleted)
}
func (a *app) updateSettings() {
@@ -436,22 +437,22 @@ func (a *app) configureRouter(uploadRoutes *uploader.Uploader, downloadRoutes *d
response.Error(r, "Method Not Allowed", fasthttp.StatusMethodNotAllowed)
}
r.POST("/upload/{cid}", a.logger(a.tokenizer(a.tracer(uploadRoutes.Upload))))
- a.log.Info("added path /upload/{cid}")
+ a.log.Info(logs.AddedPathUploadCid)
r.GET("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.DownloadByAddressOrBucketName))))
r.HEAD("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.HeadByAddressOrBucketName))))
- a.log.Info("added path /get/{cid}/{oid}")
+ a.log.Info(logs.AddedPathGetCidOid)
r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.DownloadByAttribute))))
r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.HeadByAttribute))))
- a.log.Info("added path /get_by_attribute/{cid}/{attr_key}/{attr_val:*}")
+ a.log.Info(logs.AddedPathGetByAttributeCidAttrKeyAttrVal)
r.GET("/zip/{cid}/{prefix:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.DownloadZipped))))
- a.log.Info("added path /zip/{cid}/{prefix}")
+ a.log.Info(logs.AddedPathZipCidPrefix)
a.webServer.Handler = r.Handler
}
func (a *app) logger(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
- a.log.Info("request", zap.String("remote", req.RemoteAddr().String()),
+ a.log.Info(logs.Request, zap.String("remote", req.RemoteAddr().String()),
zap.ByteString("method", req.Method()),
zap.ByteString("path", req.Path()),
zap.ByteString("query", req.QueryArgs().QueryString()),
@@ -464,7 +465,7 @@ func (a *app) tokenizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
appCtx, err := tokens.StoreBearerTokenAppCtx(a.ctx, req)
if err != nil {
- a.log.Error("could not fetch and store bearer token", zap.Error(err))
+ a.log.Error(logs.CouldNotFetchAndStoreBearerToken, zap.Error(err))
response.Error(req, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
}
utils.SetContextToRequest(appCtx, req)
@@ -507,16 +508,16 @@ func (a *app) initServers(ctx context.Context) {
}
srv, err := newServer(ctx, serverInfo)
if err != nil {
- a.log.Warn("failed to add server", append(fields, zap.Error(err))...)
+ a.log.Warn(logs.FailedToAddServer, append(fields, zap.Error(err))...)
continue
}
a.servers = append(a.servers, srv)
- a.log.Info("add server", fields...)
+ a.log.Info(logs.AddServer, fields...)
}
if len(a.servers) == 0 {
- a.log.Fatal("no healthy servers")
+ a.log.Fatal(logs.NoHealthyServers)
}
}
@@ -569,9 +570,9 @@ func (a *app) initTracing(ctx context.Context) {
}
updated, err := tracing.Setup(ctx, cfg)
if err != nil {
- a.log.Warn("failed to initialize tracing", zap.Error(err))
+ a.log.Warn(logs.FailedToInitializeTracing, zap.Error(err))
}
if updated {
- a.log.Info("tracing config updated")
+ a.log.Info(logs.TracingConfigUpdated)
}
}
diff --git a/downloader/download.go b/downloader/download.go
index 2a08c82..cd7f72f 100644
--- a/downloader/download.go
+++ b/downloader/download.go
@@ -15,6 +15,7 @@ import (
"strings"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
@@ -134,7 +135,7 @@ func receiveFile(ctx context.Context, req request, clnt *pool.Pool, objectAddres
case object.AttributeTimestamp:
value, err := strconv.ParseInt(val, 10, 64)
if err != nil {
- req.log.Info("couldn't parse creation date",
+ req.log.Info(logs.CouldntParseCreationDate,
zap.String("key", key),
zap.String("val", val),
zap.Error(err))
@@ -157,7 +158,7 @@ func receiveFile(ctx context.Context, req request, clnt *pool.Pool, objectAddres
return rObj.Payload, nil
})
if err != nil && err != io.EOF {
- req.log.Error("could not detect Content-Type from payload", zap.Error(err))
+ req.log.Error(logs.CouldNotDetectContentTypeFromPayload, zap.Error(err))
response.Error(req.RequestCtx, "could not detect Content-Type from payload: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -195,7 +196,7 @@ func (r *request) handleFrostFSErr(err error, start time.Time) {
statusCode, msg, additionalFields := response.FormErrorResponse("could not receive object", err)
logFields = append(logFields, additionalFields...)
- r.log.Error("could not receive object", logFields...)
+ r.log.Error(logs.CouldNotReceiveObject, logFields...)
response.Error(r.RequestCtx, msg, statusCode)
}
@@ -264,14 +265,14 @@ func (d *Downloader) byAddress(c *fasthttp.RequestCtx, f func(context.Context, r
cnrID, err := utils.GetContainerID(ctx, idCnr, d.containerResolver)
if err != nil {
- log.Error("wrong container id", zap.Error(err))
+ log.Error(logs.WrongContainerID, zap.Error(err))
response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
return
}
objID := new(oid.ID)
if err = objID.DecodeString(idObj); err != nil {
- log.Error("wrong object id", zap.Error(err))
+ log.Error(logs.WrongObjectID, zap.Error(err))
response.Error(c, "wrong object id", fasthttp.StatusBadRequest)
return
}
@@ -296,19 +297,19 @@ func (d *Downloader) byBucketname(req *fasthttp.RequestCtx, f func(context.Conte
cnrID, err := utils.GetContainerID(ctx, bucketname, d.containerResolver)
if err != nil {
- log.Error("wrong container id", zap.Error(err))
+ log.Error(logs.WrongContainerID, zap.Error(err))
response.Error(req, "wrong container id", fasthttp.StatusBadRequest)
return
}
foundOid, err := d.tree.GetLatestVersion(ctx, cnrID, key)
if err != nil {
- log.Error("object wasn't found", zap.Error(err))
+ log.Error(logs.ObjectWasntFound, zap.Error(err))
response.Error(req, "object wasn't found", fasthttp.StatusNotFound)
return
}
if foundOid.DeleteMarker {
- log.Error("object was deleted")
+ log.Error(logs.ObjectWasDeleted)
response.Error(req, "object deleted", fasthttp.StatusNotFound)
return
}
@@ -338,14 +339,14 @@ func (d *Downloader) byAttribute(c *fasthttp.RequestCtx, f func(context.Context,
containerID, err := utils.GetContainerID(ctx, scid, d.containerResolver)
if err != nil {
- log.Error("wrong container id", zap.Error(err))
+ log.Error(logs.WrongContainerID, zap.Error(err))
response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
return
}
res, err := d.search(ctx, containerID, key, val, object.MatchStringEqual)
if err != nil {
- log.Error("could not search for objects", zap.Error(err))
+ log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -357,12 +358,12 @@ func (d *Downloader) byAttribute(c *fasthttp.RequestCtx, f func(context.Context,
n, err := res.Read(buf)
if n == 0 {
if errors.Is(err, io.EOF) {
- log.Error("object not found", zap.Error(err))
+ log.Error(logs.ObjectNotFound, zap.Error(err))
response.Error(c, "object not found", fasthttp.StatusNotFound)
return
}
- log.Error("read object list failed", zap.Error(err))
+ log.Error(logs.ReadObjectListFailed, zap.Error(err))
response.Error(c, "read object list failed: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -424,7 +425,7 @@ func (d *Downloader) DownloadZipped(c *fasthttp.RequestCtx) {
containerID, err := utils.GetContainerID(ctx, scid, d.containerResolver)
if err != nil {
- log.Error("wrong container id", zap.Error(err))
+ log.Error(logs.WrongContainerID, zap.Error(err))
response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
return
}
@@ -433,7 +434,7 @@ func (d *Downloader) DownloadZipped(c *fasthttp.RequestCtx) {
// otherwise we get this error only in object iteration step
// and client get 200 OK.
if _, err = d.getContainer(ctx, *containerID); err != nil {
- log.Error("could not check container existence", zap.Error(err))
+ log.Error(logs.CouldNotCheckContainerExistence, zap.Error(err))
if client.IsErrContainerNotFound(err) {
response.Error(c, "Not Found", fasthttp.StatusNotFound)
return
@@ -444,7 +445,7 @@ func (d *Downloader) DownloadZipped(c *fasthttp.RequestCtx) {
resSearch, err := d.search(ctx, containerID, object.AttributeFilePath, prefix, object.MatchCommonPrefix)
if err != nil {
- log.Error("could not search for objects", zap.Error(err))
+ log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -476,19 +477,19 @@ func (d *Downloader) DownloadZipped(c *fasthttp.RequestCtx) {
addr.SetObject(id)
if err = d.zipObject(ctx, zipWriter, addr, btoken, bufZip); err != nil {
- log.Error("failed to add object to archive", zap.String("oid", id.EncodeToString()), zap.Error(err))
+ log.Error(logs.FailedToAddObjectToArchive, zap.String("oid", id.EncodeToString()), zap.Error(err))
}
return false
})
if errIter != nil {
- log.Error("iterating over selected objects failed", zap.Error(errIter))
+ log.Error(logs.IteratingOverSelectedObjectsFailed, zap.Error(errIter))
} else if !called {
- log.Error("objects not found")
+ log.Error(logs.ObjectsNotFound)
}
if err = zipWriter.Close(); err != nil {
- log.Error("close zip writer", zap.Error(err))
+ log.Error(logs.CloseZipWriter, zap.Error(err))
}
})
}
diff --git a/downloader/head.go b/downloader/head.go
index 3e5a92a..76dfd93 100644
--- a/downloader/head.go
+++ b/downloader/head.go
@@ -7,6 +7,7 @@ import (
"strconv"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@@ -57,7 +58,7 @@ func headObject(ctx context.Context, req request, clnt *pool.Pool, objectAddress
case object.AttributeTimestamp:
value, err := strconv.ParseInt(val, 10, 64)
if err != nil {
- req.log.Info("couldn't parse creation date",
+ req.log.Info(logs.CouldntParseCreationDate,
zap.String("key", key),
zap.String("val", val),
zap.Error(err))
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
new file mode 100644
index 0000000..d47a2c3
--- /dev/null
+++ b/internal/logs/logs.go
@@ -0,0 +1,69 @@
+package logs
+
+const (
+ CouldntParseCreationDate = "couldn't parse creation date" // Info in ../../downloader/*
+ CouldNotDetectContentTypeFromPayload = "could not detect Content-Type from payload" // Error in ../../downloader/download.go
+ CouldNotReceiveObject = "could not receive object" // Error in ../../downloader/download.go
+ WrongContainerID = "wrong container id" // Error in ../../downloader/download.go and uploader/upload.go
+ WrongObjectID = "wrong object id" // Error in ../../downloader/download.go
+ ObjectWasntFound = "object wasn't found" // Error in ../../downloader/download.go
+ ObjectWasDeleted = "object was deleted" // Error in ../../downloader/download.go
+ CouldNotSearchForObjects = "could not search for objects" // Error in ../../downloader/download.go
+ ObjectNotFound = "object not found" // Error in ../../downloader/download.go
+ ReadObjectListFailed = "read object list failed" // Error in ../../downloader/download.go
+ CouldNotCheckContainerExistence = "could not check container existence" // Error in ../../downloader/download.go
+ FailedToAddObjectToArchive = "failed to add object to archive" // Error in ../../downloader/download.go
+ IteratingOverSelectedObjectsFailed = "iterating over selected objects failed" // Error in ../../downloader/download.go
+ ObjectsNotFound = "objects not found" // Error in ../../downloader/download.go
+ CloseZipWriter = "close zip writer" // Error in ../../downloader/download.go
+ ServiceIsRunning = "service is running" // Info in ../../metrics/service.go
+ ServiceCouldntStartOnConfiguredPort = "service couldn't start on configured port" // Warn in ../../metrics/service.go
+ ServiceHasntStartedSinceItsDisabled = "service hasn't started since it's disabled" // Info in ../../metrics/service.go
+ ShuttingDownService = "shutting down service" // Info in ../../metrics/service.go
+ CantShutDownService = "can't shut down service" // Panic in ../../metrics/service.go
+ IgnorePartEmptyFormName = "ignore part, empty form name" // Debug in ../../uploader/upload.go
+ IgnorePartEmptyFilename = "ignore part, empty filename" // Debug in ../../uploader/upload.go
+ CloseTemporaryMultipartFormFile = "close temporary multipart/form file" // Debug in ../../uploader/upload.go
+ CouldNotReceiveMultipartForm = "could not receive multipart/form" // Error in ../../uploader/upload.go
+ CouldNotProcessHeaders = "could not process headers" // Error in ../../uploader/upload.go
+ CouldNotParseClientTime = "could not parse client time" // Warn in ../../uploader/upload.go
+ CouldNotPrepareExpirationHeader = "could not prepare expiration header" // Error in ../../uploader/upload.go
+ CouldNotEncodeResponse = "could not encode response" // Error in ../../uploader/upload.go
+ CouldNotStoreFileInFrostfs = "could not store file in frostfs" // Error in ../../uploader/upload.go
+ AddAttributeToResultObject = "add attribute to result object" // Debug in ../../uploader/filter.go
+ FailedToCreateResolver = "failed to create resolver" // Fatal in ../../app.go
+ ContainerResolverWillBeDisabledBecauseOfResolversResolverOrderIsEmpty = "container resolver will be disabled because of resolvers 'resolver_order' is empty" // Info in ../../app.go
+ MetricsAreDisabled = "metrics are disabled" // Warn in ../../app.go
+ NoWalletPathSpecifiedCreatingEphemeralKeyAutomaticallyForThisRun = "no wallet path specified, creating ephemeral key automatically for this run" // Info in ../../app.go
+ StartingApplication = "starting application" // Info in ../../app.go
+ StartingServer = "starting server" // Info in ../../app.go
+ ListenAndServe = "listen and serve" // Fatal in ../../app.go
+ ShuttingDownWebServer = "shutting down web server" // Info in ../../app.go
+ FailedToShutdownTracing = "failed to shutdown tracing" // Warn in ../../app.go
+ SIGHUPConfigReloadStarted = "SIGHUP config reload started" // Info in ../../app.go
+ FailedToReloadConfigBecauseItsMissed = "failed to reload config because it's missed" // Warn in ../../app.go
+ FailedToReloadConfig = "failed to reload config" // Warn in ../../app.go
+ LogLevelWontBeUpdated = "log level won't be updated" // Warn in ../../app.go
+ FailedToUpdateResolvers = "failed to update resolvers" // Warn in ../../app.go
+ FailedToReloadServerParameters = "failed to reload server parameters" // Warn in ../../app.go
+ SIGHUPConfigReloadCompleted = "SIGHUP config reload completed" // Info in ../../app.go
+ AddedPathUploadCid = "added path /upload/{cid}" // Info in ../../app.go
+ AddedPathGetCidOid = "added path /get/{cid}/{oid}" // Info in ../../app.go
+ AddedPathGetByAttributeCidAttrKeyAttrVal = "added path /get_by_attribute/{cid}/{attr_key}/{attr_val:*}" // Info in ../../app.go
+ AddedPathZipCidPrefix = "added path /zip/{cid}/{prefix}" // Info in ../../app.go
+ Request = "request" // Info in ../../app.go
+ CouldNotFetchAndStoreBearerToken = "could not fetch and store bearer token" // Error in ../../app.go
+ FailedToAddServer = "failed to add server" // Warn in ../../app.go
+ AddServer = "add server" // Info in ../../app.go
+ NoHealthyServers = "no healthy servers" // Fatal in ../../app.go
+ FailedToInitializeTracing = "failed to initialize tracing" // Warn in ../../app.go
+ TracingConfigUpdated = "tracing config updated" // Info in ../../app.go
+ ResolverNNSWontBeUsedSinceRPCEndpointIsntProvided = "resolver nns won't be used since rpc_endpoint isn't provided" // Warn in ../../app.go
+ CouldNotLoadFrostFSPrivateKey = "could not load FrostFS private key" // Fatal in ../../settings.go
+ UsingCredentials = "using credentials" // Info in ../../settings.go
+ FailedToCreateConnectionPool = "failed to create connection pool" // Fatal in ../../settings.go
+ FailedToDialConnectionPool = "failed to dial connection pool" // Fatal in ../../settings.go
+ FailedToCreateTreePool = "failed to create tree pool" // Fatal in ../../settings.go
+ FailedToDialTreePool = "failed to dial tree pool" // Fatal in ../../settings.go
+ AddedStoragePeer = "added storage peer" // Info in ../../settings.go
+)
diff --git a/metrics/service.go b/metrics/service.go
index 7cb3ca2..c025f06 100644
--- a/metrics/service.go
+++ b/metrics/service.go
@@ -4,6 +4,7 @@ import (
"context"
"net/http"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"go.uber.org/zap"
)
@@ -24,21 +25,21 @@ type Config struct {
// Start runs http service with the exposed endpoint on the configured port.
func (ms *Service) Start() {
if ms.enabled {
- ms.log.Info("service is running", zap.String("endpoint", ms.Addr))
+ ms.log.Info(logs.ServiceIsRunning, zap.String("endpoint", ms.Addr))
err := ms.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
- ms.log.Warn("service couldn't start on configured port")
+ ms.log.Warn(logs.ServiceCouldntStartOnConfiguredPort)
}
} else {
- ms.log.Info("service hasn't started since it's disabled")
+ ms.log.Info(logs.ServiceHasntStartedSinceItsDisabled)
}
}
// ShutDown stops the service.
func (ms *Service) ShutDown(ctx context.Context) {
- ms.log.Info("shutting down service", zap.String("endpoint", ms.Addr))
+ ms.log.Info(logs.ShuttingDownService, zap.String("endpoint", ms.Addr))
err := ms.Shutdown(ctx)
if err != nil {
- ms.log.Panic("can't shut down service")
+ ms.log.Panic(logs.CantShutDownService)
}
}
diff --git a/settings.go b/settings.go
index c402335..ed13da2 100644
--- a/settings.go
+++ b/settings.go
@@ -12,6 +12,7 @@ import (
"strings"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
@@ -403,7 +404,7 @@ func fetchServers(v *viper.Viper) []ServerInfo {
func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.Pool, *treepool.Pool, *keys.PrivateKey) {
key, err := getFrostFSKey(cfg, logger)
if err != nil {
- logger.Fatal("could not load FrostFS private key", zap.Error(err))
+ logger.Fatal(logs.CouldNotLoadFrostFSPrivateKey, zap.Error(err))
}
var prm pool.InitParameters
@@ -411,7 +412,7 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
prm.SetKey(&key.PrivateKey)
prmTree.SetKey(key)
- logger.Info("using credentials", zap.String("FrostFS", hex.EncodeToString(key.PublicKey().Bytes())))
+ logger.Info(logs.UsingCredentials, zap.String("FrostFS", hex.EncodeToString(key.PublicKey().Bytes())))
for _, peer := range fetchPeers(logger, cfg) {
prm.AddNode(peer)
@@ -469,19 +470,19 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
p, err := pool.NewPool(prm)
if err != nil {
- logger.Fatal("failed to create connection pool", zap.Error(err))
+ logger.Fatal(logs.FailedToCreateConnectionPool, zap.Error(err))
}
if err = p.Dial(ctx); err != nil {
- logger.Fatal("failed to dial connection pool", zap.Error(err))
+ logger.Fatal(logs.FailedToDialConnectionPool, zap.Error(err))
}
treePool, err := treepool.NewPool(prmTree)
if err != nil {
- logger.Fatal("failed to create tree pool", zap.Error(err))
+ logger.Fatal(logs.FailedToCreateTreePool, zap.Error(err))
}
if err = treePool.Dial(ctx); err != nil {
- logger.Fatal("failed to dial tree pool", zap.Error(err))
+ logger.Fatal(logs.FailedToDialTreePool, zap.Error(err))
}
return p, treePool, key
@@ -507,7 +508,7 @@ func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.NodeParam {
nodes = append(nodes, pool.NewNodeParam(priority, address, weight))
- l.Info("added storage peer",
+ l.Info(logs.AddedStoragePeer,
zap.Int("priority", priority),
zap.String("address", address),
zap.Float64("weight", weight))
diff --git a/uploader/filter.go b/uploader/filter.go
index 35de625..70d6eef 100644
--- a/uploader/filter.go
+++ b/uploader/filter.go
@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
@@ -47,7 +48,7 @@ func filterHeaders(l *zap.Logger, header *fasthttp.RequestHeader) (map[string]st
result[k] = v
- l.Debug("add attribute to result object",
+ l.Debug(logs.AddAttributeToResultObject,
zap.String("key", k),
zap.String("val", v))
})
diff --git a/uploader/multipart.go b/uploader/multipart.go
index cda4b34..135ee88 100644
--- a/uploader/multipart.go
+++ b/uploader/multipart.go
@@ -3,6 +3,7 @@ package uploader
import (
"io"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/uploader/multipart"
"go.uber.org/zap"
)
@@ -28,7 +29,7 @@ func fetchMultipartFile(l *zap.Logger, r io.Reader, boundary string) (MultipartF
name := part.FormName()
if name == "" {
- l.Debug("ignore part, empty form name")
+ l.Debug(logs.IgnorePartEmptyFormName)
continue
}
@@ -36,7 +37,7 @@ func fetchMultipartFile(l *zap.Logger, r io.Reader, boundary string) (MultipartF
// ignore multipart/form-data values
if filename == "" {
- l.Debug("ignore part, empty filename", zap.String("form", name))
+ l.Debug(logs.IgnorePartEmptyFilename, zap.String("form", name))
continue
}
diff --git a/uploader/multipart_test.go b/uploader/multipart_test.go
index aad2b66..d19cd5e 100644
--- a/uploader/multipart_test.go
+++ b/uploader/multipart_test.go
@@ -10,6 +10,7 @@ import (
"os"
"testing"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
@@ -111,7 +112,7 @@ func fetchMultipartFileDefault(l *zap.Logger, r io.Reader, boundary string) (Mul
name := part.FormName()
if name == "" {
- l.Debug("ignore part, empty form name")
+ l.Debug(logs.IgnorePartEmptyFormName)
continue
}
@@ -119,7 +120,7 @@ func fetchMultipartFileDefault(l *zap.Logger, r io.Reader, boundary string) (Mul
// ignore multipart/form-data values
if filename == "" {
- l.Debug("ignore part, empty filename", zap.String("form", name))
+ l.Debug(logs.IgnorePartEmptyFilename, zap.String("form", name))
continue
}
diff --git a/uploader/upload.go b/uploader/upload.go
index 57e426d..2832043 100644
--- a/uploader/upload.go
+++ b/uploader/upload.go
@@ -8,6 +8,7 @@ import (
"strconv"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
@@ -77,7 +78,7 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
idCnr, err := utils.GetContainerID(ctx, scid, u.containerResolver)
if err != nil {
- log.Error("wrong container id", zap.Error(err))
+ log.Error(logs.WrongContainerID, zap.Error(err))
response.Error(req, "wrong container id", fasthttp.StatusBadRequest)
return
}
@@ -89,7 +90,7 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
}
err := file.Close()
log.Debug(
- "close temporary multipart/form file",
+ logs.CloseTemporaryMultipartFormFile,
zap.Stringer("address", addr),
zap.String("filename", file.FileName()),
zap.Error(err),
@@ -97,13 +98,13 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
}()
boundary := string(req.Request.Header.MultipartFormBoundary())
if file, err = fetchMultipartFile(u.log, bodyStream, boundary); err != nil {
- log.Error("could not receive multipart/form", zap.Error(err))
+ log.Error(logs.CouldNotReceiveMultipartForm, zap.Error(err))
response.Error(req, "could not receive multipart/form: "+err.Error(), fasthttp.StatusBadRequest)
return
}
filtered, err := filterHeaders(u.log, &req.Request.Header)
if err != nil {
- log.Error("could not process headers", zap.Error(err))
+ log.Error(logs.CouldNotProcessHeaders, zap.Error(err))
response.Error(req, err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -111,14 +112,14 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
now := time.Now()
if rawHeader := req.Request.Header.Peek(fasthttp.HeaderDate); rawHeader != nil {
if parsed, err := time.Parse(http.TimeFormat, string(rawHeader)); err != nil {
- log.Warn("could not parse client time", zap.String("Date header", string(rawHeader)), zap.Error(err))
+ log.Warn(logs.CouldNotParseClientTime, zap.String("Date header", string(rawHeader)), zap.Error(err))
} else {
now = parsed
}
}
if err = utils.PrepareExpirationHeader(req, u.pool, filtered, now); err != nil {
- log.Error("could not prepare expiration header", zap.Error(err))
+ log.Error(logs.CouldNotPrepareExpirationHeader, zap.Error(err))
response.Error(req, "could not prepare expiration header: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -170,7 +171,7 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
// Try to return the response, otherwise, if something went wrong, throw an error.
if err = newPutResponse(addr).encode(req); err != nil {
- log.Error("could not encode response", zap.Error(err))
+ log.Error(logs.CouldNotEncodeResponse, zap.Error(err))
response.Error(req, "could not encode response", fasthttp.StatusBadRequest)
return
@@ -196,7 +197,7 @@ func (u *Uploader) handlePutFrostFSErr(r *fasthttp.RequestCtx, err error) {
statusCode, msg, additionalFields := response.FormErrorResponse("could not store file in frostfs", err)
logFields := append([]zap.Field{zap.Error(err)}, additionalFields...)
- u.log.Error("could not store file in frostfs", logFields...)
+ u.log.Error(logs.CouldNotStoreFileInFrostfs, logFields...)
response.Error(r, msg, statusCode)
}
From 54eadc3c31c317779853b714f919394a233f8952 Mon Sep 17 00:00:00 2001
From: Dmitriy Zabolotskiy
Date: Fri, 18 Aug 2023 17:21:59 +0300
Subject: [PATCH 023/161] [#69] Debian package: move home dir to match other
components
Signed-off-by: Dmitriy Zabolotskiy
---
debian/frostfs-http-gw.postinst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/debian/frostfs-http-gw.postinst b/debian/frostfs-http-gw.postinst
index 360ceef..70deea5 100755
--- a/debian/frostfs-http-gw.postinst
+++ b/debian/frostfs-http-gw.postinst
@@ -21,7 +21,7 @@ set -e
case "$1" in
configure)
USERNAME=http
- id -u frostfs-$USERNAME >/dev/null 2>&1 || useradd -s /usr/sbin/nologin -d /srv/frostfs_cache --system -M -U -c "FrostFS HTTP gateway" frostfs-$USERNAME
+ id -u frostfs-$USERNAME >/dev/null 2>&1 || useradd -s /usr/sbin/nologin -d /var/lib/frostfs/$USERNAME --system -M -U -c "FrostFS HTTP gateway" frostfs-$USERNAME
if ! dpkg-statoverride --list /etc/frostfs/$USERNAME >/dev/null; then
chown -f root:frostfs-$USERNAME /etc/frostfs/$USERNAME
chown -f root:frostfs-$USERNAME /etc/frostfs/$USERNAME/config.yaml || true
@@ -29,7 +29,7 @@ case "$1" in
chmod -f 0640 /etc/frostfs/$USERNAME/config.yaml || true
fi
USERDIR=$(getent passwd "frostfs-$USERNAME" | cut -d: -f6)
- if ! dpkg-statoverride --list frostfs-"$USERDIR" >/dev/null; then
+ if ! dpkg-statoverride --list "$USERDIR" >/dev/null; then
chown -f frostfs-$USERNAME: "$USERDIR"
fi
;;
From 7d47e88e36818a028ff56d9190a2e796c0adacd0 Mon Sep 17 00:00:00 2001
From: Marina Biryukova
Date: Mon, 28 Aug 2023 14:09:40 +0300
Subject: [PATCH 024/161] [#76] Add go1.21 to CI
Signed-off-by: Marina Biryukova
---
.forgejo/workflows/builds.yml | 2 +-
.forgejo/workflows/dco.yml | 2 +-
.forgejo/workflows/tests.yml | 2 +-
.forgejo/workflows/vulncheck.yml | 2 +-
Dockerfile | 2 +-
Makefile | 2 +-
go.mod | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/.forgejo/workflows/builds.yml b/.forgejo/workflows/builds.yml
index 3df5081..aac6857 100644
--- a/.forgejo/workflows/builds.yml
+++ b/.forgejo/workflows/builds.yml
@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.19', '1.20' ]
+ go_versions: [ '1.20', '1.21' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml
index 6cbe4dd..3d38c4b 100644
--- a/.forgejo/workflows/dco.yml
+++ b/.forgejo/workflows/dco.yml
@@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v3
with:
- go-version: '1.20'
+ go-version: '1.21'
- name: Run commit format checker
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v1
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index 7a03020..d448a87 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.19', '1.20' ]
+ go_versions: [ '1.20', '1.21' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
diff --git a/.forgejo/workflows/vulncheck.yml b/.forgejo/workflows/vulncheck.yml
index 0c9e908..0139e89 100644
--- a/.forgejo/workflows/vulncheck.yml
+++ b/.forgejo/workflows/vulncheck.yml
@@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v3
with:
- go-version: '1.20'
+ go-version: '1.21'
- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest
diff --git a/Dockerfile b/Dockerfile
index 189dc22..8b450a4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.19-alpine as basebuilder
+FROM golang:1.21-alpine as basebuilder
RUN apk add --update make bash ca-certificates
FROM basebuilder as builder
diff --git a/Makefile b/Makefile
index a4db526..6b6fa72 100755
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
REPO ?= $(shell go list -m)
VERSION ?= $(shell git describe --tags --match "v*" --dirty --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
-GO_VERSION ?= 1.19
+GO_VERSION ?= 1.20
LINT_VERSION ?= 1.49.0
BUILD ?= $(shell date -u --iso=seconds)
diff --git a/go.mod b/go.mod
index 632997f..6e6bc80 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module git.frostfs.info/TrueCloudLab/frostfs-http-gw
-go 1.19
+go 1.20
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
From dbc6804d273bf6b9df7b8f9a813e20e5c73835eb Mon Sep 17 00:00:00 2001
From: Marina Biryukova
Date: Tue, 29 Aug 2023 15:17:20 +0300
Subject: [PATCH 025/161] [#77] Add metrics for HTTP endpoint status
Signed-off-by: Marina Biryukova
---
app.go | 36 ++++++++++++++++++++++++++++--------
metrics/desc.go | 10 ++++++++++
metrics/metrics.go | 39 ++++++++++++++++++++++++++++++++++++---
3 files changed, 74 insertions(+), 11 deletions(-)
diff --git a/app.go b/app.go
index 864f39c..be9aaea 100644
--- a/app.go
+++ b/app.go
@@ -189,6 +189,13 @@ func newGateMetrics(logger *zap.Logger, provider *metrics.GateMetrics, enabled b
}
}
+func (m *gateMetrics) isEnabled() bool {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+
+ return m.enabled
+}
+
func (m *gateMetrics) SetEnabled(enabled bool) {
if !enabled {
m.logger.Warn(logs.MetricsAreDisabled)
@@ -200,23 +207,17 @@ func (m *gateMetrics) SetEnabled(enabled bool) {
}
func (m *gateMetrics) SetHealth(status metrics.HealthStatus) {
- m.mu.RLock()
- if !m.enabled {
- m.mu.RUnlock()
+ if !m.isEnabled() {
return
}
- m.mu.RUnlock()
m.provider.SetHealth(status)
}
func (m *gateMetrics) SetVersion(ver string) {
- m.mu.RLock()
- if !m.enabled {
- m.mu.RUnlock()
+ if !m.isEnabled() {
return
}
- m.mu.RUnlock()
m.provider.SetVersion(ver)
}
@@ -231,6 +232,22 @@ func (m *gateMetrics) Shutdown() {
m.mu.Unlock()
}
+func (m *gateMetrics) MarkHealthy(endpoint string) {
+ if !m.isEnabled() {
+ return
+ }
+
+ m.provider.MarkHealthy(endpoint)
+}
+
+func (m *gateMetrics) MarkUnhealthy(endpoint string) {
+ if !m.isEnabled() {
+ return
+ }
+
+ m.provider.MarkUnhealthy(endpoint)
+}
+
func remove(list []string, element string) []string {
for i, item := range list {
if item == element {
@@ -327,6 +344,7 @@ func (a *app) Serve() {
go func(i int) {
a.log.Info(logs.StartingServer, zap.String("address", a.servers[i].Address()))
if err := a.webServer.Serve(a.servers[i].Listener()); err != nil && err != http.ErrServerClosed {
+ a.metrics.MarkUnhealthy(a.servers[i].Address())
a.log.Fatal(logs.ListenAndServe, zap.Error(err))
}
}(i)
@@ -508,9 +526,11 @@ func (a *app) initServers(ctx context.Context) {
}
srv, err := newServer(ctx, serverInfo)
if err != nil {
+ a.metrics.MarkUnhealthy(serverInfo.Address)
a.log.Warn(logs.FailedToAddServer, append(fields, zap.Error(err))...)
continue
}
+ a.metrics.MarkHealthy(serverInfo.Address)
a.servers = append(a.servers, srv)
a.log.Info(logs.AddServer, fields...)
diff --git a/metrics/desc.go b/metrics/desc.go
index f2ff4f4..e10050c 100644
--- a/metrics/desc.go
+++ b/metrics/desc.go
@@ -66,6 +66,16 @@ var appMetricsDesc = map[string]map[string]Description{
VariableLabels: []string{"version"},
},
},
+ serverSubsystem: {
+ healthMetric: Description{
+ Type: dto.MetricType_GAUGE,
+ Namespace: namespace,
+ Subsystem: serverSubsystem,
+ Name: healthMetric,
+ Help: "HTTP Server endpoint health",
+ VariableLabels: []string{"endpoint"},
+ },
+ },
}
type Description struct {
diff --git a/metrics/metrics.go b/metrics/metrics.go
index cf22099..bfb66ee 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -10,9 +10,10 @@ import (
)
const (
- namespace = "frostfs_http_gw"
- stateSubsystem = "state"
- poolSubsystem = "pool"
+ namespace = "frostfs_http_gw"
+ stateSubsystem = "state"
+ poolSubsystem = "pool"
+ serverSubsystem = "server"
)
const (
@@ -60,9 +61,14 @@ type StatisticScraper interface {
Statistic() pool.Statistic
}
+type serverMetrics struct {
+ endpointHealth *prometheus.GaugeVec
+}
+
type GateMetrics struct {
stateMetrics
poolMetricsCollector
+ serverMetrics
}
type stateMetrics struct {
@@ -87,15 +93,20 @@ func NewGateMetrics(p StatisticScraper) *GateMetrics {
poolMetric := newPoolMetricsCollector(p)
poolMetric.register()
+ serverMetric := newServerMetrics()
+ serverMetric.register()
+
return &GateMetrics{
stateMetrics: *stateMetric,
poolMetricsCollector: *poolMetric,
+ serverMetrics: *serverMetric,
}
}
func (g *GateMetrics) Unregister() {
g.stateMetrics.unregister()
prometheus.Unregister(&g.poolMetricsCollector)
+ g.serverMetrics.unregister()
}
func newStateMetrics() *stateMetrics {
@@ -192,6 +203,28 @@ func (m *poolMetricsCollector) updateRequestsDuration(node pool.NodeStatistic) {
m.requestDuration.WithLabelValues(node.Address(), methodCreateSession).Set(float64(node.AverageCreateSession().Milliseconds()))
}
+func newServerMetrics() *serverMetrics {
+ return &serverMetrics{
+ endpointHealth: mustNewGaugeVec(appMetricsDesc[serverSubsystem][healthMetric]),
+ }
+}
+
+func (m serverMetrics) register() {
+ prometheus.MustRegister(m.endpointHealth)
+}
+
+func (m serverMetrics) unregister() {
+ prometheus.Unregister(m.endpointHealth)
+}
+
+func (m serverMetrics) MarkHealthy(endpoint string) {
+ m.endpointHealth.WithLabelValues(endpoint).Set(float64(1))
+}
+
+func (m serverMetrics) MarkUnhealthy(endpoint string) {
+ m.endpointHealth.WithLabelValues(endpoint).Set(float64(0))
+}
+
// NewPrometheusService creates a new service for gathering prometheus metrics.
func NewPrometheusService(log *zap.Logger, cfg Config) *Service {
if log == nil {
From 834d5b93e5d137b5847dbf7342ee806eb9cd5b80 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Fri, 1 Sep 2023 14:19:26 +0300
Subject: [PATCH 026/161] [#69] Fix postinstall script
Post install script changes rights for user dir.
With change of user dir (home dir), this dir
isn't craeted anymore, so post install script
fails. This commit changes useradd flag `-m` to
create user dir.
Signed-off-by: Alex Vanin
---
debian/frostfs-http-gw.postinst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/debian/frostfs-http-gw.postinst b/debian/frostfs-http-gw.postinst
index 70deea5..1f25055 100755
--- a/debian/frostfs-http-gw.postinst
+++ b/debian/frostfs-http-gw.postinst
@@ -21,7 +21,7 @@ set -e
case "$1" in
configure)
USERNAME=http
- id -u frostfs-$USERNAME >/dev/null 2>&1 || useradd -s /usr/sbin/nologin -d /var/lib/frostfs/$USERNAME --system -M -U -c "FrostFS HTTP gateway" frostfs-$USERNAME
+ id -u frostfs-$USERNAME >/dev/null 2>&1 || useradd -s /usr/sbin/nologin -d /var/lib/frostfs/$USERNAME --system -m -U -c "FrostFS HTTP gateway" frostfs-$USERNAME
if ! dpkg-statoverride --list /etc/frostfs/$USERNAME >/dev/null; then
chown -f root:frostfs-$USERNAME /etc/frostfs/$USERNAME
chown -f root:frostfs-$USERNAME /etc/frostfs/$USERNAME/config.yaml || true
From 40568590c73c0ece76afa6fd61cde73ecdd6f7e1 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Thu, 31 Aug 2023 19:19:57 +0300
Subject: [PATCH 027/161] [#72] Support soft memory limit setting
Signed-off-by: Roman Loginov
---
app.go | 21 ++++++
config/config.env | 2 +
config/config.yaml | 3 +
docs/gate-configuration.md | 13 ++++
internal/logs/logs.go | 132 +++++++++++++++++++------------------
settings.go | 15 +++++
6 files changed, 121 insertions(+), 65 deletions(-)
diff --git a/app.go b/app.go
index be9aaea..a0d6e0e 100644
--- a/app.go
+++ b/app.go
@@ -6,6 +6,7 @@ import (
"net/http"
"os"
"os/signal"
+ "runtime/debug"
"sync"
"syscall"
"time"
@@ -128,6 +129,8 @@ func newApp(ctx context.Context, opt ...Option) App {
user.IDFromKey(&owner, a.key.PrivateKey.PublicKey)
a.owner = &owner
+ a.setRuntimeParameters()
+
a.initAppSettings()
a.initResolver()
a.initMetrics()
@@ -407,6 +410,8 @@ func (a *app) configReload(ctx context.Context) {
a.log.Warn(logs.FailedToReloadServerParameters, zap.Error(err))
}
+ a.setRuntimeParameters()
+
a.stopServices()
a.startServices()
@@ -596,3 +601,19 @@ func (a *app) initTracing(ctx context.Context) {
a.log.Info(logs.TracingConfigUpdated)
}
}
+
+func (a *app) setRuntimeParameters() {
+ if len(os.Getenv("GOMEMLIMIT")) != 0 {
+ // default limit < yaml limit < app env limit < GOMEMLIMIT
+ a.log.Warn(logs.RuntimeSoftMemoryDefinedWithGOMEMLIMIT)
+ return
+ }
+
+ softMemoryLimit := fetchSoftMemoryLimit(a.cfg)
+ previous := debug.SetMemoryLimit(softMemoryLimit)
+ if softMemoryLimit != previous {
+ a.log.Info(logs.RuntimeSoftMemoryLimitUpdated,
+ zap.Int64("new_value", softMemoryLimit),
+ zap.Int64("old_value", previous))
+ }
+}
diff --git a/config/config.env b/config/config.env
index debdca2..62920a2 100644
--- a/config/config.env
+++ b/config/config.env
@@ -96,3 +96,5 @@ HTTP_GW_ZIP_COMPRESSION=false
HTTP_GW_TRACING_ENABLED=true
HTTP_GW_TRACING_ENDPOINT="localhost:4317"
HTTP_GW_TRACING_EXPORTER="otlp_grpc"
+
+HTTP_GW_RUNTIME_SOFT_MEMORY_LIMIT=1073741824
diff --git a/config/config.yaml b/config/config.yaml
index 510cb43..d2804d6 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -101,3 +101,6 @@ pool_error_threshold: 100 # The number of errors on connection after which node
zip:
compression: false # Enable zip compression to download files by common prefix.
+
+runtime:
+ soft_memory_limit: 1gb
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 5b4edae..95e6c8e 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -53,6 +53,7 @@ $ cat http.log
| `pprof` | [Pprof configuration](#pprof-section) |
| `prometheus` | [Prometheus configuration](#prometheus-section) |
| `tracing` | [Tracing configuration](#tracing-section) |
+| `runtime` | [Runtime configuration](#runtime-section) |
# General section
@@ -256,3 +257,15 @@ tracing:
| `enabled` | `bool` | yes | `false` | Flag to enable the tracing. |
| `exporter` | `string` | yes | | Trace collector type (`stdout` or `otlp_grpc` are supported). |
| `endpoint` | `string` | yes | | Address of collector endpoint for OTLP exporters. |
+
+# `runtime` section
+Contains runtime parameters.
+
+```yaml
+runtime:
+ soft_memory_limit: 1gb
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|---------------------|--------|---------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `soft_memory_limit` | `size` | yes | maxint64 | Soft memory limit for the runtime. Zero or no value stands for no limit. If `GOMEMLIMIT` environment variable is set, the value from the configuration file will be ignored. |
\ No newline at end of file
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index d47a2c3..ebb3c24 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -1,69 +1,71 @@
package logs
const (
- CouldntParseCreationDate = "couldn't parse creation date" // Info in ../../downloader/*
- CouldNotDetectContentTypeFromPayload = "could not detect Content-Type from payload" // Error in ../../downloader/download.go
- CouldNotReceiveObject = "could not receive object" // Error in ../../downloader/download.go
- WrongContainerID = "wrong container id" // Error in ../../downloader/download.go and uploader/upload.go
- WrongObjectID = "wrong object id" // Error in ../../downloader/download.go
- ObjectWasntFound = "object wasn't found" // Error in ../../downloader/download.go
- ObjectWasDeleted = "object was deleted" // Error in ../../downloader/download.go
- CouldNotSearchForObjects = "could not search for objects" // Error in ../../downloader/download.go
- ObjectNotFound = "object not found" // Error in ../../downloader/download.go
- ReadObjectListFailed = "read object list failed" // Error in ../../downloader/download.go
- CouldNotCheckContainerExistence = "could not check container existence" // Error in ../../downloader/download.go
- FailedToAddObjectToArchive = "failed to add object to archive" // Error in ../../downloader/download.go
- IteratingOverSelectedObjectsFailed = "iterating over selected objects failed" // Error in ../../downloader/download.go
- ObjectsNotFound = "objects not found" // Error in ../../downloader/download.go
- CloseZipWriter = "close zip writer" // Error in ../../downloader/download.go
- ServiceIsRunning = "service is running" // Info in ../../metrics/service.go
- ServiceCouldntStartOnConfiguredPort = "service couldn't start on configured port" // Warn in ../../metrics/service.go
- ServiceHasntStartedSinceItsDisabled = "service hasn't started since it's disabled" // Info in ../../metrics/service.go
- ShuttingDownService = "shutting down service" // Info in ../../metrics/service.go
- CantShutDownService = "can't shut down service" // Panic in ../../metrics/service.go
- IgnorePartEmptyFormName = "ignore part, empty form name" // Debug in ../../uploader/upload.go
- IgnorePartEmptyFilename = "ignore part, empty filename" // Debug in ../../uploader/upload.go
- CloseTemporaryMultipartFormFile = "close temporary multipart/form file" // Debug in ../../uploader/upload.go
- CouldNotReceiveMultipartForm = "could not receive multipart/form" // Error in ../../uploader/upload.go
- CouldNotProcessHeaders = "could not process headers" // Error in ../../uploader/upload.go
- CouldNotParseClientTime = "could not parse client time" // Warn in ../../uploader/upload.go
- CouldNotPrepareExpirationHeader = "could not prepare expiration header" // Error in ../../uploader/upload.go
- CouldNotEncodeResponse = "could not encode response" // Error in ../../uploader/upload.go
- CouldNotStoreFileInFrostfs = "could not store file in frostfs" // Error in ../../uploader/upload.go
- AddAttributeToResultObject = "add attribute to result object" // Debug in ../../uploader/filter.go
- FailedToCreateResolver = "failed to create resolver" // Fatal in ../../app.go
- ContainerResolverWillBeDisabledBecauseOfResolversResolverOrderIsEmpty = "container resolver will be disabled because of resolvers 'resolver_order' is empty" // Info in ../../app.go
- MetricsAreDisabled = "metrics are disabled" // Warn in ../../app.go
- NoWalletPathSpecifiedCreatingEphemeralKeyAutomaticallyForThisRun = "no wallet path specified, creating ephemeral key automatically for this run" // Info in ../../app.go
- StartingApplication = "starting application" // Info in ../../app.go
- StartingServer = "starting server" // Info in ../../app.go
- ListenAndServe = "listen and serve" // Fatal in ../../app.go
- ShuttingDownWebServer = "shutting down web server" // Info in ../../app.go
- FailedToShutdownTracing = "failed to shutdown tracing" // Warn in ../../app.go
- SIGHUPConfigReloadStarted = "SIGHUP config reload started" // Info in ../../app.go
- FailedToReloadConfigBecauseItsMissed = "failed to reload config because it's missed" // Warn in ../../app.go
- FailedToReloadConfig = "failed to reload config" // Warn in ../../app.go
- LogLevelWontBeUpdated = "log level won't be updated" // Warn in ../../app.go
- FailedToUpdateResolvers = "failed to update resolvers" // Warn in ../../app.go
- FailedToReloadServerParameters = "failed to reload server parameters" // Warn in ../../app.go
- SIGHUPConfigReloadCompleted = "SIGHUP config reload completed" // Info in ../../app.go
- AddedPathUploadCid = "added path /upload/{cid}" // Info in ../../app.go
- AddedPathGetCidOid = "added path /get/{cid}/{oid}" // Info in ../../app.go
- AddedPathGetByAttributeCidAttrKeyAttrVal = "added path /get_by_attribute/{cid}/{attr_key}/{attr_val:*}" // Info in ../../app.go
- AddedPathZipCidPrefix = "added path /zip/{cid}/{prefix}" // Info in ../../app.go
- Request = "request" // Info in ../../app.go
- CouldNotFetchAndStoreBearerToken = "could not fetch and store bearer token" // Error in ../../app.go
- FailedToAddServer = "failed to add server" // Warn in ../../app.go
- AddServer = "add server" // Info in ../../app.go
- NoHealthyServers = "no healthy servers" // Fatal in ../../app.go
- FailedToInitializeTracing = "failed to initialize tracing" // Warn in ../../app.go
- TracingConfigUpdated = "tracing config updated" // Info in ../../app.go
- ResolverNNSWontBeUsedSinceRPCEndpointIsntProvided = "resolver nns won't be used since rpc_endpoint isn't provided" // Warn in ../../app.go
- CouldNotLoadFrostFSPrivateKey = "could not load FrostFS private key" // Fatal in ../../settings.go
- UsingCredentials = "using credentials" // Info in ../../settings.go
- FailedToCreateConnectionPool = "failed to create connection pool" // Fatal in ../../settings.go
- FailedToDialConnectionPool = "failed to dial connection pool" // Fatal in ../../settings.go
- FailedToCreateTreePool = "failed to create tree pool" // Fatal in ../../settings.go
- FailedToDialTreePool = "failed to dial tree pool" // Fatal in ../../settings.go
- AddedStoragePeer = "added storage peer" // Info in ../../settings.go
+ CouldntParseCreationDate = "couldn't parse creation date" // Info in ../../downloader/*
+ CouldNotDetectContentTypeFromPayload = "could not detect Content-Type from payload" // Error in ../../downloader/download.go
+ CouldNotReceiveObject = "could not receive object" // Error in ../../downloader/download.go
+ WrongContainerID = "wrong container id" // Error in ../../downloader/download.go and uploader/upload.go
+ WrongObjectID = "wrong object id" // Error in ../../downloader/download.go
+ ObjectWasntFound = "object wasn't found" // Error in ../../downloader/download.go
+ ObjectWasDeleted = "object was deleted" // Error in ../../downloader/download.go
+ CouldNotSearchForObjects = "could not search for objects" // Error in ../../downloader/download.go
+ ObjectNotFound = "object not found" // Error in ../../downloader/download.go
+ ReadObjectListFailed = "read object list failed" // Error in ../../downloader/download.go
+ CouldNotCheckContainerExistence = "could not check container existence" // Error in ../../downloader/download.go
+ FailedToAddObjectToArchive = "failed to add object to archive" // Error in ../../downloader/download.go
+ IteratingOverSelectedObjectsFailed = "iterating over selected objects failed" // Error in ../../downloader/download.go
+ ObjectsNotFound = "objects not found" // Error in ../../downloader/download.go
+ CloseZipWriter = "close zip writer" // Error in ../../downloader/download.go
+ ServiceIsRunning = "service is running" // Info in ../../metrics/service.go
+ ServiceCouldntStartOnConfiguredPort = "service couldn't start on configured port" // Warn in ../../metrics/service.go
+ ServiceHasntStartedSinceItsDisabled = "service hasn't started since it's disabled" // Info in ../../metrics/service.go
+ ShuttingDownService = "shutting down service" // Info in ../../metrics/service.go
+ CantShutDownService = "can't shut down service" // Panic in ../../metrics/service.go
+ IgnorePartEmptyFormName = "ignore part, empty form name" // Debug in ../../uploader/upload.go
+ IgnorePartEmptyFilename = "ignore part, empty filename" // Debug in ../../uploader/upload.go
+ CloseTemporaryMultipartFormFile = "close temporary multipart/form file" // Debug in ../../uploader/upload.go
+ CouldNotReceiveMultipartForm = "could not receive multipart/form" // Error in ../../uploader/upload.go
+ CouldNotProcessHeaders = "could not process headers" // Error in ../../uploader/upload.go
+ CouldNotParseClientTime = "could not parse client time" // Warn in ../../uploader/upload.go
+ CouldNotPrepareExpirationHeader = "could not prepare expiration header" // Error in ../../uploader/upload.go
+ CouldNotEncodeResponse = "could not encode response" // Error in ../../uploader/upload.go
+ CouldNotStoreFileInFrostfs = "could not store file in frostfs" // Error in ../../uploader/upload.go
+ AddAttributeToResultObject = "add attribute to result object" // Debug in ../../uploader/filter.go
+ FailedToCreateResolver = "failed to create resolver" // Fatal in ../../app.go
+ ContainerResolverWillBeDisabledBecauseOfResolversResolverOrderIsEmpty = "container resolver will be disabled because of resolvers 'resolver_order' is empty" // Info in ../../app.go
+ MetricsAreDisabled = "metrics are disabled" // Warn in ../../app.go
+ NoWalletPathSpecifiedCreatingEphemeralKeyAutomaticallyForThisRun = "no wallet path specified, creating ephemeral key automatically for this run" // Info in ../../app.go
+ StartingApplication = "starting application" // Info in ../../app.go
+ StartingServer = "starting server" // Info in ../../app.go
+ ListenAndServe = "listen and serve" // Fatal in ../../app.go
+ ShuttingDownWebServer = "shutting down web server" // Info in ../../app.go
+ FailedToShutdownTracing = "failed to shutdown tracing" // Warn in ../../app.go
+ SIGHUPConfigReloadStarted = "SIGHUP config reload started" // Info in ../../app.go
+ FailedToReloadConfigBecauseItsMissed = "failed to reload config because it's missed" // Warn in ../../app.go
+ FailedToReloadConfig = "failed to reload config" // Warn in ../../app.go
+ LogLevelWontBeUpdated = "log level won't be updated" // Warn in ../../app.go
+ FailedToUpdateResolvers = "failed to update resolvers" // Warn in ../../app.go
+ FailedToReloadServerParameters = "failed to reload server parameters" // Warn in ../../app.go
+ SIGHUPConfigReloadCompleted = "SIGHUP config reload completed" // Info in ../../app.go
+ AddedPathUploadCid = "added path /upload/{cid}" // Info in ../../app.go
+ AddedPathGetCidOid = "added path /get/{cid}/{oid}" // Info in ../../app.go
+ AddedPathGetByAttributeCidAttrKeyAttrVal = "added path /get_by_attribute/{cid}/{attr_key}/{attr_val:*}" // Info in ../../app.go
+ AddedPathZipCidPrefix = "added path /zip/{cid}/{prefix}" // Info in ../../app.go
+ Request = "request" // Info in ../../app.go
+ CouldNotFetchAndStoreBearerToken = "could not fetch and store bearer token" // Error in ../../app.go
+ FailedToAddServer = "failed to add server" // Warn in ../../app.go
+ AddServer = "add server" // Info in ../../app.go
+ NoHealthyServers = "no healthy servers" // Fatal in ../../app.go
+ FailedToInitializeTracing = "failed to initialize tracing" // Warn in ../../app.go
+ TracingConfigUpdated = "tracing config updated" // Info in ../../app.go
+ ResolverNNSWontBeUsedSinceRPCEndpointIsntProvided = "resolver nns won't be used since rpc_endpoint isn't provided" // Warn in ../../app.go
+ RuntimeSoftMemoryDefinedWithGOMEMLIMIT = "soft runtime memory defined with GOMEMLIMIT environment variable, config value skipped" // Warn in ../../app.go
+ RuntimeSoftMemoryLimitUpdated = "soft runtime memory limit value updated" // Info in ../../app.go
+ CouldNotLoadFrostFSPrivateKey = "could not load FrostFS private key" // Fatal in ../../settings.go
+ UsingCredentials = "using credentials" // Info in ../../settings.go
+ FailedToCreateConnectionPool = "failed to create connection pool" // Fatal in ../../settings.go
+ FailedToDialConnectionPool = "failed to dial connection pool" // Fatal in ../../settings.go
+ FailedToCreateTreePool = "failed to create tree pool" // Fatal in ../../settings.go
+ FailedToDialTreePool = "failed to dial tree pool" // Fatal in ../../settings.go
+ AddedStoragePeer = "added storage peer" // Info in ../../settings.go
)
diff --git a/settings.go b/settings.go
index ed13da2..6708ad4 100644
--- a/settings.go
+++ b/settings.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/hex"
"fmt"
+ "math"
"os"
"path"
"runtime"
@@ -36,6 +37,8 @@ const (
defaultPoolErrorThreshold uint32 = 100
+ defaultSoftMemoryLimit = math.MaxInt64
+
cfgServer = "server"
cfgTLSEnabled = "tls.enabled"
cfgTLSCertFile = "tls.cert_file"
@@ -90,6 +93,9 @@ const (
// Zip compression.
cfgZipCompression = "zip.compression"
+ // Runtime.
+ cfgSoftMemoryLimit = "runtime.soft_memory_limit"
+
// Command line args.
cmdHelp = "help"
cmdVersion = "version"
@@ -516,3 +522,12 @@ func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.NodeParam {
return nodes
}
+
+func fetchSoftMemoryLimit(cfg *viper.Viper) int64 {
+ softMemoryLimit := cfg.GetSizeInBytes(cfgSoftMemoryLimit)
+ if softMemoryLimit <= 0 {
+ softMemoryLimit = defaultSoftMemoryLimit
+ }
+
+ return int64(softMemoryLimit)
+}
From add07a21ed4f4aa47649c2e97b706880afeec51d Mon Sep 17 00:00:00 2001
From: Marina Biryukova
Date: Mon, 4 Sep 2023 16:09:57 +0300
Subject: [PATCH 028/161] [#71] Add log constants linter
Signed-off-by: Marina Biryukova
---
.forgejo/workflows/tests.yml | 13 ++++++++++---
.golangci.yml | 11 +++++++++++
.pre-commit-config.yaml | 24 ++++++++++++++++++------
Makefile | 23 +++++++++++++++++++++--
4 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index d448a87..14b9edf 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -7,10 +7,17 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: golangci-lint
- uses: https://github.com/golangci/golangci-lint-action@v2
+ - name: Set up Go
+ uses: actions/setup-go@v3
with:
- version: latest
+ go-version: '1.21'
+ cache: true
+
+ - name: Install linters
+ run: make lint-install
+
+ - name: Run linters
+ run: make lint
tests:
name: Tests
diff --git a/.golangci.yml b/.golangci.yml
index a271450..5459bde 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -24,6 +24,16 @@ linters-settings:
govet:
# report about shadowed variables
check-shadowing: false
+ custom:
+ truecloudlab-linters:
+ path: bin/external_linters.so
+ original-url: git.frostfs.info/TrueCloudLab/linters.git
+ settings:
+ noliteral:
+ enable: true
+ target-methods: ["Fatal"]
+ disable-packages: ["req", "r"]
+ constants-package: "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
linters:
enable:
@@ -45,6 +55,7 @@ linters:
- gofmt
- whitespace
- goimports
+ - truecloudlab-linters
disable-all: true
fast: false
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4fde2a0..e97fc23 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -37,9 +37,21 @@ repos:
- repo: local
hooks:
- - id: go-unit-tests
- name: go unit tests
- entry: make test
- pass_filenames: false
- types: [go]
- language: system
+ - id: make-lint-install
+ name: install linters
+ entry: make lint-install
+ language: system
+ pass_filenames: false
+
+ - id: make-lint
+ name: run linters
+ entry: make lint
+ language: system
+ pass_filenames: false
+
+ - id: go-unit-tests
+ name: go unit tests
+ entry: make test
+ pass_filenames: false
+ types: [go]
+ language: system
diff --git a/Makefile b/Makefile
index 6b6fa72..c5296e1 100755
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,8 @@
REPO ?= $(shell go list -m)
VERSION ?= $(shell git describe --tags --match "v*" --dirty --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
GO_VERSION ?= 1.20
-LINT_VERSION ?= 1.49.0
+LINT_VERSION ?= 1.54.0
+TRUECLOUDLAB_LINT_VERSION ?= 0.0.2
BUILD ?= $(shell date -u --iso=seconds)
HUB_IMAGE ?= truecloudlab/frostfs-http-gw
@@ -11,6 +12,10 @@ HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
METRICS_DUMP_OUT ?= ./metrics-dump.json
+OUTPUT_LINT_DIR ?= $(shell pwd)/bin
+LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
+TMP_DIR := .cache
+
# List of binaries to build. For now just one.
BINDIR = bin
DIRS = $(BINDIR)
@@ -103,9 +108,23 @@ dirty-image:
-f Dockerfile.dirty \
-t $(HUB_IMAGE)-dirty:$(HUB_TAG) .
+# Install linters
+ lint-install:
+ @mkdir -p $(TMP_DIR)
+ @rm -rf $(TMP_DIR)/linters
+ @git -c advice.detachedHead=false clone --branch v$(TRUECLOUDLAB_LINT_VERSION) https://git.frostfs.info/TrueCloudLab/linters.git $(TMP_DIR)/linters
+ @@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
+ @rm -rf $(TMP_DIR)/linters
+ @rmdir $(TMP_DIR) 2>/dev/null || true
+ @CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
+
# Run linters
lint:
- @golangci-lint --timeout=5m run
+ @if [ ! -d "$(LINT_DIR)" ]; then \
+ echo "Run make lint-install"; \
+ exit 1; \
+ fi
+ $(LINT_DIR)/golangci-lint --timeout=5m run
# Run linters in Docker
docker/lint:
From d2199435426b673f89fef39e7234061bd476caad Mon Sep 17 00:00:00 2001
From: Marina Biryukova
Date: Thu, 31 Aug 2023 11:37:03 +0300
Subject: [PATCH 029/161] [#73] Uploader, downloader structures refactoring
Signed-off-by: Marina Biryukova
---
Dockerfile => .docker/Dockerfile | 0
Dockerfile.dirty => .docker/Dockerfile.dirty | 0
Makefile | 10 +-
app.go => cmd/http-gw/app.go | 39 +-
.../http-gw/integration_test.go | 2 +-
main.go => cmd/http-gw/main.go | 0
misc.go => cmd/http-gw/misc.go | 0
server.go => cmd/http-gw/server.go | 0
settings.go => cmd/http-gw/settings.go | 0
downloader/download.go | 537 ------------------
go.mod | 2 +-
{api => internal/api}/layer/tree_service.go | 2 +-
{api => internal/api}/tree.go | 0
internal/handler/download.go | 210 +++++++
{uploader => internal/handler}/filter.go | 2 +-
{uploader => internal/handler}/filter_test.go | 2 +-
internal/handler/handler.go | 193 +++++++
{downloader => internal/handler}/head.go | 18 +-
{uploader => internal/handler}/multipart.go | 4 +-
.../handler}/multipart/multipart.go | 0
.../handler}/multipart_test.go | 2 +-
internal/handler/reader.go | 141 +++++
.../handler}/reader_test.go | 2 +-
{uploader => internal/handler}/upload.go | 93 +--
internal/handler/utils.go | 60 ++
tree/tree.go | 4 +-
utils/util.go | 13 -
27 files changed, 672 insertions(+), 664 deletions(-)
rename Dockerfile => .docker/Dockerfile (100%)
rename Dockerfile.dirty => .docker/Dockerfile.dirty (100%)
rename app.go => cmd/http-gw/app.go (91%)
rename integration_test.go => cmd/http-gw/integration_test.go (99%)
rename main.go => cmd/http-gw/main.go (100%)
rename misc.go => cmd/http-gw/misc.go (100%)
rename server.go => cmd/http-gw/server.go (100%)
rename settings.go => cmd/http-gw/settings.go (100%)
delete mode 100644 downloader/download.go
rename {api => internal/api}/layer/tree_service.go (90%)
rename {api => internal/api}/tree.go (100%)
create mode 100644 internal/handler/download.go
rename {uploader => internal/handler}/filter.go (98%)
rename {uploader => internal/handler}/filter_test.go (98%)
create mode 100644 internal/handler/handler.go
rename {downloader => internal/handler}/head.go (86%)
rename {uploader => internal/handler}/multipart.go (92%)
rename {uploader => internal/handler}/multipart/multipart.go (100%)
rename {uploader => internal/handler}/multipart_test.go (99%)
create mode 100644 internal/handler/reader.go
rename {downloader => internal/handler}/reader_test.go (98%)
rename {uploader => internal/handler}/upload.go (73%)
create mode 100644 internal/handler/utils.go
diff --git a/Dockerfile b/.docker/Dockerfile
similarity index 100%
rename from Dockerfile
rename to .docker/Dockerfile
diff --git a/Dockerfile.dirty b/.docker/Dockerfile.dirty
similarity index 100%
rename from Dockerfile.dirty
rename to .docker/Dockerfile.dirty
diff --git a/Makefile b/Makefile
index c5296e1..d02d41b 100755
--- a/Makefile
+++ b/Makefile
@@ -18,8 +18,8 @@ TMP_DIR := .cache
# List of binaries to build. For now just one.
BINDIR = bin
-DIRS = $(BINDIR)
-BINS = $(BINDIR)/frostfs-http-gw
+CMDS = $(addprefix frostfs-, $(notdir $(wildcard cmd/*)))
+BINS = $(addprefix $(BINDIR)/, $(CMDS))
.PHONY: all $(BINS) $(DIRS) dep docker/ test cover fmt image image-push dirty-image lint docker/lint pre-commit unpre-commit version clean
@@ -37,7 +37,7 @@ $(BINS): $(DIRS) dep
CGO_ENABLED=0 \
go build -v -trimpath \
-ldflags "-X main.Version=$(VERSION)" \
- -o $@ ./
+ -o $@ ./cmd/$(subst frostfs-,,$(notdir $@))
$(DIRS):
@echo "⇒ Ensure dir: $@"
@@ -90,7 +90,7 @@ image:
--build-arg REPO=$(REPO) \
--build-arg VERSION=$(VERSION) \
--rm \
- -f Dockerfile \
+ -f .docker/Dockerfile \
-t $(HUB_IMAGE):$(HUB_TAG) .
# Push Docker image to the hub
@@ -105,7 +105,7 @@ dirty-image:
--build-arg REPO=$(REPO) \
--build-arg VERSION=$(VERSION) \
--rm \
- -f Dockerfile.dirty \
+ -f .docker/Dockerfile.dirty \
-t $(HUB_IMAGE)-dirty:$(HUB_TAG) .
# Install linters
diff --git a/app.go b/cmd/http-gw/app.go
similarity index 91%
rename from app.go
rename to cmd/http-gw/app.go
index a0d6e0e..40db433 100644
--- a/app.go
+++ b/cmd/http-gw/app.go
@@ -11,15 +11,14 @@ import (
"syscall"
"time"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/downloader"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/uploader"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
@@ -51,15 +50,10 @@ type (
resolver *resolver.ContainerResolver
metrics *gateMetrics
services []*metrics.Service
- settings *appSettings
+ settings *handler.Settings
servers []Server
}
- appSettings struct {
- Uploader *uploader.Settings
- Downloader *downloader.Settings
- }
-
// App is an interface for the main gateway function.
App interface {
Wait()
@@ -140,10 +134,7 @@ func newApp(ctx context.Context, opt ...Option) App {
}
func (a *app) initAppSettings() {
- a.settings = &appSettings{
- Uploader: &uploader.Settings{},
- Downloader: &downloader.Settings{},
- }
+ a.settings = &handler.Settings{}
a.updateSettings()
}
@@ -334,11 +325,10 @@ func (a *app) setHealthStatus() {
}
func (a *app) Serve() {
- uploadRoutes := uploader.New(a.AppParams(), a.settings.Uploader)
- downloadRoutes := downloader.New(a.AppParams(), a.settings.Downloader, tree.NewTree(services.NewPoolWrapper(a.treePool)))
+ handler := handler.New(a.AppParams(), a.settings, tree.NewTree(services.NewPoolWrapper(a.treePool)))
// Configure router.
- a.configureRouter(uploadRoutes, downloadRoutes)
+ a.configureRouter(handler)
a.startServices()
a.initServers(a.ctx)
@@ -425,8 +415,8 @@ func (a *app) configReload(ctx context.Context) {
}
func (a *app) updateSettings() {
- a.settings.Uploader.SetDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
- a.settings.Downloader.SetZipCompression(a.cfg.GetBool(cfgZipCompression))
+ a.settings.SetDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
+ a.settings.SetZipCompression(a.cfg.GetBool(cfgZipCompression))
}
func (a *app) startServices() {
@@ -450,7 +440,7 @@ func (a *app) stopServices() {
}
}
-func (a *app) configureRouter(uploadRoutes *uploader.Uploader, downloadRoutes *downloader.Downloader) {
+func (a *app) configureRouter(handler *handler.Handler) {
r := router.New()
r.RedirectTrailingSlash = true
r.NotFound = func(r *fasthttp.RequestCtx) {
@@ -459,15 +449,16 @@ func (a *app) configureRouter(uploadRoutes *uploader.Uploader, downloadRoutes *d
r.MethodNotAllowed = func(r *fasthttp.RequestCtx) {
response.Error(r, "Method Not Allowed", fasthttp.StatusMethodNotAllowed)
}
- r.POST("/upload/{cid}", a.logger(a.tokenizer(a.tracer(uploadRoutes.Upload))))
+
+ r.POST("/upload/{cid}", a.logger(a.tokenizer(a.tracer(handler.Upload))))
a.log.Info(logs.AddedPathUploadCid)
- r.GET("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.DownloadByAddressOrBucketName))))
- r.HEAD("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.HeadByAddressOrBucketName))))
+ r.GET("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(handler.DownloadByAddressOrBucketName))))
+ r.HEAD("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(handler.HeadByAddressOrBucketName))))
a.log.Info(logs.AddedPathGetCidOid)
- r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.DownloadByAttribute))))
- r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.HeadByAttribute))))
+ r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(handler.DownloadByAttribute))))
+ r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(handler.HeadByAttribute))))
a.log.Info(logs.AddedPathGetByAttributeCidAttrKeyAttrVal)
- r.GET("/zip/{cid}/{prefix:*}", a.logger(a.tokenizer(a.tracer(downloadRoutes.DownloadZipped))))
+ r.GET("/zip/{cid}/{prefix:*}", a.logger(a.tokenizer(a.tracer(handler.DownloadZipped))))
a.log.Info(logs.AddedPathZipCidPrefix)
a.webServer.Handler = r.Handler
diff --git a/integration_test.go b/cmd/http-gw/integration_test.go
similarity index 99%
rename from integration_test.go
rename to cmd/http-gw/integration_test.go
index 34506c4..76a8325 100644
--- a/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -83,7 +83,7 @@ func runServer() (App, context.CancelFunc) {
v := getDefaultConfig()
l, lvl := newLogger(v)
application := newApp(cancelCtx, WithConfig(v), WithLogger(l, lvl))
- go application.Serve(cancelCtx)
+ go application.Serve()
return application, cancel
}
diff --git a/main.go b/cmd/http-gw/main.go
similarity index 100%
rename from main.go
rename to cmd/http-gw/main.go
diff --git a/misc.go b/cmd/http-gw/misc.go
similarity index 100%
rename from misc.go
rename to cmd/http-gw/misc.go
diff --git a/server.go b/cmd/http-gw/server.go
similarity index 100%
rename from server.go
rename to cmd/http-gw/server.go
diff --git a/settings.go b/cmd/http-gw/settings.go
similarity index 100%
rename from settings.go
rename to cmd/http-gw/settings.go
diff --git a/downloader/download.go b/downloader/download.go
deleted file mode 100644
index cd7f72f..0000000
--- a/downloader/download.go
+++ /dev/null
@@ -1,537 +0,0 @@
-package downloader
-
-import (
- "archive/zip"
- "bufio"
- "bytes"
- "context"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "path"
- "strconv"
- "strings"
- "time"
-
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
- cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
- oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
- "github.com/valyala/fasthttp"
- "go.uber.org/atomic"
- "go.uber.org/zap"
-)
-
-type request struct {
- *fasthttp.RequestCtx
- log *zap.Logger
-}
-
-func isValidToken(s string) bool {
- for _, c := range s {
- if c <= ' ' || c > 127 {
- return false
- }
- if strings.ContainsRune("()<>@,;:\\\"/[]?={}", c) {
- return false
- }
- }
- return true
-}
-
-func isValidValue(s string) bool {
- for _, c := range s {
- // HTTP specification allows for more technically, but we don't want to escape things.
- if c < ' ' || c > 127 || c == '"' {
- return false
- }
- }
- return true
-}
-
-type readCloser struct {
- io.Reader
- io.Closer
-}
-
-// initializes io.Reader with the limited size and detects Content-Type from it.
-// Returns r's error directly. Also returns the processed data.
-func readContentType(maxSize uint64, rInit func(uint64) (io.Reader, error)) (string, []byte, error) {
- if maxSize > sizeToDetectType {
- maxSize = sizeToDetectType
- }
-
- buf := make([]byte, maxSize) // maybe sync-pool the slice?
-
- r, err := rInit(maxSize)
- if err != nil {
- return "", nil, err
- }
-
- n, err := r.Read(buf)
- if err != nil && err != io.EOF {
- return "", nil, err
- }
-
- buf = buf[:n]
-
- return http.DetectContentType(buf), buf, err // to not lose io.EOF
-}
-
-func receiveFile(ctx context.Context, req request, clnt *pool.Pool, objectAddress oid.Address) {
- var (
- err error
- dis = "inline"
- start = time.Now()
- filename string
- )
-
- var prm pool.PrmObjectGet
- prm.SetAddress(objectAddress)
- if btoken := bearerToken(ctx); btoken != nil {
- prm.UseBearer(*btoken)
- }
-
- rObj, err := clnt.GetObject(ctx, prm)
- if err != nil {
- req.handleFrostFSErr(err, start)
- return
- }
-
- // we can't close reader in this function, so how to do it?
-
- if req.Request.URI().QueryArgs().GetBool("download") {
- dis = "attachment"
- }
-
- payloadSize := rObj.Header.PayloadSize()
-
- req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
- var contentType string
- for _, attr := range rObj.Header.Attributes() {
- key := attr.Key()
- val := attr.Value()
- if !isValidToken(key) || !isValidValue(val) {
- continue
- }
-
- key = utils.BackwardTransformIfSystem(key)
-
- req.Response.Header.Set(utils.UserAttributeHeaderPrefix+key, val)
- switch key {
- case object.AttributeFileName:
- filename = val
- case object.AttributeTimestamp:
- value, err := strconv.ParseInt(val, 10, 64)
- if err != nil {
- req.log.Info(logs.CouldntParseCreationDate,
- zap.String("key", key),
- zap.String("val", val),
- zap.Error(err))
- continue
- }
- req.Response.Header.Set(fasthttp.HeaderLastModified,
- time.Unix(value, 0).UTC().Format(http.TimeFormat))
- case object.AttributeContentType:
- contentType = val
- }
- }
-
- idsToResponse(&req.Response, &rObj.Header)
-
- if len(contentType) == 0 {
- // determine the Content-Type from the payload head
- var payloadHead []byte
-
- contentType, payloadHead, err = readContentType(payloadSize, func(uint64) (io.Reader, error) {
- return rObj.Payload, nil
- })
- if err != nil && err != io.EOF {
- req.log.Error(logs.CouldNotDetectContentTypeFromPayload, zap.Error(err))
- response.Error(req.RequestCtx, "could not detect Content-Type from payload: "+err.Error(), fasthttp.StatusBadRequest)
- return
- }
-
- // reset payload reader since a part of the data has been read
- var headReader io.Reader = bytes.NewReader(payloadHead)
-
- if err != io.EOF { // otherwise, we've already read full payload
- headReader = io.MultiReader(headReader, rObj.Payload)
- }
-
- // note: we could do with io.Reader, but SetBodyStream below closes body stream
- // if it implements io.Closer and that's useful for us.
- rObj.Payload = readCloser{headReader, rObj.Payload}
- }
- req.SetContentType(contentType)
-
- req.Response.Header.Set(fasthttp.HeaderContentDisposition, dis+"; filename="+path.Base(filename))
-
- req.Response.SetBodyStream(rObj.Payload, int(payloadSize))
-}
-
-func bearerToken(ctx context.Context) *bearer.Token {
- if tkn, err := tokens.LoadBearerToken(ctx); err == nil {
- return tkn
- }
- return nil
-}
-
-func (r *request) handleFrostFSErr(err error, start time.Time) {
- logFields := []zap.Field{
- zap.Stringer("elapsed", time.Since(start)),
- zap.Error(err),
- }
- statusCode, msg, additionalFields := response.FormErrorResponse("could not receive object", err)
- logFields = append(logFields, additionalFields...)
-
- r.log.Error(logs.CouldNotReceiveObject, logFields...)
- response.Error(r.RequestCtx, msg, statusCode)
-}
-
-// Downloader is a download request handler.
-type Downloader struct {
- log *zap.Logger
- pool *pool.Pool
- containerResolver *resolver.ContainerResolver
- settings *Settings
- tree *tree.Tree
-}
-
-// Settings stores reloading parameters, so it has to provide atomic getters and setters.
-type Settings struct {
- zipCompression atomic.Bool
-}
-
-func (s *Settings) ZipCompression() bool {
- return s.zipCompression.Load()
-}
-
-func (s *Settings) SetZipCompression(val bool) {
- s.zipCompression.Store(val)
-}
-
-// New creates an instance of Downloader using specified options.
-func New(params *utils.AppParams, settings *Settings, tree *tree.Tree) *Downloader {
- return &Downloader{
- log: params.Logger,
- pool: params.Pool,
- settings: settings,
- containerResolver: params.Resolver,
- tree: tree,
- }
-}
-
-func (d *Downloader) newRequest(ctx *fasthttp.RequestCtx, log *zap.Logger) *request {
- return &request{
- RequestCtx: ctx,
- log: log,
- }
-}
-
-// DownloadByAddressOrBucketName handles download requests using simple cid/oid or bucketname/key format.
-func (d *Downloader) DownloadByAddressOrBucketName(c *fasthttp.RequestCtx) {
- test, _ := c.UserValue("oid").(string)
- var id oid.ID
- err := id.DecodeString(test)
- if err != nil {
- d.byBucketname(c, receiveFile)
- } else {
- d.byAddress(c, receiveFile)
- }
-}
-
-// byAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that
-// prepares request and object address to it.
-func (d *Downloader) byAddress(c *fasthttp.RequestCtx, f func(context.Context, request, *pool.Pool, oid.Address)) {
- var (
- idCnr, _ = c.UserValue("cid").(string)
- idObj, _ = c.UserValue("oid").(string)
- log = d.log.With(zap.String("cid", idCnr), zap.String("oid", idObj))
- )
-
- ctx := utils.GetContextFromRequest(c)
-
- cnrID, err := utils.GetContainerID(ctx, idCnr, d.containerResolver)
- if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
- return
- }
-
- objID := new(oid.ID)
- if err = objID.DecodeString(idObj); err != nil {
- log.Error(logs.WrongObjectID, zap.Error(err))
- response.Error(c, "wrong object id", fasthttp.StatusBadRequest)
- return
- }
-
- var addr oid.Address
- addr.SetContainer(*cnrID)
- addr.SetObject(*objID)
-
- f(ctx, *d.newRequest(c, log), d.pool, addr)
-}
-
-// byBucketname is a wrapper for function (e.g. request.headObject, request.receiveFile) that
-// prepares request and object address to it.
-func (d *Downloader) byBucketname(req *fasthttp.RequestCtx, f func(context.Context, request, *pool.Pool, oid.Address)) {
- var (
- bucketname = req.UserValue("cid").(string)
- key = req.UserValue("oid").(string)
- log = d.log.With(zap.String("bucketname", bucketname), zap.String("key", key))
- )
-
- ctx := utils.GetContextFromRequest(req)
-
- cnrID, err := utils.GetContainerID(ctx, bucketname, d.containerResolver)
- if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(req, "wrong container id", fasthttp.StatusBadRequest)
- return
- }
-
- foundOid, err := d.tree.GetLatestVersion(ctx, cnrID, key)
- if err != nil {
- log.Error(logs.ObjectWasntFound, zap.Error(err))
- response.Error(req, "object wasn't found", fasthttp.StatusNotFound)
- return
- }
- if foundOid.DeleteMarker {
- log.Error(logs.ObjectWasDeleted)
- response.Error(req, "object deleted", fasthttp.StatusNotFound)
- return
- }
-
- var addr oid.Address
- addr.SetContainer(*cnrID)
- addr.SetObject(foundOid.OID)
-
- f(ctx, *d.newRequest(req, log), d.pool, addr)
-}
-
-// DownloadByAttribute handles attribute-based download requests.
-func (d *Downloader) DownloadByAttribute(c *fasthttp.RequestCtx) {
- d.byAttribute(c, receiveFile)
-}
-
-// byAttribute is a wrapper similar to byAddress.
-func (d *Downloader) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, request, *pool.Pool, oid.Address)) {
- var (
- scid, _ = c.UserValue("cid").(string)
- key, _ = url.QueryUnescape(c.UserValue("attr_key").(string))
- val, _ = url.QueryUnescape(c.UserValue("attr_val").(string))
- log = d.log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val))
- )
-
- ctx := utils.GetContextFromRequest(c)
-
- containerID, err := utils.GetContainerID(ctx, scid, d.containerResolver)
- if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
- return
- }
-
- res, err := d.search(ctx, containerID, key, val, object.MatchStringEqual)
- if err != nil {
- log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
- response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
- return
- }
-
- defer res.Close()
-
- buf := make([]oid.ID, 1)
-
- n, err := res.Read(buf)
- if n == 0 {
- if errors.Is(err, io.EOF) {
- log.Error(logs.ObjectNotFound, zap.Error(err))
- response.Error(c, "object not found", fasthttp.StatusNotFound)
- return
- }
-
- log.Error(logs.ReadObjectListFailed, zap.Error(err))
- response.Error(c, "read object list failed: "+err.Error(), fasthttp.StatusBadRequest)
- return
- }
-
- var addrObj oid.Address
- addrObj.SetContainer(*containerID)
- addrObj.SetObject(buf[0])
-
- f(ctx, *d.newRequest(c, log), d.pool, addrObj)
-}
-
-func (d *Downloader) search(ctx context.Context, cid *cid.ID, key, val string, op object.SearchMatchType) (pool.ResObjectSearch, error) {
- filters := object.NewSearchFilters()
- filters.AddRootFilter()
- filters.AddFilter(key, val, op)
-
- var prm pool.PrmObjectSearch
- prm.SetContainerID(*cid)
- prm.SetFilters(filters)
- if btoken := bearerToken(ctx); btoken != nil {
- prm.UseBearer(*btoken)
- }
-
- return d.pool.SearchObjects(ctx, prm)
-}
-
-func (d *Downloader) getContainer(ctx context.Context, cnrID cid.ID) (container.Container, error) {
- var prm pool.PrmContainerGet
- prm.SetContainerID(cnrID)
-
- return d.pool.GetContainer(ctx, prm)
-}
-
-func (d *Downloader) addObjectToZip(zw *zip.Writer, obj *object.Object) (io.Writer, error) {
- method := zip.Store
- if d.settings.ZipCompression() {
- method = zip.Deflate
- }
-
- filePath := getZipFilePath(obj)
- if len(filePath) == 0 || filePath[len(filePath)-1] == '/' {
- return nil, fmt.Errorf("invalid filepath '%s'", filePath)
- }
-
- return zw.CreateHeader(&zip.FileHeader{
- Name: filePath,
- Method: method,
- Modified: time.Now(),
- })
-}
-
-// DownloadZipped handles zip by prefix requests.
-func (d *Downloader) DownloadZipped(c *fasthttp.RequestCtx) {
- scid, _ := c.UserValue("cid").(string)
- prefix, _ := url.QueryUnescape(c.UserValue("prefix").(string))
- log := d.log.With(zap.String("cid", scid), zap.String("prefix", prefix))
-
- ctx := utils.GetContextFromRequest(c)
-
- containerID, err := utils.GetContainerID(ctx, scid, d.containerResolver)
- if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
- return
- }
-
- // check if container exists here to be able to return 404 error,
- // otherwise we get this error only in object iteration step
- // and client get 200 OK.
- if _, err = d.getContainer(ctx, *containerID); err != nil {
- log.Error(logs.CouldNotCheckContainerExistence, zap.Error(err))
- if client.IsErrContainerNotFound(err) {
- response.Error(c, "Not Found", fasthttp.StatusNotFound)
- return
- }
- response.Error(c, "could not check container existence: "+err.Error(), fasthttp.StatusBadRequest)
- return
- }
-
- resSearch, err := d.search(ctx, containerID, object.AttributeFilePath, prefix, object.MatchCommonPrefix)
- if err != nil {
- log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
- response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
- return
- }
-
- c.Response.Header.Set(fasthttp.HeaderContentType, "application/zip")
- c.Response.Header.Set(fasthttp.HeaderContentDisposition, "attachment; filename=\"archive.zip\"")
- c.Response.SetStatusCode(http.StatusOK)
-
- c.SetBodyStreamWriter(func(w *bufio.Writer) {
- defer resSearch.Close()
-
- zipWriter := zip.NewWriter(w)
-
- var bufZip []byte
- var addr oid.Address
-
- empty := true
- called := false
- btoken := bearerToken(ctx)
- addr.SetContainer(*containerID)
-
- errIter := resSearch.Iterate(func(id oid.ID) bool {
- called = true
-
- if empty {
- bufZip = make([]byte, 3<<20) // the same as for upload
- }
- empty = false
-
- addr.SetObject(id)
- if err = d.zipObject(ctx, zipWriter, addr, btoken, bufZip); err != nil {
- log.Error(logs.FailedToAddObjectToArchive, zap.String("oid", id.EncodeToString()), zap.Error(err))
- }
-
- return false
- })
- if errIter != nil {
- log.Error(logs.IteratingOverSelectedObjectsFailed, zap.Error(errIter))
- } else if !called {
- log.Error(logs.ObjectsNotFound)
- }
-
- if err = zipWriter.Close(); err != nil {
- log.Error(logs.CloseZipWriter, zap.Error(err))
- }
- })
-}
-
-func (d *Downloader) zipObject(ctx context.Context, zipWriter *zip.Writer, addr oid.Address, btoken *bearer.Token, bufZip []byte) error {
- var prm pool.PrmObjectGet
- prm.SetAddress(addr)
- if btoken != nil {
- prm.UseBearer(*btoken)
- }
-
- resGet, err := d.pool.GetObject(ctx, prm)
- if err != nil {
- return fmt.Errorf("get FrostFS object: %v", err)
- }
-
- objWriter, err := d.addObjectToZip(zipWriter, &resGet.Header)
- if err != nil {
- return fmt.Errorf("zip create header: %v", err)
- }
-
- if _, err = io.CopyBuffer(objWriter, resGet.Payload, bufZip); err != nil {
- return fmt.Errorf("copy object payload to zip file: %v", err)
- }
-
- if err = resGet.Payload.Close(); err != nil {
- return fmt.Errorf("object body close error: %w", err)
- }
-
- if err = zipWriter.Flush(); err != nil {
- return fmt.Errorf("flush zip writer: %v", err)
- }
-
- return nil
-}
-
-func getZipFilePath(obj *object.Object) string {
- for _, attr := range obj.Attributes() {
- if attr.Key() == object.AttributeFilePath {
- return attr.Value()
- }
- }
-
- return ""
-}
diff --git a/go.mod b/go.mod
index 6e6bc80..80d794c 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,6 @@ require (
github.com/valyala/fasthttp v1.34.0
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
- go.uber.org/atomic v1.10.0
go.uber.org/zap v1.24.0
google.golang.org/grpc v1.55.0
)
@@ -97,6 +96,7 @@ require (
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
+ go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
diff --git a/api/layer/tree_service.go b/internal/api/layer/tree_service.go
similarity index 90%
rename from api/layer/tree_service.go
rename to internal/api/layer/tree_service.go
index 9852257..beb1e7a 100644
--- a/api/layer/tree_service.go
+++ b/internal/api/layer/tree_service.go
@@ -4,7 +4,7 @@ import (
"context"
"errors"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/api"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
)
diff --git a/api/tree.go b/internal/api/tree.go
similarity index 100%
rename from api/tree.go
rename to internal/api/tree.go
diff --git a/internal/handler/download.go b/internal/handler/download.go
new file mode 100644
index 0000000..8ee76bf
--- /dev/null
+++ b/internal/handler/download.go
@@ -0,0 +1,210 @@
+package handler
+
+import (
+ "archive/zip"
+ "bufio"
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
+ "github.com/valyala/fasthttp"
+ "go.uber.org/zap"
+)
+
+// DownloadByAddressOrBucketName handles download requests using simple cid/oid or bucketname/key format.
+func (h *Handler) DownloadByAddressOrBucketName(c *fasthttp.RequestCtx) {
+ test, _ := c.UserValue("oid").(string)
+ var id oid.ID
+ err := id.DecodeString(test)
+ if err != nil {
+ h.byBucketname(c, h.receiveFile)
+ } else {
+ h.byAddress(c, h.receiveFile)
+ }
+}
+
+func (h *Handler) newRequest(ctx *fasthttp.RequestCtx, log *zap.Logger) *request {
+ return &request{
+ RequestCtx: ctx,
+ log: log,
+ }
+}
+
+// DownloadByAttribute handles attribute-based download requests.
+func (h *Handler) DownloadByAttribute(c *fasthttp.RequestCtx) {
+ h.byAttribute(c, h.receiveFile)
+}
+
+func (h *Handler) search(ctx context.Context, cid *cid.ID, key, val string, op object.SearchMatchType) (pool.ResObjectSearch, error) {
+ filters := object.NewSearchFilters()
+ filters.AddRootFilter()
+ filters.AddFilter(key, val, op)
+
+ var prm pool.PrmObjectSearch
+ prm.SetContainerID(*cid)
+ prm.SetFilters(filters)
+ if btoken := bearerToken(ctx); btoken != nil {
+ prm.UseBearer(*btoken)
+ }
+
+ return h.pool.SearchObjects(ctx, prm)
+}
+
+func (h *Handler) getContainer(ctx context.Context, cnrID cid.ID) (container.Container, error) {
+ var prm pool.PrmContainerGet
+ prm.SetContainerID(cnrID)
+
+ return h.pool.GetContainer(ctx, prm)
+}
+
+func (h *Handler) addObjectToZip(zw *zip.Writer, obj *object.Object) (io.Writer, error) {
+ method := zip.Store
+ if h.settings.ZipCompression() {
+ method = zip.Deflate
+ }
+
+ filePath := getZipFilePath(obj)
+ if len(filePath) == 0 || filePath[len(filePath)-1] == '/' {
+ return nil, fmt.Errorf("invalid filepath '%s'", filePath)
+ }
+
+ return zw.CreateHeader(&zip.FileHeader{
+ Name: filePath,
+ Method: method,
+ Modified: time.Now(),
+ })
+}
+
+// DownloadZipped handles zip by prefix requests.
+func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
+ scid, _ := c.UserValue("cid").(string)
+ prefix, _ := url.QueryUnescape(c.UserValue("prefix").(string))
+ log := h.log.With(zap.String("cid", scid), zap.String("prefix", prefix))
+
+ ctx := utils.GetContextFromRequest(c)
+
+ containerID, err := h.getContainerID(ctx, scid)
+ if err != nil {
+ log.Error(logs.WrongContainerID, zap.Error(err))
+ response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
+ return
+ }
+
+ // check if container exists here to be able to return 404 error,
+ // otherwise we get this error only in object iteration step
+ // and client get 200 OK.
+ if _, err = h.getContainer(ctx, *containerID); err != nil {
+ log.Error(logs.CouldNotCheckContainerExistence, zap.Error(err))
+ if client.IsErrContainerNotFound(err) {
+ response.Error(c, "Not Found", fasthttp.StatusNotFound)
+ return
+ }
+ response.Error(c, "could not check container existence: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ resSearch, err := h.search(ctx, containerID, object.AttributeFilePath, prefix, object.MatchCommonPrefix)
+ if err != nil {
+ log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
+ response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ c.Response.Header.Set(fasthttp.HeaderContentType, "application/zip")
+ c.Response.Header.Set(fasthttp.HeaderContentDisposition, "attachment; filename=\"archive.zip\"")
+ c.Response.SetStatusCode(http.StatusOK)
+
+ c.SetBodyStreamWriter(func(w *bufio.Writer) {
+ defer resSearch.Close()
+
+ zipWriter := zip.NewWriter(w)
+
+ var bufZip []byte
+ var addr oid.Address
+
+ empty := true
+ called := false
+ btoken := bearerToken(ctx)
+ addr.SetContainer(*containerID)
+
+ errIter := resSearch.Iterate(func(id oid.ID) bool {
+ called = true
+
+ if empty {
+ bufZip = make([]byte, 3<<20) // the same as for upload
+ }
+ empty = false
+
+ addr.SetObject(id)
+ if err = h.zipObject(ctx, zipWriter, addr, btoken, bufZip); err != nil {
+ log.Error(logs.FailedToAddObjectToArchive, zap.String("oid", id.EncodeToString()), zap.Error(err))
+ }
+
+ return false
+ })
+ if errIter != nil {
+ log.Error(logs.IteratingOverSelectedObjectsFailed, zap.Error(errIter))
+ } else if !called {
+ log.Error(logs.ObjectsNotFound)
+ }
+
+ if err = zipWriter.Close(); err != nil {
+ log.Error(logs.CloseZipWriter, zap.Error(err))
+ }
+ })
+}
+
+func (h *Handler) zipObject(ctx context.Context, zipWriter *zip.Writer, addr oid.Address, btoken *bearer.Token, bufZip []byte) error {
+ var prm pool.PrmObjectGet
+ prm.SetAddress(addr)
+ if btoken != nil {
+ prm.UseBearer(*btoken)
+ }
+
+ resGet, err := h.pool.GetObject(ctx, prm)
+ if err != nil {
+ return fmt.Errorf("get FrostFS object: %v", err)
+ }
+
+ objWriter, err := h.addObjectToZip(zipWriter, &resGet.Header)
+ if err != nil {
+ return fmt.Errorf("zip create header: %v", err)
+ }
+
+ if _, err = io.CopyBuffer(objWriter, resGet.Payload, bufZip); err != nil {
+ return fmt.Errorf("copy object payload to zip file: %v", err)
+ }
+
+ if err = resGet.Payload.Close(); err != nil {
+ return fmt.Errorf("object body close error: %w", err)
+ }
+
+ if err = zipWriter.Flush(); err != nil {
+ return fmt.Errorf("flush zip writer: %v", err)
+ }
+
+ return nil
+}
+
+func getZipFilePath(obj *object.Object) string {
+ for _, attr := range obj.Attributes() {
+ if attr.Key() == object.AttributeFilePath {
+ return attr.Value()
+ }
+ }
+
+ return ""
+}
diff --git a/uploader/filter.go b/internal/handler/filter.go
similarity index 98%
rename from uploader/filter.go
rename to internal/handler/filter.go
index 70d6eef..745718a 100644
--- a/uploader/filter.go
+++ b/internal/handler/filter.go
@@ -1,4 +1,4 @@
-package uploader
+package handler
import (
"bytes"
diff --git a/uploader/filter_test.go b/internal/handler/filter_test.go
similarity index 98%
rename from uploader/filter_test.go
rename to internal/handler/filter_test.go
index 9d32b84..0322952 100644
--- a/uploader/filter_test.go
+++ b/internal/handler/filter_test.go
@@ -1,6 +1,6 @@
//go:build !integration
-package uploader
+package handler
import (
"testing"
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
new file mode 100644
index 0000000..d462280
--- /dev/null
+++ b/internal/handler/handler.go
@@ -0,0 +1,193 @@
+package handler
+
+import (
+ "context"
+ "errors"
+ "io"
+ "net/url"
+ "sync/atomic"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
+ "github.com/valyala/fasthttp"
+ "go.uber.org/zap"
+)
+
+type Handler struct {
+ log *zap.Logger
+ pool *pool.Pool
+ ownerID *user.ID
+ settings *Settings
+ containerResolver *resolver.ContainerResolver
+ tree *tree.Tree
+}
+
+// Settings stores reloading parameters, so it has to provide atomic getters and setters.
+type Settings struct {
+ defaultTimestamp atomic.Bool
+ zipCompression atomic.Bool
+}
+
+func (s *Settings) DefaultTimestamp() bool {
+ return s.defaultTimestamp.Load()
+}
+
+func (s *Settings) SetDefaultTimestamp(val bool) {
+ s.defaultTimestamp.Store(val)
+}
+
+func (s *Settings) ZipCompression() bool {
+ return s.zipCompression.Load()
+}
+
+func (s *Settings) SetZipCompression(val bool) {
+ s.zipCompression.Store(val)
+}
+
+func New(params *utils.AppParams, settings *Settings, tree *tree.Tree) *Handler {
+ return &Handler{
+ log: params.Logger,
+ pool: params.Pool,
+ ownerID: params.Owner,
+ settings: settings,
+ containerResolver: params.Resolver,
+ tree: tree,
+ }
+}
+
+// getContainerID decode container id, if it's not a valid container id
+// then trey to resolve name using provided resolver.
+func (h *Handler) getContainerID(ctx context.Context, containerID string) (*cid.ID, error) {
+ cnrID := new(cid.ID)
+ err := cnrID.DecodeString(containerID)
+ if err != nil {
+ cnrID, err = h.containerResolver.Resolve(ctx, containerID)
+ }
+ return cnrID, err
+}
+
+// byAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that
+// prepares request and object address to it.
+func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
+ var (
+ idCnr, _ = c.UserValue("cid").(string)
+ idObj, _ = c.UserValue("oid").(string)
+ log = h.log.With(zap.String("cid", idCnr), zap.String("oid", idObj))
+ )
+
+ ctx := utils.GetContextFromRequest(c)
+
+ cnrID, err := h.getContainerID(ctx, idCnr)
+ if err != nil {
+ log.Error(logs.WrongContainerID, zap.Error(err))
+ response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
+ return
+ }
+
+ objID := new(oid.ID)
+ if err = objID.DecodeString(idObj); err != nil {
+ log.Error(logs.WrongObjectID, zap.Error(err))
+ response.Error(c, "wrong object id", fasthttp.StatusBadRequest)
+ return
+ }
+
+ var addr oid.Address
+ addr.SetContainer(*cnrID)
+ addr.SetObject(*objID)
+
+ f(ctx, *h.newRequest(c, log), addr)
+}
+
+// byBucketname is a wrapper for function (e.g. request.headObject, request.receiveFile) that
+// prepares request and object address to it.
+func (h *Handler) byBucketname(req *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
+ var (
+ bucketname = req.UserValue("cid").(string)
+ key = req.UserValue("oid").(string)
+ log = h.log.With(zap.String("bucketname", bucketname), zap.String("key", key))
+ )
+
+ ctx := utils.GetContextFromRequest(req)
+
+ cnrID, err := h.getContainerID(ctx, bucketname)
+ if err != nil {
+ log.Error(logs.WrongContainerID, zap.Error(err))
+ response.Error(req, "wrong container id", fasthttp.StatusBadRequest)
+ return
+ }
+
+ foundOid, err := h.tree.GetLatestVersion(ctx, cnrID, key)
+ if err != nil {
+ log.Error(logs.ObjectWasntFound, zap.Error(err))
+ response.Error(req, "object wasn't found", fasthttp.StatusNotFound)
+ return
+ }
+ if foundOid.DeleteMarker {
+ log.Error(logs.ObjectWasDeleted)
+ response.Error(req, "object deleted", fasthttp.StatusNotFound)
+ return
+ }
+
+ var addr oid.Address
+ addr.SetContainer(*cnrID)
+ addr.SetObject(foundOid.OID)
+
+ f(ctx, *h.newRequest(req, log), addr)
+}
+
+// byAttribute is a wrapper similar to byAddress.
+func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
+ var (
+ scid, _ = c.UserValue("cid").(string)
+ key, _ = url.QueryUnescape(c.UserValue("attr_key").(string))
+ val, _ = url.QueryUnescape(c.UserValue("attr_val").(string))
+ log = h.log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val))
+ )
+
+ ctx := utils.GetContextFromRequest(c)
+
+ containerID, err := h.getContainerID(ctx, scid)
+ if err != nil {
+ log.Error(logs.WrongContainerID, zap.Error(err))
+ response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
+ return
+ }
+
+ res, err := h.search(ctx, containerID, key, val, object.MatchStringEqual)
+ if err != nil {
+ log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
+ response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ defer res.Close()
+
+ buf := make([]oid.ID, 1)
+
+ n, err := res.Read(buf)
+ if n == 0 {
+ if errors.Is(err, io.EOF) {
+ log.Error(logs.ObjectNotFound, zap.Error(err))
+ response.Error(c, "object not found", fasthttp.StatusNotFound)
+ return
+ }
+
+ log.Error(logs.ReadObjectListFailed, zap.Error(err))
+ response.Error(c, "read object list failed: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ var addrObj oid.Address
+ addrObj.SetContainer(*containerID)
+ addrObj.SetObject(buf[0])
+
+ f(ctx, *h.newRequest(c, log), addrObj)
+}
diff --git a/downloader/head.go b/internal/handler/head.go
similarity index 86%
rename from downloader/head.go
rename to internal/handler/head.go
index 76dfd93..f7478f1 100644
--- a/downloader/head.go
+++ b/internal/handler/head.go
@@ -1,4 +1,4 @@
-package downloader
+package handler
import (
"context"
@@ -25,7 +25,7 @@ const (
hdrContainerID = "X-Container-Id"
)
-func headObject(ctx context.Context, req request, clnt *pool.Pool, objectAddress oid.Address) {
+func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid.Address) {
var start = time.Now()
btoken := bearerToken(ctx)
@@ -36,7 +36,7 @@ func headObject(ctx context.Context, req request, clnt *pool.Pool, objectAddress
prm.UseBearer(*btoken)
}
- obj, err := clnt.HeadObject(ctx, prm)
+ obj, err := h.pool.HeadObject(ctx, prm)
if err != nil {
req.handleFrostFSErr(err, start)
return
@@ -81,7 +81,7 @@ func headObject(ctx context.Context, req request, clnt *pool.Pool, objectAddress
prmRange.UseBearer(*btoken)
}
- resObj, err := clnt.ObjectRange(ctx, prmRange)
+ resObj, err := h.pool.ObjectRange(ctx, prmRange)
if err != nil {
return nil, err
}
@@ -104,19 +104,19 @@ func idsToResponse(resp *fasthttp.Response, obj *object.Object) {
}
// HeadByAddressOrBucketName handles head requests using simple cid/oid or bucketname/key format.
-func (d *Downloader) HeadByAddressOrBucketName(c *fasthttp.RequestCtx) {
+func (h *Handler) HeadByAddressOrBucketName(c *fasthttp.RequestCtx) {
test, _ := c.UserValue("oid").(string)
var id oid.ID
err := id.DecodeString(test)
if err != nil {
- d.byBucketname(c, headObject)
+ h.byBucketname(c, h.headObject)
} else {
- d.byAddress(c, headObject)
+ h.byAddress(c, h.headObject)
}
}
// HeadByAttribute handles attribute-based head requests.
-func (d *Downloader) HeadByAttribute(c *fasthttp.RequestCtx) {
- d.byAttribute(c, headObject)
+func (h *Handler) HeadByAttribute(c *fasthttp.RequestCtx) {
+ h.byAttribute(c, h.headObject)
}
diff --git a/uploader/multipart.go b/internal/handler/multipart.go
similarity index 92%
rename from uploader/multipart.go
rename to internal/handler/multipart.go
index 135ee88..de9242f 100644
--- a/uploader/multipart.go
+++ b/internal/handler/multipart.go
@@ -1,10 +1,10 @@
-package uploader
+package handler
import (
"io"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/multipart"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/uploader/multipart"
"go.uber.org/zap"
)
diff --git a/uploader/multipart/multipart.go b/internal/handler/multipart/multipart.go
similarity index 100%
rename from uploader/multipart/multipart.go
rename to internal/handler/multipart/multipart.go
diff --git a/uploader/multipart_test.go b/internal/handler/multipart_test.go
similarity index 99%
rename from uploader/multipart_test.go
rename to internal/handler/multipart_test.go
index d19cd5e..2c50a87 100644
--- a/uploader/multipart_test.go
+++ b/internal/handler/multipart_test.go
@@ -1,6 +1,6 @@
//go:build !integration
-package uploader
+package handler
import (
"crypto/rand"
diff --git a/internal/handler/reader.go b/internal/handler/reader.go
new file mode 100644
index 0000000..76801f7
--- /dev/null
+++ b/internal/handler/reader.go
@@ -0,0 +1,141 @@
+package handler
+
+import (
+ "bytes"
+ "context"
+ "io"
+ "net/http"
+ "path"
+ "strconv"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
+ "github.com/valyala/fasthttp"
+ "go.uber.org/zap"
+)
+
+type readCloser struct {
+ io.Reader
+ io.Closer
+}
+
+// initializes io.Reader with the limited size and detects Content-Type from it.
+// Returns r's error directly. Also returns the processed data.
+func readContentType(maxSize uint64, rInit func(uint64) (io.Reader, error)) (string, []byte, error) {
+ if maxSize > sizeToDetectType {
+ maxSize = sizeToDetectType
+ }
+
+ buf := make([]byte, maxSize) // maybe sync-pool the slice?
+
+ r, err := rInit(maxSize)
+ if err != nil {
+ return "", nil, err
+ }
+
+ n, err := r.Read(buf)
+ if err != nil && err != io.EOF {
+ return "", nil, err
+ }
+
+ buf = buf[:n]
+
+ return http.DetectContentType(buf), buf, err // to not lose io.EOF
+}
+
+func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oid.Address) {
+ var (
+ err error
+ dis = "inline"
+ start = time.Now()
+ filename string
+ )
+
+ var prm pool.PrmObjectGet
+ prm.SetAddress(objectAddress)
+ if btoken := bearerToken(ctx); btoken != nil {
+ prm.UseBearer(*btoken)
+ }
+
+ rObj, err := h.pool.GetObject(ctx, prm)
+ if err != nil {
+ req.handleFrostFSErr(err, start)
+ return
+ }
+
+ // we can't close reader in this function, so how to do it?
+
+ if req.Request.URI().QueryArgs().GetBool("download") {
+ dis = "attachment"
+ }
+
+ payloadSize := rObj.Header.PayloadSize()
+
+ req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
+ var contentType string
+ for _, attr := range rObj.Header.Attributes() {
+ key := attr.Key()
+ val := attr.Value()
+ if !isValidToken(key) || !isValidValue(val) {
+ continue
+ }
+
+ key = utils.BackwardTransformIfSystem(key)
+
+ req.Response.Header.Set(utils.UserAttributeHeaderPrefix+key, val)
+ switch key {
+ case object.AttributeFileName:
+ filename = val
+ case object.AttributeTimestamp:
+ value, err := strconv.ParseInt(val, 10, 64)
+ if err != nil {
+ req.log.Info(logs.CouldntParseCreationDate,
+ zap.String("key", key),
+ zap.String("val", val),
+ zap.Error(err))
+ continue
+ }
+ req.Response.Header.Set(fasthttp.HeaderLastModified,
+ time.Unix(value, 0).UTC().Format(http.TimeFormat))
+ case object.AttributeContentType:
+ contentType = val
+ }
+ }
+
+ idsToResponse(&req.Response, &rObj.Header)
+
+ if len(contentType) == 0 {
+ // determine the Content-Type from the payload head
+ var payloadHead []byte
+
+ contentType, payloadHead, err = readContentType(payloadSize, func(uint64) (io.Reader, error) {
+ return rObj.Payload, nil
+ })
+ if err != nil && err != io.EOF {
+ req.log.Error(logs.CouldNotDetectContentTypeFromPayload, zap.Error(err))
+ response.Error(req.RequestCtx, "could not detect Content-Type from payload: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ // reset payload reader since a part of the data has been read
+ var headReader io.Reader = bytes.NewReader(payloadHead)
+
+ if err != io.EOF { // otherwise, we've already read full payload
+ headReader = io.MultiReader(headReader, rObj.Payload)
+ }
+
+ // note: we could do with io.Reader, but SetBodyStream below closes body stream
+ // if it implements io.Closer and that's useful for us.
+ rObj.Payload = readCloser{headReader, rObj.Payload}
+ }
+ req.SetContentType(contentType)
+
+ req.Response.Header.Set(fasthttp.HeaderContentDisposition, dis+"; filename="+path.Base(filename))
+
+ req.Response.SetBodyStream(rObj.Payload, int(payloadSize))
+}
diff --git a/downloader/reader_test.go b/internal/handler/reader_test.go
similarity index 98%
rename from downloader/reader_test.go
rename to internal/handler/reader_test.go
index 09c990a..73899ca 100644
--- a/downloader/reader_test.go
+++ b/internal/handler/reader_test.go
@@ -1,6 +1,6 @@
//go:build !integration
-package downloader
+package handler
import (
"io"
diff --git a/uploader/upload.go b/internal/handler/upload.go
similarity index 73%
rename from uploader/upload.go
rename to internal/handler/upload.go
index 2832043..8d4e681 100644
--- a/uploader/upload.go
+++ b/internal/handler/upload.go
@@ -1,4 +1,4 @@
-package uploader
+package handler
import (
"context"
@@ -9,7 +9,6 @@ import (
"time"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
@@ -17,9 +16,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/valyala/fasthttp"
- "go.uber.org/atomic"
"go.uber.org/zap"
)
@@ -28,55 +25,39 @@ const (
drainBufSize = 4096
)
-// Uploader is an upload request handler.
-type Uploader struct {
- log *zap.Logger
- pool *pool.Pool
- ownerID *user.ID
- settings *Settings
- containerResolver *resolver.ContainerResolver
+type putResponse struct {
+ ObjectID string `json:"object_id"`
+ ContainerID string `json:"container_id"`
}
-// Settings stores reloading parameters, so it has to provide atomic getters and setters.
-type Settings struct {
- defaultTimestamp atomic.Bool
-}
-
-func (s *Settings) DefaultTimestamp() bool {
- return s.defaultTimestamp.Load()
-}
-
-func (s *Settings) SetDefaultTimestamp(val bool) {
- s.defaultTimestamp.Store(val)
-}
-
-// New creates a new Uploader using specified logger, connection pool and
-// other options.
-func New(params *utils.AppParams, settings *Settings) *Uploader {
- return &Uploader{
- log: params.Logger,
- pool: params.Pool,
- ownerID: params.Owner,
- settings: settings,
- containerResolver: params.Resolver,
+func newPutResponse(addr oid.Address) *putResponse {
+ return &putResponse{
+ ObjectID: addr.Object().EncodeToString(),
+ ContainerID: addr.Container().EncodeToString(),
}
}
+func (pr *putResponse) encode(w io.Writer) error {
+ enc := json.NewEncoder(w)
+ enc.SetIndent("", "\t")
+ return enc.Encode(pr)
+}
+
// Upload handles multipart upload request.
-func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
+func (h *Handler) Upload(req *fasthttp.RequestCtx) {
var (
file MultipartFile
idObj oid.ID
addr oid.Address
scid, _ = req.UserValue("cid").(string)
- log = u.log.With(zap.String("cid", scid))
+ log = h.log.With(zap.String("cid", scid))
bodyStream = req.RequestBodyStream()
drainBuf = make([]byte, drainBufSize)
)
ctx := utils.GetContextFromRequest(req)
- idCnr, err := utils.GetContainerID(ctx, scid, u.containerResolver)
+ idCnr, err := h.getContainerID(ctx, scid)
if err != nil {
log.Error(logs.WrongContainerID, zap.Error(err))
response.Error(req, "wrong container id", fasthttp.StatusBadRequest)
@@ -97,12 +78,12 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
)
}()
boundary := string(req.Request.Header.MultipartFormBoundary())
- if file, err = fetchMultipartFile(u.log, bodyStream, boundary); err != nil {
+ if file, err = fetchMultipartFile(h.log, bodyStream, boundary); err != nil {
log.Error(logs.CouldNotReceiveMultipartForm, zap.Error(err))
response.Error(req, "could not receive multipart/form: "+err.Error(), fasthttp.StatusBadRequest)
return
}
- filtered, err := filterHeaders(u.log, &req.Request.Header)
+ filtered, err := filterHeaders(h.log, &req.Request.Header)
if err != nil {
log.Error(logs.CouldNotProcessHeaders, zap.Error(err))
response.Error(req, err.Error(), fasthttp.StatusBadRequest)
@@ -118,7 +99,7 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
}
}
- if err = utils.PrepareExpirationHeader(req, u.pool, filtered, now); err != nil {
+ if err = utils.PrepareExpirationHeader(req, h.pool, filtered, now); err != nil {
log.Error(logs.CouldNotPrepareExpirationHeader, zap.Error(err))
response.Error(req, "could not prepare expiration header: "+err.Error(), fasthttp.StatusBadRequest)
return
@@ -140,7 +121,7 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
attributes = append(attributes, *filename)
}
// sets Timestamp attribute if it wasn't set from header and enabled by settings
- if _, ok := filtered[object.AttributeTimestamp]; !ok && u.settings.DefaultTimestamp() {
+ if _, ok := filtered[object.AttributeTimestamp]; !ok && h.settings.DefaultTimestamp() {
timestamp := object.NewAttribute()
timestamp.SetKey(object.AttributeTimestamp)
timestamp.SetValue(strconv.FormatInt(time.Now().Unix(), 10))
@@ -149,20 +130,20 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
obj := object.New()
obj.SetContainerID(*idCnr)
- obj.SetOwnerID(u.ownerID)
+ obj.SetOwnerID(h.ownerID)
obj.SetAttributes(attributes...)
var prm pool.PrmObjectPut
prm.SetHeader(*obj)
prm.SetPayload(file)
- bt := u.fetchBearerToken(ctx)
+ bt := h.fetchBearerToken(ctx)
if bt != nil {
prm.UseBearer(*bt)
}
- if idObj, err = u.pool.PutObject(ctx, prm); err != nil {
- u.handlePutFrostFSErr(req, err)
+ if idObj, err = h.pool.PutObject(ctx, prm); err != nil {
+ h.handlePutFrostFSErr(req, err)
return
}
@@ -193,35 +174,17 @@ func (u *Uploader) Upload(req *fasthttp.RequestCtx) {
req.Response.Header.SetContentType(jsonHeader)
}
-func (u *Uploader) handlePutFrostFSErr(r *fasthttp.RequestCtx, err error) {
+func (h *Handler) handlePutFrostFSErr(r *fasthttp.RequestCtx, err error) {
statusCode, msg, additionalFields := response.FormErrorResponse("could not store file in frostfs", err)
logFields := append([]zap.Field{zap.Error(err)}, additionalFields...)
- u.log.Error(logs.CouldNotStoreFileInFrostfs, logFields...)
+ h.log.Error(logs.CouldNotStoreFileInFrostfs, logFields...)
response.Error(r, msg, statusCode)
}
-func (u *Uploader) fetchBearerToken(ctx context.Context) *bearer.Token {
+func (h *Handler) fetchBearerToken(ctx context.Context) *bearer.Token {
if tkn, err := tokens.LoadBearerToken(ctx); err == nil && tkn != nil {
return tkn
}
return nil
}
-
-type putResponse struct {
- ObjectID string `json:"object_id"`
- ContainerID string `json:"container_id"`
-}
-
-func newPutResponse(addr oid.Address) *putResponse {
- return &putResponse{
- ObjectID: addr.Object().EncodeToString(),
- ContainerID: addr.Container().EncodeToString(),
- }
-}
-
-func (pr *putResponse) encode(w io.Writer) error {
- enc := json.NewEncoder(w)
- enc.SetIndent("", "\t")
- return enc.Encode(pr)
-}
diff --git a/internal/handler/utils.go b/internal/handler/utils.go
new file mode 100644
index 0000000..b51400c
--- /dev/null
+++ b/internal/handler/utils.go
@@ -0,0 +1,60 @@
+package handler
+
+import (
+ "context"
+ "strings"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
+ "github.com/valyala/fasthttp"
+ "go.uber.org/zap"
+)
+
+type request struct {
+ *fasthttp.RequestCtx
+ log *zap.Logger
+}
+
+func (r *request) handleFrostFSErr(err error, start time.Time) {
+ logFields := []zap.Field{
+ zap.Stringer("elapsed", time.Since(start)),
+ zap.Error(err),
+ }
+ statusCode, msg, additionalFields := response.FormErrorResponse("could not receive object", err)
+ logFields = append(logFields, additionalFields...)
+
+ r.log.Error(logs.CouldNotReceiveObject, logFields...)
+ response.Error(r.RequestCtx, msg, statusCode)
+}
+
+func bearerToken(ctx context.Context) *bearer.Token {
+ if tkn, err := tokens.LoadBearerToken(ctx); err == nil {
+ return tkn
+ }
+ return nil
+}
+
+func isValidToken(s string) bool {
+ for _, c := range s {
+ if c <= ' ' || c > 127 {
+ return false
+ }
+ if strings.ContainsRune("()<>@,;:\\\"/[]?={}", c) {
+ return false
+ }
+ }
+ return true
+}
+
+func isValidValue(s string) bool {
+ for _, c := range s {
+ // HTTP specification allows for more technically, but we don't want to escape things.
+ if c < ' ' || c > 127 || c == '"' {
+ return false
+ }
+ }
+ return true
+}
diff --git a/tree/tree.go b/tree/tree.go
index 84b6707..3a673b3 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -5,8 +5,8 @@ import (
"fmt"
"strings"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/api"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/api/layer"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api/layer"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
diff --git a/utils/util.go b/utils/util.go
index e54c98d..a328769 100644
--- a/utils/util.go
+++ b/utils/util.go
@@ -4,23 +4,10 @@ import (
"context"
"fmt"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
- cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/valyala/fasthttp"
)
-// GetContainerID decode container id, if it's not a valid container id
-// then trey to resolve name using provided resolver.
-func GetContainerID(ctx context.Context, containerID string, resolver *resolver.ContainerResolver) (*cid.ID, error) {
- cnrID := new(cid.ID)
- err := cnrID.DecodeString(containerID)
- if err != nil {
- cnrID, err = resolver.Resolve(ctx, containerID)
- }
- return cnrID, err
-}
-
type EpochDurations struct {
CurrentEpoch uint64
MsPerBlock int64
From e26577e753c963258af7f5aa30241f404bbbc3b9 Mon Sep 17 00:00:00 2001
From: Marina Biryukova
Date: Tue, 5 Sep 2023 18:17:22 +0300
Subject: [PATCH 030/161] [#74] Replace atomics with mutex for reloadable
params
Signed-off-by: Marina Biryukova
---
cmd/http-gw/app.go | 39 ++++++++++++++++++++++++++++++++----
internal/handler/download.go | 2 +-
internal/handler/handler.go | 34 ++++++++-----------------------
internal/handler/upload.go | 2 +-
4 files changed, 45 insertions(+), 32 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 40db433..e8ac917 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -50,7 +50,7 @@ type (
resolver *resolver.ContainerResolver
metrics *gateMetrics
services []*metrics.Service
- settings *handler.Settings
+ settings *appSettings
servers []Server
}
@@ -69,6 +69,13 @@ type (
mu sync.RWMutex
enabled bool
}
+
+ // appSettings stores reloading parameters, so it has to provide getters and setters which use RWMutex.
+ appSettings struct {
+ mu sync.RWMutex
+ defaultTimestamp bool
+ zipCompression bool
+ }
)
// WithLogger returns Option to set a specific logger.
@@ -133,8 +140,32 @@ func newApp(ctx context.Context, opt ...Option) App {
return a
}
+func (s *appSettings) DefaultTimestamp() bool {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.defaultTimestamp
+}
+
+func (s *appSettings) setDefaultTimestamp(val bool) {
+ s.mu.Lock()
+ s.defaultTimestamp = val
+ s.mu.Unlock()
+}
+
+func (s *appSettings) ZipCompression() bool {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.zipCompression
+}
+
+func (s *appSettings) setZipCompression(val bool) {
+ s.mu.Lock()
+ s.zipCompression = val
+ s.mu.Unlock()
+}
+
func (a *app) initAppSettings() {
- a.settings = &handler.Settings{}
+ a.settings = &appSettings{}
a.updateSettings()
}
@@ -415,8 +446,8 @@ func (a *app) configReload(ctx context.Context) {
}
func (a *app) updateSettings() {
- a.settings.SetDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
- a.settings.SetZipCompression(a.cfg.GetBool(cfgZipCompression))
+ a.settings.setDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
+ a.settings.setZipCompression(a.cfg.GetBool(cfgZipCompression))
}
func (a *app) startServices() {
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 8ee76bf..696c57e 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -72,7 +72,7 @@ func (h *Handler) getContainer(ctx context.Context, cnrID cid.ID) (container.Con
func (h *Handler) addObjectToZip(zw *zip.Writer, obj *object.Object) (io.Writer, error) {
method := zip.Store
- if h.settings.ZipCompression() {
+ if h.config.ZipCompression() {
method = zip.Deflate
}
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index d462280..579a55f 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -5,7 +5,6 @@ import (
"errors"
"io"
"net/url"
- "sync/atomic"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
@@ -21,43 +20,26 @@ import (
"go.uber.org/zap"
)
+type Config interface {
+ DefaultTimestamp() bool
+ ZipCompression() bool
+}
+
type Handler struct {
log *zap.Logger
pool *pool.Pool
ownerID *user.ID
- settings *Settings
+ config Config
containerResolver *resolver.ContainerResolver
tree *tree.Tree
}
-// Settings stores reloading parameters, so it has to provide atomic getters and setters.
-type Settings struct {
- defaultTimestamp atomic.Bool
- zipCompression atomic.Bool
-}
-
-func (s *Settings) DefaultTimestamp() bool {
- return s.defaultTimestamp.Load()
-}
-
-func (s *Settings) SetDefaultTimestamp(val bool) {
- s.defaultTimestamp.Store(val)
-}
-
-func (s *Settings) ZipCompression() bool {
- return s.zipCompression.Load()
-}
-
-func (s *Settings) SetZipCompression(val bool) {
- s.zipCompression.Store(val)
-}
-
-func New(params *utils.AppParams, settings *Settings, tree *tree.Tree) *Handler {
+func New(params *utils.AppParams, config Config, tree *tree.Tree) *Handler {
return &Handler{
log: params.Logger,
pool: params.Pool,
ownerID: params.Owner,
- settings: settings,
+ config: config,
containerResolver: params.Resolver,
tree: tree,
}
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index 8d4e681..f5e0459 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -121,7 +121,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
attributes = append(attributes, *filename)
}
// sets Timestamp attribute if it wasn't set from header and enabled by settings
- if _, ok := filtered[object.AttributeTimestamp]; !ok && h.settings.DefaultTimestamp() {
+ if _, ok := filtered[object.AttributeTimestamp]; !ok && h.config.DefaultTimestamp() {
timestamp := object.NewAttribute()
timestamp.SetKey(object.AttributeTimestamp)
timestamp.SetValue(strconv.FormatInt(time.Now().Unix(), 10))
From 84eb57475bfbccd303d68eddb1b0ecfc91771ca3 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Mon, 9 Oct 2023 09:41:17 +0300
Subject: [PATCH 031/161] [#85] Fix get latest version node
Signed-off-by: Roman Loginov
---
tree/tree.go | 39 ++++++++++++-
tree/tree_test.go | 143 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 179 insertions(+), 3 deletions(-)
create mode 100644 tree/tree_test.go
diff --git a/tree/tree.go b/tree/tree.go
index 3a673b3..a9135eb 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -73,6 +73,7 @@ type Meta interface {
type NodeResponse interface {
GetMeta() []Meta
+ GetTimestamp() uint64
}
func newTreeNode(nodeInfo NodeResponse) (*treeNode, error) {
@@ -135,7 +136,7 @@ func (c *Tree) GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName s
TreeID: versionTree,
Path: path,
Meta: meta,
- LatestOnly: true,
+ LatestOnly: false,
AllAttrs: false,
}
nodes, err := c.service.GetNodes(ctx, p)
@@ -143,11 +144,43 @@ func (c *Tree) GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName s
return nil, err
}
- if len(nodes) == 0 {
+ latestNode, err := getLatestNode(nodes)
+ if err != nil {
+ return nil, err
+ }
+
+ return newNodeVersion(latestNode)
+}
+
+func getLatestNode(nodes []NodeResponse) (NodeResponse, error) {
+ var (
+ maxCreationTime uint64
+ targetIndexNode = -1
+ )
+
+ for i, node := range nodes {
+ currentCreationTime := node.GetTimestamp()
+ if checkExistOID(node.GetMeta()) && currentCreationTime > maxCreationTime {
+ maxCreationTime = currentCreationTime
+ targetIndexNode = i
+ }
+ }
+
+ if targetIndexNode == -1 {
return nil, layer.ErrNodeNotFound
}
- return newNodeVersion(nodes[0])
+ return nodes[targetIndexNode], nil
+}
+
+func checkExistOID(meta []Meta) bool {
+ for _, kv := range meta {
+ if kv.GetKey() == "OID" {
+ return true
+ }
+ }
+
+ return false
}
// pathFromName splits name by '/'.
diff --git a/tree/tree_test.go b/tree/tree_test.go
new file mode 100644
index 0000000..7cd2314
--- /dev/null
+++ b/tree/tree_test.go
@@ -0,0 +1,143 @@
+package tree
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+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
+ timestamp uint64
+}
+
+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 TestGetLatestNode(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ nodes []NodeResponse
+ exceptedOID string
+ error bool
+ }{
+ {
+ name: "empty",
+ nodes: []NodeResponse{},
+ error: true,
+ },
+ {
+ name: "one node of the object version",
+ nodes: []NodeResponse{
+ nodeResponse{
+ timestamp: 1,
+ meta: []nodeMeta{
+ {
+ key: oidKV,
+ value: []byte("oid1"),
+ },
+ },
+ },
+ },
+ exceptedOID: "oid1",
+ },
+ {
+ name: "one node of the object version and one node of the secondary object",
+ nodes: []NodeResponse{
+ nodeResponse{
+ timestamp: 3,
+ meta: []nodeMeta{},
+ },
+ nodeResponse{
+ timestamp: 1,
+ meta: []nodeMeta{
+ {
+ key: oidKV,
+ value: []byte("oid1"),
+ },
+ },
+ },
+ },
+ exceptedOID: "oid1",
+ },
+ {
+ name: "all nodes represent a secondary object",
+ nodes: []NodeResponse{
+ nodeResponse{
+ timestamp: 3,
+ meta: []nodeMeta{},
+ },
+ nodeResponse{
+ timestamp: 5,
+ meta: []nodeMeta{},
+ },
+ },
+ error: true,
+ },
+ {
+ name: "several nodes of different types and with different timestamp",
+ nodes: []NodeResponse{
+ nodeResponse{
+ timestamp: 1,
+ meta: []nodeMeta{
+ {
+ key: oidKV,
+ value: []byte("oid1"),
+ },
+ },
+ },
+ nodeResponse{
+ timestamp: 3,
+ meta: []nodeMeta{},
+ },
+ nodeResponse{
+ timestamp: 4,
+ meta: []nodeMeta{
+ {
+ key: oidKV,
+ value: []byte("oid2"),
+ },
+ },
+ },
+ nodeResponse{
+ timestamp: 6,
+ meta: []nodeMeta{},
+ },
+ },
+ exceptedOID: "oid2",
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ actualNode, err := getLatestNode(tc.nodes)
+ if tc.error {
+ require.Error(t, err)
+ return
+ }
+
+ require.NoError(t, err)
+ require.Equal(t, tc.exceptedOID, string(actualNode.GetMeta()[0].GetValue()))
+ })
+ }
+}
From e61b4867c9ff85c2c93d96475516eda95a63e99a Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Mon, 21 Aug 2023 16:30:19 +0300
Subject: [PATCH 032/161] [#70] Update SDK to support client cut
Signed-off-by: Denis Kirillov
---
go.mod | 2 +-
go.sum | 4 ++--
internal/handler/download.go | 5 +++--
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/go.mod b/go.mod
index 80d794c..a58272e 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.20
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230802103237-363f153eafa6
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8
github.com/fasthttp/router v1.4.1
github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc
github.com/prometheus/client_golang v1.15.1
diff --git a/go.sum b/go.sum
index f4c8c56..0b9b457 100644
--- a/go.sum
+++ b/go.sum
@@ -45,8 +45,8 @@ git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSV
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230802103237-363f153eafa6 h1:u6lzNotV6MEMNEG/XeS7g+FjPrrf+j4gnOHtvun2KJc=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230802103237-363f153eafa6/go.mod h1:LI2GOj0pEx0jYTjB3QHja2PNhQFYL2pCm71RAFwDv0M=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8 h1:0s2RkATjdtK/5fHjRGsIi8qMvc9IoeMZgMX5ddMwI+I=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8/go.mod h1:t1akKcUH7iBrFHX8rSXScYMP17k2kYQXMbZooiL5Juw=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 696c57e..5021b4a 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -64,8 +64,9 @@ func (h *Handler) search(ctx context.Context, cid *cid.ID, key, val string, op o
}
func (h *Handler) getContainer(ctx context.Context, cnrID cid.ID) (container.Container, error) {
- var prm pool.PrmContainerGet
- prm.SetContainerID(cnrID)
+ prm := pool.PrmContainerGet{
+ ContainerID: cnrID,
+ }
return h.pool.GetContainer(ctx, prm)
}
From 9b34413e17757af891cd56bd5ff74b99416ce3f2 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Mon, 21 Aug 2023 16:50:23 +0300
Subject: [PATCH 033/161] [#70] Support client cut
Signed-off-by: Denis Kirillov
---
CHANGELOG.md | 1 +
cmd/http-gw/app.go | 14 ++++++++++++++
cmd/http-gw/settings.go | 3 +++
config/config.env | 4 ++++
config/config.yaml | 5 +++++
docs/gate-configuration.md | 16 +++++++++++++++-
internal/handler/handler.go | 1 +
internal/handler/upload.go | 1 +
8 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d323c89..d660f10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ This document outlines major changes between releases.
- Support impersonate bearer token (#40, #45)
- Tracing support (#20, #44, #60)
- Object name resolving with tree service (#30)
+- Add new `frostfs.client_cut` config param (#70)
### Changed
- Update prometheus to v1.15.0 (#35)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index e8ac917..93a6b6c 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -75,6 +75,7 @@ type (
mu sync.RWMutex
defaultTimestamp bool
zipCompression bool
+ clientCut bool
}
)
@@ -164,6 +165,18 @@ func (s *appSettings) setZipCompression(val bool) {
s.mu.Unlock()
}
+func (s *appSettings) ClientCut() bool {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.clientCut
+}
+
+func (s *appSettings) setClientCut(val bool) {
+ s.mu.Lock()
+ s.clientCut = val
+ s.mu.Unlock()
+}
+
func (a *app) initAppSettings() {
a.settings = &appSettings{}
@@ -448,6 +461,7 @@ func (a *app) configReload(ctx context.Context) {
func (a *app) updateSettings() {
a.settings.setDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
a.settings.setZipCompression(a.cfg.GetBool(cfgZipCompression))
+ a.settings.setClientCut(a.cfg.GetBool(cfgClientCut))
}
func (a *app) startServices() {
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 6708ad4..86718eb 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -96,6 +96,9 @@ const (
// Runtime.
cfgSoftMemoryLimit = "runtime.soft_memory_limit"
+ // Enabling client side object preparing for PUT operations.
+ cfgClientCut = "frostfs.client_cut"
+
// Command line args.
cmdHelp = "help"
cmdVersion = "version"
diff --git a/config/config.env b/config/config.env
index 62920a2..58e6814 100644
--- a/config/config.env
+++ b/config/config.env
@@ -98,3 +98,7 @@ HTTP_GW_TRACING_ENDPOINT="localhost:4317"
HTTP_GW_TRACING_EXPORTER="otlp_grpc"
HTTP_GW_RUNTIME_SOFT_MEMORY_LIMIT=1073741824
+
+# Parameters of requests to FrostFS
+# This flag enables client side object preparing.
+HTTP_GW_FROSTFS_CLIENT_CUT=false
diff --git a/config/config.yaml b/config/config.yaml
index d2804d6..3eae752 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -104,3 +104,8 @@ zip:
runtime:
soft_memory_limit: 1gb
+
+# Parameters of requests to FrostFS
+frostfs:
+ # This flag enables client side object preparing.
+ client_cut: false
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 95e6c8e..25d068b 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -54,6 +54,7 @@ $ cat http.log
| `prometheus` | [Prometheus configuration](#prometheus-section) |
| `tracing` | [Tracing configuration](#tracing-section) |
| `runtime` | [Runtime configuration](#runtime-section) |
+| `frostfs` | [Frostfs configuration](#frostfs-section) |
# General section
@@ -268,4 +269,17 @@ runtime:
| Parameter | Type | SIGHUP reload | Default value | Description |
|---------------------|--------|---------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `soft_memory_limit` | `size` | yes | maxint64 | Soft memory limit for the runtime. Zero or no value stands for no limit. If `GOMEMLIMIT` environment variable is set, the value from the configuration file will be ignored. |
\ No newline at end of file
+| `soft_memory_limit` | `size` | yes | maxint64 | Soft memory limit for the runtime. Zero or no value stands for no limit. If `GOMEMLIMIT` environment variable is set, the value from the configuration file will be ignored. |
+
+# `frostfs` section
+
+Contains parameters of requests to FrostFS.
+
+```yaml
+frostfs:
+ client_cut: false
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|--------------|--------|---------------|---------------|-------------------------------------------------|
+| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. |
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 579a55f..2bb4347 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -23,6 +23,7 @@ import (
type Config interface {
DefaultTimestamp() bool
ZipCompression() bool
+ ClientCut() bool
}
type Handler struct {
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index f5e0459..a8c4365 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -136,6 +136,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
var prm pool.PrmObjectPut
prm.SetHeader(*obj)
prm.SetPayload(file)
+ prm.SetClientCut(h.config.ClientCut())
bt := h.fetchBearerToken(ctx)
if bt != nil {
From 8bc246f8f91582f32f8b8889f89f935bf418de17 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Fri, 25 Aug 2023 14:53:59 +0300
Subject: [PATCH 034/161] [#70] Support configuring buffer size for put
Signed-off-by: Denis Kirillov
---
CHANGELOG.md | 2 +-
cmd/http-gw/app.go | 22 ++++++++++++++++++----
cmd/http-gw/settings.go | 7 +++++++
config/config.env | 2 ++
config/config.yaml | 2 ++
docs/gate-configuration.md | 8 +++++---
internal/handler/handler.go | 1 +
internal/handler/upload.go | 1 +
8 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d660f10..10d42c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@ This document outlines major changes between releases.
- Support impersonate bearer token (#40, #45)
- Tracing support (#20, #44, #60)
- Object name resolving with tree service (#30)
-- Add new `frostfs.client_cut` config param (#70)
+- Add new `frostfs.client_cut` and `frostfs.buffer_max_size_for_put` config params (#70)
### Changed
- Update prometheus to v1.15.0 (#35)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 93a6b6c..81bfb14 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -72,10 +72,11 @@ type (
// appSettings stores reloading parameters, so it has to provide getters and setters which use RWMutex.
appSettings struct {
- mu sync.RWMutex
- defaultTimestamp bool
- zipCompression bool
- clientCut bool
+ mu sync.RWMutex
+ defaultTimestamp bool
+ zipCompression bool
+ clientCut bool
+ bufferMaxSizeForPut uint64
}
)
@@ -177,6 +178,18 @@ func (s *appSettings) setClientCut(val bool) {
s.mu.Unlock()
}
+func (s *appSettings) BufferMaxSizeForPut() uint64 {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.bufferMaxSizeForPut
+}
+
+func (s *appSettings) setBufferMaxSizeForPut(val uint64) {
+ s.mu.Lock()
+ s.bufferMaxSizeForPut = val
+ s.mu.Unlock()
+}
+
func (a *app) initAppSettings() {
a.settings = &appSettings{}
@@ -462,6 +475,7 @@ func (a *app) updateSettings() {
a.settings.setDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
a.settings.setZipCompression(a.cfg.GetBool(cfgZipCompression))
a.settings.setClientCut(a.cfg.GetBool(cfgClientCut))
+ a.settings.setBufferMaxSizeForPut(a.cfg.GetUint64(cfgBufferMaxSizeForPut))
}
func (a *app) startServices() {
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 86718eb..b097aa3 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -39,6 +39,8 @@ const (
defaultSoftMemoryLimit = math.MaxInt64
+ defaultBufferMaxSizeForPut = 1024 * 1024 // 1mb
+
cfgServer = "server"
cfgTLSEnabled = "tls.enabled"
cfgTLSCertFile = "tls.cert_file"
@@ -98,6 +100,8 @@ const (
// Enabling client side object preparing for PUT operations.
cfgClientCut = "frostfs.client_cut"
+ // Sets max buffer size for read payload in put operations.
+ cfgBufferMaxSizeForPut = "frostfs.buffer_max_size_for_put"
// Command line args.
cmdHelp = "help"
@@ -160,6 +164,9 @@ func settings() *viper.Viper {
// pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
+ // frostfs:
+ v.SetDefault(cfgBufferMaxSizeForPut, defaultBufferMaxSizeForPut)
+
// web-server:
v.SetDefault(cfgWebReadBufferSize, 4096)
v.SetDefault(cfgWebWriteBufferSize, 4096)
diff --git a/config/config.env b/config/config.env
index 58e6814..06e71f8 100644
--- a/config/config.env
+++ b/config/config.env
@@ -102,3 +102,5 @@ HTTP_GW_RUNTIME_SOFT_MEMORY_LIMIT=1073741824
# Parameters of requests to FrostFS
# This flag enables client side object preparing.
HTTP_GW_FROSTFS_CLIENT_CUT=false
+# Sets max buffer size for read payload in put operations.
+HTTP_GW_FROSTFS_BUFFER_MAX_SIZE_FOR_PUT=1048576
diff --git a/config/config.yaml b/config/config.yaml
index 3eae752..15afeec 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -109,3 +109,5 @@ runtime:
frostfs:
# This flag enables client side object preparing.
client_cut: false
+ # Sets max buffer size for read payload in put operations.
+ buffer_max_size_for_put: 1048576
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 25d068b..852908b 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -278,8 +278,10 @@ Contains parameters of requests to FrostFS.
```yaml
frostfs:
client_cut: false
+ buffer_max_size_for_put: 1048576 # 1mb
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|--------------|--------|---------------|---------------|-------------------------------------------------|
-| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|---------------------------|----------|---------------|---------------|----------------------------------------------------------|
+| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. |
+| `buffer_max_size_for_put` | `uint64` | yes | `1048576` | Sets max buffer size for read payload in put operations. |
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 2bb4347..61e2bc1 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -24,6 +24,7 @@ type Config interface {
DefaultTimestamp() bool
ZipCompression() bool
ClientCut() bool
+ BufferMaxSizeForPut() uint64
}
type Handler struct {
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index a8c4365..b95896f 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -137,6 +137,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
prm.SetHeader(*obj)
prm.SetPayload(file)
prm.SetClientCut(h.config.ClientCut())
+ prm.SetBufferMaxSize(h.config.BufferMaxSizeForPut())
bt := h.fetchBearerToken(ctx)
if bt != nil {
From 9a5a2239bd0ef00e32f68dcb4d478293cb67dedf Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Wed, 4 Oct 2023 14:50:37 +0300
Subject: [PATCH 035/161] [#70] Support bucket/container caching
Mainly it was added because
we need to know if TZ hashing is disabled or not for container
Signed-off-by: Denis Kirillov
---
CHANGELOG.md | 6 ++-
cmd/http-gw/app.go | 2 +
cmd/http-gw/settings.go | 46 ++++++++++++++++++
config/config.env | 5 ++
config/config.yaml | 7 +++
docs/gate-configuration.md | 29 ++++++++++++
go.mod | 1 +
go.sum | 2 +
internal/cache/buckets.go | 68 +++++++++++++++++++++++++++
internal/data/bucket.go | 12 +++++
internal/handler/download.go | 34 ++------------
internal/handler/handler.go | 90 +++++++++++++++++++++++++++++++-----
internal/handler/head.go | 2 +-
internal/handler/upload.go | 10 ++--
internal/handler/utils.go | 11 +++++
internal/logs/logs.go | 4 +-
utils/params.go | 2 +
17 files changed, 283 insertions(+), 48 deletions(-)
create mode 100644 internal/cache/buckets.go
create mode 100644 internal/data/bucket.go
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10d42c0..4618d35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,11 @@ This document outlines major changes between releases.
- Support impersonate bearer token (#40, #45)
- Tracing support (#20, #44, #60)
- Object name resolving with tree service (#30)
-- Add new `frostfs.client_cut` and `frostfs.buffer_max_size_for_put` config params (#70)
+- Support client side object cut (#70)
+ - Add `frostfs.client_cut` config param
+ - Add `frostfs.buffer_max_size_for_put` config param
+ - Add bucket/container caching
+ - Disable homomorphic hash for PUT if it's disabled in container itself
### Changed
- Update prometheus to v1.15.0 (#35)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 81bfb14..3878277 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -11,6 +11,7 @@ import (
"syscall"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
@@ -567,6 +568,7 @@ func (a *app) AppParams() *utils.AppParams {
Pool: a.pool,
Owner: a.owner,
Resolver: a.resolver,
+ Cache: cache.NewBucketCache(getCacheOptions(a.cfg, a.log)),
}
}
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index b097aa3..cb309b7 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -13,6 +13,7 @@ import (
"strings"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
@@ -103,6 +104,10 @@ const (
// Sets max buffer size for read payload in put operations.
cfgBufferMaxSizeForPut = "frostfs.buffer_max_size_for_put"
+ // Caching.
+ cfgBucketsCacheLifetime = "cache.buckets.lifetime"
+ cfgBucketsCacheSize = "cache.buckets.size"
+
// Command line args.
cmdHelp = "help"
cmdVersion = "version"
@@ -541,3 +546,44 @@ func fetchSoftMemoryLimit(cfg *viper.Viper) int64 {
return int64(softMemoryLimit)
}
+
+func getCacheOptions(v *viper.Viper, l *zap.Logger) *cache.Config {
+ cacheCfg := cache.DefaultBucketConfig(l)
+
+ cacheCfg.Lifetime = fetchCacheLifetime(v, l, cfgBucketsCacheLifetime, cacheCfg.Lifetime)
+ cacheCfg.Size = fetchCacheSize(v, l, cfgBucketsCacheSize, cacheCfg.Size)
+
+ return cacheCfg
+}
+
+func fetchCacheLifetime(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue time.Duration) time.Duration {
+ if v.IsSet(cfgEntry) {
+ lifetime := v.GetDuration(cfgEntry)
+ if lifetime <= 0 {
+ l.Error("invalid lifetime, using default value (in seconds)",
+ zap.String("parameter", cfgEntry),
+ zap.Duration("value in config", lifetime),
+ zap.Duration("default", defaultValue))
+ } else {
+ return lifetime
+ }
+ }
+
+ return defaultValue
+}
+
+func fetchCacheSize(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue int) int {
+ if v.IsSet(cfgEntry) {
+ size := v.GetInt(cfgEntry)
+ if size <= 0 {
+ l.Error("invalid cache size, using default value",
+ zap.String("parameter", cfgEntry),
+ zap.Int("value in config", size),
+ zap.Int("default", defaultValue))
+ } else {
+ return size
+ }
+ }
+
+ return defaultValue
+}
diff --git a/config/config.env b/config/config.env
index 06e71f8..739cb96 100644
--- a/config/config.env
+++ b/config/config.env
@@ -104,3 +104,8 @@ HTTP_GW_RUNTIME_SOFT_MEMORY_LIMIT=1073741824
HTTP_GW_FROSTFS_CLIENT_CUT=false
# Sets max buffer size for read payload in put operations.
HTTP_GW_FROSTFS_BUFFER_MAX_SIZE_FOR_PUT=1048576
+
+# Caching
+# Cache which contains mapping of bucket name to bucket info
+HTTP_GW_CACHE_BUCKETS_LIFETIME=1m
+HTTP_GW_CACHE_BUCKETS_SIZE=1000
diff --git a/config/config.yaml b/config/config.yaml
index 15afeec..2cd20b5 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -111,3 +111,10 @@ frostfs:
client_cut: false
# Sets max buffer size for read payload in put operations.
buffer_max_size_for_put: 1048576
+
+# Caching
+cache:
+ # Cache which contains mapping of bucket name to bucket info
+ buckets:
+ lifetime: 1m
+ size: 1000
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 852908b..65fe618 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -55,6 +55,7 @@ $ cat http.log
| `tracing` | [Tracing configuration](#tracing-section) |
| `runtime` | [Runtime configuration](#runtime-section) |
| `frostfs` | [Frostfs configuration](#frostfs-section) |
+| `cache` | [Cache configuration](#cache-section) |
# General section
@@ -285,3 +286,31 @@ frostfs:
|---------------------------|----------|---------------|---------------|----------------------------------------------------------|
| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. |
| `buffer_max_size_for_put` | `uint64` | yes | `1048576` | Sets max buffer size for read payload in put operations. |
+
+
+### `cache` section
+
+```yaml
+cache:
+ buckets:
+ lifetime: 1m
+ size: 1000
+
+```
+
+| Parameter | Type | Default value | Description |
+|-----------------|-----------------------------------|-----------------------------------|----------------------------------------------------------------------------------------|
+| `buckets` | [Cache config](#cache-subsection) | `lifetime: 60s`
`size: 1000` | Cache which contains mapping of bucket name to bucket info. |
+
+
+#### `cache` subsection
+
+```yaml
+lifetime: 1m
+size: 1000
+```
+
+| Parameter | Type | Default value | Description |
+|------------|------------|------------------|-------------------------------|
+| `lifetime` | `duration` | depends on cache | Lifetime of entries in cache. |
+| `size` | `int` | depends on cache | LRU cache size. |
diff --git a/go.mod b/go.mod
index a58272e..5f9b1b0 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8
+ github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc
github.com/prometheus/client_golang v1.15.1
diff --git a/go.sum b/go.sum
index 0b9b457..dedb570 100644
--- a/go.sum
+++ b/go.sum
@@ -138,6 +138,8 @@ github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngE
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
+github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
diff --git a/internal/cache/buckets.go b/internal/cache/buckets.go
new file mode 100644
index 0000000..abeda6a
--- /dev/null
+++ b/internal/cache/buckets.go
@@ -0,0 +1,68 @@
+package cache
+
+import (
+ "fmt"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "github.com/bluele/gcache"
+ "go.uber.org/zap"
+)
+
+// BucketCache contains cache with objects and the lifetime of cache entries.
+type BucketCache struct {
+ cache gcache.Cache
+ logger *zap.Logger
+}
+
+// Config stores expiration params for cache.
+type Config struct {
+ Size int
+ Lifetime time.Duration
+ Logger *zap.Logger
+}
+
+const (
+ // DefaultBucketCacheSize is a default maximum number of entries in cache.
+ DefaultBucketCacheSize = 1e3
+ // DefaultBucketCacheLifetime is a default lifetime of entries in cache.
+ DefaultBucketCacheLifetime = time.Minute
+)
+
+// DefaultBucketConfig returns new default cache expiration values.
+func DefaultBucketConfig(logger *zap.Logger) *Config {
+ return &Config{
+ Size: DefaultBucketCacheSize,
+ Lifetime: DefaultBucketCacheLifetime,
+ Logger: logger,
+ }
+}
+
+// NewBucketCache creates an object of BucketCache.
+func NewBucketCache(config *Config) *BucketCache {
+ gc := gcache.New(config.Size).LRU().Expiration(config.Lifetime).Build()
+ return &BucketCache{cache: gc, logger: config.Logger}
+}
+
+// Get returns a cached object.
+func (o *BucketCache) Get(key string) *data.BucketInfo {
+ entry, err := o.cache.Get(key)
+ if err != nil {
+ return nil
+ }
+
+ result, ok := entry.(*data.BucketInfo)
+ if !ok {
+ o.logger.Warn(logs.InvalidCacheEntryType, zap.String("actual", fmt.Sprintf("%T", entry)),
+ zap.String("expected", fmt.Sprintf("%T", result)))
+ return nil
+ }
+
+ return result
+}
+
+// Put puts an object to cache.
+func (o *BucketCache) Put(bkt *data.BucketInfo) error {
+ return o.cache.Set(bkt.Name, bkt)
+}
diff --git a/internal/data/bucket.go b/internal/data/bucket.go
new file mode 100644
index 0000000..d99ca49
--- /dev/null
+++ b/internal/data/bucket.go
@@ -0,0 +1,12 @@
+package data
+
+import (
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+)
+
+type BucketInfo struct {
+ Name string // container name from system attribute
+ Zone string // container zone from system attribute
+ CID cid.ID
+ HomomorphicHashDisabled bool
+}
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 5021b4a..06a247a 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -14,8 +14,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@@ -30,7 +28,7 @@ func (h *Handler) DownloadByAddressOrBucketName(c *fasthttp.RequestCtx) {
var id oid.ID
err := id.DecodeString(test)
if err != nil {
- h.byBucketname(c, h.receiveFile)
+ h.byObjectName(c, h.receiveFile)
} else {
h.byAddress(c, h.receiveFile)
}
@@ -63,14 +61,6 @@ func (h *Handler) search(ctx context.Context, cid *cid.ID, key, val string, op o
return h.pool.SearchObjects(ctx, prm)
}
-func (h *Handler) getContainer(ctx context.Context, cnrID cid.ID) (container.Container, error) {
- prm := pool.PrmContainerGet{
- ContainerID: cnrID,
- }
-
- return h.pool.GetContainer(ctx, prm)
-}
-
func (h *Handler) addObjectToZip(zw *zip.Writer, obj *object.Object) (io.Writer, error) {
method := zip.Store
if h.config.ZipCompression() {
@@ -97,27 +87,13 @@ func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
ctx := utils.GetContextFromRequest(c)
- containerID, err := h.getContainerID(ctx, scid)
+ bktInfo, err := h.getBucketInfo(ctx, scid, log)
if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
+ logAndSendBucketError(c, log, err)
return
}
- // check if container exists here to be able to return 404 error,
- // otherwise we get this error only in object iteration step
- // and client get 200 OK.
- if _, err = h.getContainer(ctx, *containerID); err != nil {
- log.Error(logs.CouldNotCheckContainerExistence, zap.Error(err))
- if client.IsErrContainerNotFound(err) {
- response.Error(c, "Not Found", fasthttp.StatusNotFound)
- return
- }
- response.Error(c, "could not check container existence: "+err.Error(), fasthttp.StatusBadRequest)
- return
- }
-
- resSearch, err := h.search(ctx, containerID, object.AttributeFilePath, prefix, object.MatchCommonPrefix)
+ resSearch, err := h.search(ctx, &bktInfo.CID, object.AttributeFilePath, prefix, object.MatchCommonPrefix)
if err != nil {
log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
@@ -139,7 +115,7 @@ func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
empty := true
called := false
btoken := bearerToken(ctx)
- addr.SetContainer(*containerID)
+ addr.SetContainer(bktInfo.CID)
errIter := resSearch.Iterate(func(id oid.ID) bool {
called = true
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 61e2bc1..54602c2 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -3,14 +3,20 @@ package handler
import (
"context"
"errors"
+ "fmt"
"io"
"net/url"
+ "strings"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@@ -34,6 +40,7 @@ type Handler struct {
config Config
containerResolver *resolver.ContainerResolver
tree *tree.Tree
+ cache *cache.BucketCache
}
func New(params *utils.AppParams, config Config, tree *tree.Tree) *Handler {
@@ -44,6 +51,7 @@ func New(params *utils.AppParams, config Config, tree *tree.Tree) *Handler {
config: config,
containerResolver: params.Resolver,
tree: tree,
+ cache: params.Cache,
}
}
@@ -69,10 +77,9 @@ func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, requ
ctx := utils.GetContextFromRequest(c)
- cnrID, err := h.getContainerID(ctx, idCnr)
+ bktInfo, err := h.getBucketInfo(ctx, idCnr, log)
if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
+ logAndSendBucketError(c, log, err)
return
}
@@ -84,15 +91,15 @@ func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, requ
}
var addr oid.Address
- addr.SetContainer(*cnrID)
+ addr.SetContainer(bktInfo.CID)
addr.SetObject(*objID)
f(ctx, *h.newRequest(c, log), addr)
}
-// byBucketname is a wrapper for function (e.g. request.headObject, request.receiveFile) that
+// byObjectName is a wrapper for function (e.g. request.headObject, request.receiveFile) that
// prepares request and object address to it.
-func (h *Handler) byBucketname(req *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
+func (h *Handler) byObjectName(req *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
var (
bucketname = req.UserValue("cid").(string)
key = req.UserValue("oid").(string)
@@ -101,14 +108,13 @@ func (h *Handler) byBucketname(req *fasthttp.RequestCtx, f func(context.Context,
ctx := utils.GetContextFromRequest(req)
- cnrID, err := h.getContainerID(ctx, bucketname)
+ bktInfo, err := h.getBucketInfo(ctx, bucketname, log)
if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(req, "wrong container id", fasthttp.StatusBadRequest)
+ logAndSendBucketError(req, log, err)
return
}
- foundOid, err := h.tree.GetLatestVersion(ctx, cnrID, key)
+ foundOid, err := h.tree.GetLatestVersion(ctx, &bktInfo.CID, key)
if err != nil {
log.Error(logs.ObjectWasntFound, zap.Error(err))
response.Error(req, "object wasn't found", fasthttp.StatusNotFound)
@@ -121,7 +127,7 @@ func (h *Handler) byBucketname(req *fasthttp.RequestCtx, f func(context.Context,
}
var addr oid.Address
- addr.SetContainer(*cnrID)
+ addr.SetContainer(bktInfo.CID)
addr.SetObject(foundOid.OID)
f(ctx, *h.newRequest(req, log), addr)
@@ -175,3 +181,65 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, re
f(ctx, *h.newRequest(c, log), addrObj)
}
+
+// resolveContainer decode container id, if it's not a valid container id
+// then trey to resolve name using provided resolver.
+func (h *Handler) resolveContainer(ctx context.Context, containerID string) (*cid.ID, error) {
+ cnrID := new(cid.ID)
+ err := cnrID.DecodeString(containerID)
+ if err != nil {
+ cnrID, err = h.containerResolver.Resolve(ctx, containerID)
+ if err != nil && strings.Contains(err.Error(), "not found") {
+ err = fmt.Errorf("%w: %s", &apistatus.ContainerNotFound{}, err.Error())
+
+ }
+ }
+ return cnrID, err
+}
+
+func (h *Handler) getBucketInfo(ctx context.Context, containerName string, log *zap.Logger) (*data.BucketInfo, error) {
+ if bktInfo := h.cache.Get(containerName); bktInfo != nil {
+ return bktInfo, nil
+ }
+
+ cnrID, err := h.resolveContainer(ctx, containerName)
+ if err != nil {
+ return nil, err
+ }
+
+ bktInfo, err := h.readContainer(ctx, *cnrID)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = h.cache.Put(bktInfo); err != nil {
+ log.Warn(logs.CouldntPutBucketIntoCache,
+ zap.String("bucket name", bktInfo.Name),
+ zap.Stringer("bucket cid", bktInfo.CID),
+ zap.Error(err))
+ }
+
+ return bktInfo, nil
+}
+
+func (h *Handler) readContainer(ctx context.Context, cnrID cid.ID) (*data.BucketInfo, error) {
+ prm := pool.PrmContainerGet{ContainerID: cnrID}
+ res, err := h.pool.GetContainer(ctx, prm)
+ if err != nil {
+ return nil, fmt.Errorf("get frostfs container '%s': %w", cnrID.String(), err)
+ }
+
+ bktInfo := &data.BucketInfo{
+ CID: cnrID,
+ Name: cnrID.EncodeToString(),
+ }
+
+ if domain := container.ReadDomain(res); domain.Name() != "" {
+ bktInfo.Name = domain.Name()
+ bktInfo.Zone = domain.Zone()
+ }
+
+ bktInfo.HomomorphicHashDisabled = container.IsHomomorphicHashingDisabled(res)
+
+ return bktInfo, err
+}
diff --git a/internal/handler/head.go b/internal/handler/head.go
index f7478f1..9418567 100644
--- a/internal/handler/head.go
+++ b/internal/handler/head.go
@@ -110,7 +110,7 @@ func (h *Handler) HeadByAddressOrBucketName(c *fasthttp.RequestCtx) {
err := id.DecodeString(test)
if err != nil {
- h.byBucketname(c, h.headObject)
+ h.byObjectName(c, h.headObject)
} else {
h.byAddress(c, h.headObject)
}
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index b95896f..935b51b 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -57,10 +57,9 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
ctx := utils.GetContextFromRequest(req)
- idCnr, err := h.getContainerID(ctx, scid)
+ bktInfo, err := h.getBucketInfo(ctx, scid, log)
if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(req, "wrong container id", fasthttp.StatusBadRequest)
+ logAndSendBucketError(req, log, err)
return
}
@@ -129,7 +128,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
}
obj := object.New()
- obj.SetContainerID(*idCnr)
+ obj.SetContainerID(bktInfo.CID)
obj.SetOwnerID(h.ownerID)
obj.SetAttributes(attributes...)
@@ -138,6 +137,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
prm.SetPayload(file)
prm.SetClientCut(h.config.ClientCut())
prm.SetBufferMaxSize(h.config.BufferMaxSizeForPut())
+ prm.WithoutHomomorphicHash(bktInfo.HomomorphicHashDisabled)
bt := h.fetchBearerToken(ctx)
if bt != nil {
@@ -150,7 +150,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
}
addr.SetObject(idObj)
- addr.SetContainer(*idCnr)
+ addr.SetContainer(bktInfo.CID)
// Try to return the response, otherwise, if something went wrong, throw an error.
if err = newPutResponse(addr).encode(req); err != nil {
diff --git a/internal/handler/utils.go b/internal/handler/utils.go
index b51400c..a5a53ed 100644
--- a/internal/handler/utils.go
+++ b/internal/handler/utils.go
@@ -9,6 +9,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -58,3 +59,13 @@ func isValidValue(s string) bool {
}
return true
}
+
+func logAndSendBucketError(c *fasthttp.RequestCtx, log *zap.Logger, err error) {
+ log.Error(logs.CouldntGetBucket, zap.Error(err))
+
+ if client.IsErrContainerNotFound(err) {
+ response.Error(c, "Not Found", fasthttp.StatusNotFound)
+ return
+ }
+ response.Error(c, "could not get bucket: "+err.Error(), fasthttp.StatusBadRequest)
+}
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index ebb3c24..79ddce5 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -11,7 +11,6 @@ const (
CouldNotSearchForObjects = "could not search for objects" // Error in ../../downloader/download.go
ObjectNotFound = "object not found" // Error in ../../downloader/download.go
ReadObjectListFailed = "read object list failed" // Error in ../../downloader/download.go
- CouldNotCheckContainerExistence = "could not check container existence" // Error in ../../downloader/download.go
FailedToAddObjectToArchive = "failed to add object to archive" // Error in ../../downloader/download.go
IteratingOverSelectedObjectsFailed = "iterating over selected objects failed" // Error in ../../downloader/download.go
ObjectsNotFound = "objects not found" // Error in ../../downloader/download.go
@@ -68,4 +67,7 @@ const (
FailedToCreateTreePool = "failed to create tree pool" // Fatal in ../../settings.go
FailedToDialTreePool = "failed to dial tree pool" // Fatal in ../../settings.go
AddedStoragePeer = "added storage peer" // Info in ../../settings.go
+ CouldntGetBucket = "could not get bucket" // Error in ../handler/utils.go
+ CouldntPutBucketIntoCache = "couldn't put bucket info into cache" // Warn in ../handler/handler.go
+ InvalidCacheEntryType = "invalid cache entry type" // Warn in ../cache/buckets.go
)
diff --git a/utils/params.go b/utils/params.go
index a6fe59b..f27ff71 100644
--- a/utils/params.go
+++ b/utils/params.go
@@ -1,6 +1,7 @@
package utils
import (
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
@@ -12,4 +13,5 @@ type AppParams struct {
Pool *pool.Pool
Owner *user.ID
Resolver *resolver.ContainerResolver
+ Cache *cache.BucketCache
}
From 49d6a2756291452ad212bcaf4f96f165741ed41f Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Wed, 4 Oct 2023 15:39:44 +0300
Subject: [PATCH 036/161] [#70] Adjust status codes
Signed-off-by: Denis Kirillov
---
internal/handler/handler.go | 30 +++++++++++-------------------
internal/logs/logs.go | 2 +-
2 files changed, 12 insertions(+), 20 deletions(-)
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 54602c2..fa3c364 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -55,17 +55,6 @@ func New(params *utils.AppParams, config Config, tree *tree.Tree) *Handler {
}
}
-// getContainerID decode container id, if it's not a valid container id
-// then trey to resolve name using provided resolver.
-func (h *Handler) getContainerID(ctx context.Context, containerID string) (*cid.ID, error) {
- cnrID := new(cid.ID)
- err := cnrID.DecodeString(containerID)
- if err != nil {
- cnrID, err = h.containerResolver.Resolve(ctx, containerID)
- }
- return cnrID, err
-}
-
// byAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that
// prepares request and object address to it.
func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
@@ -116,7 +105,12 @@ func (h *Handler) byObjectName(req *fasthttp.RequestCtx, f func(context.Context,
foundOid, err := h.tree.GetLatestVersion(ctx, &bktInfo.CID, key)
if err != nil {
- log.Error(logs.ObjectWasntFound, zap.Error(err))
+ if errors.Is(err, tree.ErrNodeAccessDenied) {
+ response.Error(req, "Access Denied", fasthttp.StatusForbidden)
+ return
+ }
+ log.Error(logs.GetLatestObjectVersion, zap.Error(err))
+
response.Error(req, "object wasn't found", fasthttp.StatusNotFound)
return
}
@@ -144,14 +138,13 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, re
ctx := utils.GetContextFromRequest(c)
- containerID, err := h.getContainerID(ctx, scid)
+ bktInfo, err := h.getBucketInfo(ctx, scid, log)
if err != nil {
- log.Error(logs.WrongContainerID, zap.Error(err))
- response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
+ logAndSendBucketError(c, log, err)
return
}
- res, err := h.search(ctx, containerID, key, val, object.MatchStringEqual)
+ res, err := h.search(ctx, &bktInfo.CID, key, val, object.MatchStringEqual)
if err != nil {
log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
@@ -176,7 +169,7 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, re
}
var addrObj oid.Address
- addrObj.SetContainer(*containerID)
+ addrObj.SetContainer(bktInfo.CID)
addrObj.SetObject(buf[0])
f(ctx, *h.newRequest(c, log), addrObj)
@@ -190,8 +183,7 @@ func (h *Handler) resolveContainer(ctx context.Context, containerID string) (*ci
if err != nil {
cnrID, err = h.containerResolver.Resolve(ctx, containerID)
if err != nil && strings.Contains(err.Error(), "not found") {
- err = fmt.Errorf("%w: %s", &apistatus.ContainerNotFound{}, err.Error())
-
+ err = fmt.Errorf("%w: %s", new(apistatus.ContainerNotFound), err.Error())
}
}
return cnrID, err
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index 79ddce5..0534ebc 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -6,7 +6,7 @@ const (
CouldNotReceiveObject = "could not receive object" // Error in ../../downloader/download.go
WrongContainerID = "wrong container id" // Error in ../../downloader/download.go and uploader/upload.go
WrongObjectID = "wrong object id" // Error in ../../downloader/download.go
- ObjectWasntFound = "object wasn't found" // Error in ../../downloader/download.go
+ GetLatestObjectVersion = "get latest object version" // Error in ../../downloader/download.go
ObjectWasDeleted = "object was deleted" // Error in ../../downloader/download.go
CouldNotSearchForObjects = "could not search for objects" // Error in ../../downloader/download.go
ObjectNotFound = "object not found" // Error in ../../downloader/download.go
From 1ced82a71475ae8c6dac9f78185294af1985b05e Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Fri, 6 Oct 2023 16:14:09 +0300
Subject: [PATCH 037/161] [#70] Fix log messages (move to constants)
Signed-off-by: Denis Kirillov
---
cmd/http-gw/settings.go | 4 ++--
internal/logs/logs.go | 3 +++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index cb309b7..7e53158 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -560,7 +560,7 @@ func fetchCacheLifetime(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultV
if v.IsSet(cfgEntry) {
lifetime := v.GetDuration(cfgEntry)
if lifetime <= 0 {
- l.Error("invalid lifetime, using default value (in seconds)",
+ l.Error(logs.InvalidLifetimeUsingDefaultValue,
zap.String("parameter", cfgEntry),
zap.Duration("value in config", lifetime),
zap.Duration("default", defaultValue))
@@ -576,7 +576,7 @@ func fetchCacheSize(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue
if v.IsSet(cfgEntry) {
size := v.GetInt(cfgEntry)
if size <= 0 {
- l.Error("invalid cache size, using default value",
+ l.Error(logs.InvalidCacheSizeUsingDefaultValue,
zap.String("parameter", cfgEntry),
zap.Int("value in config", size),
zap.Int("default", defaultValue))
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index 0534ebc..9d464c3 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -70,4 +70,7 @@ const (
CouldntGetBucket = "could not get bucket" // Error in ../handler/utils.go
CouldntPutBucketIntoCache = "couldn't put bucket info into cache" // Warn in ../handler/handler.go
InvalidCacheEntryType = "invalid cache entry type" // Warn in ../cache/buckets.go
+ InvalidLifetimeUsingDefaultValue = "invalid lifetime, using default value (in seconds)" // Error in ../../cmd/http-gw/settings.go
+ InvalidCacheSizeUsingDefaultValue = "invalid cache size, using default value" // Error in ../../cmd/http-gw/settings.go
+
)
From 7fa973b26123532a12a3c2b08bec045038c602cf Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Thu, 9 Nov 2023 11:11:30 +0300
Subject: [PATCH 038/161] [#89] Add support zapjournald logger configuration
Signed-off-by: Roman Loginov
---
CHANGELOG.md | 1 +
cmd/http-gw/main.go | 2 +-
cmd/http-gw/settings.go | 57 ++++++++++++++++++++++++++++++++------
config/config.yaml | 1 +
docs/gate-configuration.md | 9 +++---
go.mod | 2 ++
go.sum | 4 +++
7 files changed, 63 insertions(+), 13 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4618d35..fd19460 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ This document outlines major changes between releases.
- Add `frostfs.buffer_max_size_for_put` config param
- Add bucket/container caching
- Disable homomorphic hash for PUT if it's disabled in container itself
+- Add new `logger.destination` config param (#89)
### Changed
- Update prometheus to v1.15.0 (#35)
diff --git a/cmd/http-gw/main.go b/cmd/http-gw/main.go
index 5762675..ea9fbd7 100644
--- a/cmd/http-gw/main.go
+++ b/cmd/http-gw/main.go
@@ -9,7 +9,7 @@ import (
func main() {
globalContext, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
v := settings()
- logger, atomicLevel := newLogger(v)
+ logger, atomicLevel := pickLogger(v)
application := newApp(globalContext, WithLogger(logger, atomicLevel), WithConfig(v))
go application.Serve()
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 7e53158..6e633ba 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -19,15 +19,22 @@ import (
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
treepool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
+ "git.frostfs.info/TrueCloudLab/zapjournald"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/spf13/pflag"
"github.com/spf13/viper"
+ "github.com/ssgreg/journald"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
)
+const (
+ destinationStdout = "stdout"
+ destinationJournald = "journald"
+)
+
const (
defaultRebalanceTimer = 60 * time.Second
defaultRequestTimeout = 15 * time.Second
@@ -74,7 +81,8 @@ const (
cfgPoolErrorThreshold = "pool_error_threshold"
// Logger.
- cfgLoggerLevel = "logger.level"
+ cfgLoggerLevel = "logger.level"
+ cfgLoggerDestination = "logger.destination"
// Wallet.
cfgWalletPassphrase = "wallet.passphrase"
@@ -165,6 +173,7 @@ func settings() *viper.Viper {
// logger:
v.SetDefault(cfgLoggerLevel, "debug")
+ v.SetDefault(cfgLoggerDestination, "stdout")
// pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
@@ -349,7 +358,25 @@ func mergeConfig(v *viper.Viper, fileName string) error {
return v.MergeConfig(cfgFile)
}
-// newLogger constructs a zap.Logger instance for current application.
+func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
+ lvl, err := getLogLevel(v)
+ if err != nil {
+ panic(err)
+ }
+
+ dest := v.GetString(cfgLoggerDestination)
+
+ switch dest {
+ case destinationStdout:
+ return newStdoutLogger(lvl)
+ case destinationJournald:
+ return newJournaldLogger(lvl)
+ default:
+ panic(fmt.Sprintf("wrong destination for logger: %s", dest))
+ }
+}
+
+// newStdoutLogger constructs a zap.Logger instance for current application.
// Panics on failure.
//
// Logger is built from zap's production logging configuration with:
@@ -360,12 +387,7 @@ func mergeConfig(v *viper.Viper, fileName string) error {
// Logger records a stack trace for all messages at or above fatal level.
//
// See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace.
-func newLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
- lvl, err := getLogLevel(v)
- if err != nil {
- panic(err)
- }
-
+func newStdoutLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
c := zap.NewProductionConfig()
c.Level = zap.NewAtomicLevelAt(lvl)
c.Encoding = "console"
@@ -381,6 +403,25 @@ func newLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
return l, c.Level
}
+func newJournaldLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
+ c := zap.NewProductionConfig()
+ c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+ c.Level = zap.NewAtomicLevelAt(lvl)
+
+ encoder := zapcore.NewConsoleEncoder(c.EncoderConfig)
+
+ core := zapjournald.NewCore(zap.NewAtomicLevelAt(lvl), encoder, &journald.Journal{}, zapjournald.SyslogFields)
+ coreWithContext := core.With([]zapcore.Field{
+ zapjournald.SyslogFacility(zapjournald.LogDaemon),
+ zapjournald.SyslogIdentifier(),
+ zapjournald.SyslogPid(),
+ })
+
+ l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
+
+ return l, c.Level
+}
+
func getLogLevel(v *viper.Viper) (zapcore.Level, error) {
var lvl zapcore.Level
lvlStr := v.GetString(cfgLoggerLevel)
diff --git a/config/config.yaml b/config/config.yaml
index 2cd20b5..6ab9994 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -16,6 +16,7 @@ tracing:
logger:
level: debug # Log level.
+ destination: stdout
server:
- address: 0.0.0.0:8080
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 65fe618..1b51848 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -160,12 +160,13 @@ server:
```yaml
logger:
level: debug
+ destination: stdout
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|-----------|----------|---------------|---------------|----------------------------------------------------------------------------------------------------|
-| `level` | `string` | yes | `debug` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. |
-
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|---------------|----------|---------------|---------------|----------------------------------------------------------------------------------------------------|
+| `level` | `string` | yes | `debug` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. |
+| `destination` | `string` | no | `stdout` | Destination for logger: `stdout` or `journald` |
# `web` section
diff --git a/go.mod b/go.mod
index 5f9b1b0..98abcb7 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8
+ git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d
github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc
@@ -13,6 +14,7 @@ require (
github.com/prometheus/client_model v0.3.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
+ github.com/ssgreg/journald v1.0.0
github.com/stretchr/testify v1.8.3
github.com/testcontainers/testcontainers-go v0.13.0
github.com/valyala/fasthttp v1.34.0
diff --git a/go.sum b/go.sum
index dedb570..5d46822 100644
--- a/go.sum
+++ b/go.sum
@@ -53,6 +53,8 @@ git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9m
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
git.frostfs.info/TrueCloudLab/tzhash v1.8.0/go.mod h1:dhY+oy274hV8wGvGL4MwwMpdL3GYvaX1a8GQZQHvlF8=
+git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d h1:Z9UuI+jxzPtwQZUMmATdTuA8/8l2jzBY1rVh/gwBDsw=
+git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d/go.mod h1:rQFJJdEOV7KbbMtQYR2lNfiZk+ONRDJSbMCTWxKt8Fw=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
@@ -873,6 +875,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
+github.com/ssgreg/journald v1.0.0 h1:0YmTDPJXxcWDPba12qNMdO6TxvfkFSYpFIJ31CwmLcU=
+github.com/ssgreg/journald v1.0.0/go.mod h1:RUckwmTM8ghGWPslq2+ZBZzbb9/2KgjzYZ4JEP+oRt0=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
From dc8d0d4ab380b66586522a449aac902bc2102e1a Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 22 Nov 2023 11:56:59 +0300
Subject: [PATCH 039/161] [#95] Add dirty version check
Signed-off-by: Alex Vanin
---
.forgejo/workflows/builds.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.forgejo/workflows/builds.yml b/.forgejo/workflows/builds.yml
index aac6857..97ac86b 100644
--- a/.forgejo/workflows/builds.yml
+++ b/.forgejo/workflows/builds.yml
@@ -18,3 +18,6 @@ jobs:
- name: Build binary
run: make
+
+ - name: Check dirty suffix
+ run: if [[ $(make version) == *"dirty"* ]]; then echo "Version has dirty suffix" && exit 1; fi
From a375af7d98373a5d7177a04755bc87d95f1b77ef Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Tue, 28 Nov 2023 11:29:08 +0300
Subject: [PATCH 040/161] [#91] Add support namespaces
Signed-off-by: Roman Loginov
---
CHANGELOG.md | 3 +-
cmd/http-gw/app.go | 66 ++++++++++++++++++++++++++---
cmd/http-gw/integration_test.go | 38 ++++++++++++++++-
cmd/http-gw/settings.go | 10 +++++
config/config.env | 5 +++
config/config.yaml | 4 ++
docs/gate-configuration.md | 49 ++++++++++++++-------
go.mod | 2 +-
internal/cache/buckets.go | 10 +++--
internal/handler/handler.go | 9 +++-
internal/handler/middleware/util.go | 26 ++++++++++++
resolver/resolver.go | 51 ++++++++++++++++++----
12 files changed, 236 insertions(+), 37 deletions(-)
create mode 100644 internal/handler/middleware/util.go
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd19460..e2dd2c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,7 +17,8 @@ This document outlines major changes between releases.
- Add `frostfs.buffer_max_size_for_put` config param
- Add bucket/container caching
- Disable homomorphic hash for PUT if it's disabled in container itself
-- Add new `logger.destination` config param (#89)
+- Add new `logger.destination` config param (#89)
+- Add support namespaces (#91)
### Changed
- Update prometheus to v1.15.0 (#35)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 3878277..1ad1f20 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -7,13 +7,16 @@ import (
"os"
"os/signal"
"runtime/debug"
+ "strings"
"sync"
"syscall"
"time"
+ v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
@@ -34,6 +37,7 @@ import (
"github.com/spf13/viper"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
+ "golang.org/x/exp/slices"
)
type (
@@ -78,6 +82,8 @@ type (
zipCompression bool
clientCut bool
bufferMaxSizeForPut uint64
+ namespaceHeader string
+ defaultNamespaces []string
}
)
@@ -209,6 +215,7 @@ func (a *app) getResolverConfig() ([]string, *resolver.Config) {
resolveCfg := &resolver.Config{
FrostFS: resolver.NewFrostFSResolver(a.pool),
RPCAddress: a.cfg.GetString(cfgRPCEndpoint),
+ Settings: a.settings,
}
order := a.cfg.GetStringSlice(cfgResolveOrder)
@@ -477,6 +484,8 @@ func (a *app) updateSettings() {
a.settings.setZipCompression(a.cfg.GetBool(cfgZipCompression))
a.settings.setClientCut(a.cfg.GetBool(cfgClientCut))
a.settings.setBufferMaxSizeForPut(a.cfg.GetUint64(cfgBufferMaxSizeForPut))
+ a.settings.setNamespaceHeader(a.cfg.GetString(cfgResolveNamespaceHeader))
+ a.settings.setDefaultNamespaces(a.cfg.GetStringSlice(cfgResolveDefaultNamespaces))
}
func (a *app) startServices() {
@@ -510,15 +519,15 @@ func (a *app) configureRouter(handler *handler.Handler) {
response.Error(r, "Method Not Allowed", fasthttp.StatusMethodNotAllowed)
}
- r.POST("/upload/{cid}", a.logger(a.tokenizer(a.tracer(handler.Upload))))
+ r.POST("/upload/{cid}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.Upload)))))
a.log.Info(logs.AddedPathUploadCid)
- r.GET("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(handler.DownloadByAddressOrBucketName))))
- r.HEAD("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(handler.HeadByAddressOrBucketName))))
+ r.GET("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAddressOrBucketName)))))
+ r.HEAD("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAddressOrBucketName)))))
a.log.Info(logs.AddedPathGetCidOid)
- r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(handler.DownloadByAttribute))))
- r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(handler.HeadByAttribute))))
+ r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAttribute)))))
+ r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAttribute)))))
a.log.Info(logs.AddedPathGetByAttributeCidAttrKeyAttrVal)
- r.GET("/zip/{cid}/{prefix:*}", a.logger(a.tokenizer(a.tracer(handler.DownloadZipped))))
+ r.GET("/zip/{cid}/{prefix:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadZipped)))))
a.log.Info(logs.AddedPathZipCidPrefix)
a.webServer.Handler = r.Handler
@@ -562,6 +571,18 @@ func (a *app) tracer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
}
}
+func (a *app) reqNamespace(h fasthttp.RequestHandler) fasthttp.RequestHandler {
+ return func(req *fasthttp.RequestCtx) {
+ appCtx := utils.GetContextFromRequest(req)
+
+ nsBytes := req.Request.Header.Peek(a.settings.NamespaceHeader())
+ appCtx = middleware.SetNamespace(appCtx, string(nsBytes))
+
+ utils.SetContextToRequest(appCtx, req)
+ h(req)
+ }
+}
+
func (a *app) AppParams() *utils.AppParams {
return &utils.AppParams{
Logger: a.log,
@@ -669,3 +690,36 @@ func (a *app) setRuntimeParameters() {
zap.Int64("old_value", previous))
}
}
+
+func (s *appSettings) NamespaceHeader() string {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.namespaceHeader
+}
+
+func (s *appSettings) setNamespaceHeader(nsHeader string) {
+ s.mu.Lock()
+ s.namespaceHeader = nsHeader
+ s.mu.Unlock()
+}
+
+func (s *appSettings) FormContainerZone(ns string) (zone string, isDefault bool) {
+ s.mu.RLock()
+ namespaces := s.defaultNamespaces
+ s.mu.RUnlock()
+ if slices.Contains(namespaces, ns) {
+ return v2container.SysAttributeZoneDefault, true
+ }
+
+ return ns + ".ns", false
+}
+
+func (s *appSettings) setDefaultNamespaces(namespaces []string) {
+ for i := range namespaces { // to be set namespaces in env variable as `HTTP_GW_RESOLVE_BUCKET_DEFAULT_NAMESPACES="" "root"`
+ namespaces[i] = strings.Trim(namespaces[i], "\"")
+ }
+
+ s.mu.Lock()
+ s.defaultNamespaces = namespaces
+ s.mu.Unlock()
+}
diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go
index 76a8325..f76c3ce 100644
--- a/cmd/http-gw/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -29,6 +29,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
+ "go.uber.org/zap/zapcore"
)
type putResponse struct {
@@ -68,6 +69,7 @@ func TestIntegration(t *testing.T) {
t.Run("simple get "+version, func(t *testing.T) { simpleGet(ctx, t, clientPool, ownerID, CID, version) })
t.Run("get by attribute "+version, func(t *testing.T) { getByAttr(ctx, t, clientPool, ownerID, CID, version) })
t.Run("get zip "+version, func(t *testing.T) { getZip(ctx, t, clientPool, ownerID, CID, version) })
+ t.Run("test namespaces "+version, func(t *testing.T) { checkNamespaces(ctx, t, clientPool, ownerID, CID, version) })
cancel()
server.Wait()
@@ -81,7 +83,7 @@ func runServer() (App, context.CancelFunc) {
cancelCtx, cancel := context.WithCancel(context.Background())
v := getDefaultConfig()
- l, lvl := newLogger(v)
+ l, lvl := newStdoutLogger(zapcore.DebugLevel)
application := newApp(cancelCtx, WithConfig(v), WithLogger(l, lvl))
go application.Serve()
@@ -338,6 +340,40 @@ func checkZip(t *testing.T, data []byte, length int64, names, contents []string)
}
}
+func checkNamespaces(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID, version string) {
+ content := "content of file"
+ attributes := map[string]string{
+ "some-attr": "some-get-value",
+ }
+
+ id := putObject(ctx, t, clientPool, ownerID, CID, content, attributes)
+
+ req, err := http.NewRequest(http.MethodGet, testHost+"/get/"+testContainerName+"/"+id.String(), nil)
+ require.NoError(t, err)
+ req.Header.Set(defaultNamespaceHeader, "")
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ checkGetResponse(t, resp, content, attributes)
+
+ req, err = http.NewRequest(http.MethodGet, testHost+"/get/"+testContainerName+"/"+id.String(), nil)
+ require.NoError(t, err)
+ req.Header.Set(defaultNamespaceHeader, "root")
+
+ resp, err = http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ checkGetResponse(t, resp, content, attributes)
+
+ req, err = http.NewRequest(http.MethodGet, testHost+"/get/"+testContainerName+"/"+id.String(), nil)
+ require.NoError(t, err)
+ req.Header.Set(defaultNamespaceHeader, "root2")
+
+ resp, err = http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ require.Equal(t, http.StatusNotFound, resp.StatusCode)
+
+}
+
func createDockerContainer(ctx context.Context, t *testing.T, image string) testcontainers.Container {
req := testcontainers.ContainerRequest{
Image: image,
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 6e633ba..24e4f37 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -49,6 +49,8 @@ const (
defaultBufferMaxSizeForPut = 1024 * 1024 // 1mb
+ defaultNamespaceHeader = "X-Frostfs-Namespace"
+
cfgServer = "server"
cfgTLSEnabled = "tls.enabled"
cfgTLSCertFile = "tls.cert_file"
@@ -116,6 +118,10 @@ const (
cfgBucketsCacheLifetime = "cache.buckets.lifetime"
cfgBucketsCacheSize = "cache.buckets.size"
+ // Bucket resolving options.
+ cfgResolveNamespaceHeader = "resolve_bucket.namespace_header"
+ cfgResolveDefaultNamespaces = "resolve_bucket.default_namespaces"
+
// Command line args.
cmdHelp = "help"
cmdVersion = "version"
@@ -199,6 +205,10 @@ func settings() *viper.Viper {
v.SetDefault(cfgPprofAddress, "localhost:8083")
v.SetDefault(cfgPrometheusAddress, "localhost:8084")
+ // resolve bucket
+ v.SetDefault(cfgResolveNamespaceHeader, defaultNamespaceHeader)
+ v.SetDefault(cfgResolveDefaultNamespaces, []string{"", "root"})
+
// Binding flags
if err := v.BindPFlag(cfgPprofEnabled, flags.Lookup(cmdPprof)); err != nil {
panic(err)
diff --git a/config/config.env b/config/config.env
index 739cb96..be42af9 100644
--- a/config/config.env
+++ b/config/config.env
@@ -109,3 +109,8 @@ HTTP_GW_FROSTFS_BUFFER_MAX_SIZE_FOR_PUT=1048576
# Cache which contains mapping of bucket name to bucket info
HTTP_GW_CACHE_BUCKETS_LIFETIME=1m
HTTP_GW_CACHE_BUCKETS_SIZE=1000
+
+# Header to determine zone to resolve bucket name
+HTTP_GW_RESOLVE_BUCKET_NAMESPACE_HEADER=X-Frostfs-Namespace
+# Namespaces that should be handled as default
+HTTP_GW_RESOLVE_BUCKET_DEFAULT_NAMESPACES="" "root"
diff --git a/config/config.yaml b/config/config.yaml
index 6ab9994..020b0dd 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -119,3 +119,7 @@ cache:
buckets:
lifetime: 1m
size: 1000
+
+resolve_bucket:
+ namespace_header: X-Frostfs-Namespace
+ default_namespaces: [ "", "root" ]
\ No newline at end of file
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 1b51848..fe4b50f 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -40,22 +40,23 @@ $ cat http.log
# Structure
-| Section | Description |
-|-----------------|-------------------------------------------------------|
-| no section | [General parameters](#general-section) |
-| `wallet` | [Wallet configuration](#wallet-section) |
-| `peers` | [Nodes configuration](#peers-section) |
-| `logger` | [Logger configuration](#logger-section) |
-| `web` | [Web configuration](#web-section) |
-| `server` | [Server configuration](#server-section) |
-| `upload-header` | [Upload header configuration](#upload-header-section) |
-| `zip` | [ZIP configuration](#zip-section) |
-| `pprof` | [Pprof configuration](#pprof-section) |
-| `prometheus` | [Prometheus configuration](#prometheus-section) |
-| `tracing` | [Tracing configuration](#tracing-section) |
-| `runtime` | [Runtime configuration](#runtime-section) |
-| `frostfs` | [Frostfs configuration](#frostfs-section) |
-| `cache` | [Cache configuration](#cache-section) |
+| Section | Description |
+|------------------|----------------------------------------------------------------|
+| no section | [General parameters](#general-section) |
+| `wallet` | [Wallet configuration](#wallet-section) |
+| `peers` | [Nodes configuration](#peers-section) |
+| `logger` | [Logger configuration](#logger-section) |
+| `web` | [Web configuration](#web-section) |
+| `server` | [Server configuration](#server-section) |
+| `upload-header` | [Upload header configuration](#upload-header-section) |
+| `zip` | [ZIP configuration](#zip-section) |
+| `pprof` | [Pprof configuration](#pprof-section) |
+| `prometheus` | [Prometheus configuration](#prometheus-section) |
+| `tracing` | [Tracing configuration](#tracing-section) |
+| `runtime` | [Runtime configuration](#runtime-section) |
+| `frostfs` | [Frostfs configuration](#frostfs-section) |
+| `cache` | [Cache configuration](#cache-section) |
+| `resolve_bucket` | [Bucket name resolving configuration](#resolve_bucket-section) |
# General section
@@ -315,3 +316,19 @@ size: 1000
|------------|------------|------------------|-------------------------------|
| `lifetime` | `duration` | depends on cache | Lifetime of entries in cache. |
| `size` | `int` | depends on cache | LRU cache size. |
+
+
+# `resolve_bucket` section
+
+Bucket name resolving parameters from and to container ID.
+
+```yaml
+resolve_bucket:
+ namespace_header: X-Frostfs-Namespace
+ default_namespaces: [ "", "root" ]
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|----------------------|------------|---------------|-----------------------|--------------------------------------------------------------------------------------------------------------------------|
+| `namespace_header` | `string` | yes | `X-Frostfs-Namespace` | Header to determine zone to resolve bucket name. |
+| `default_namespaces` | `[]string` | yes | ["","root"] | Namespaces that should be handled as default. |
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 98abcb7..c398358 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
go.uber.org/zap v1.24.0
+ golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
google.golang.org/grpc v1.55.0
)
@@ -102,7 +103,6 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
- golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.8.0 // indirect
diff --git a/internal/cache/buckets.go b/internal/cache/buckets.go
index abeda6a..f8e6d88 100644
--- a/internal/cache/buckets.go
+++ b/internal/cache/buckets.go
@@ -46,8 +46,8 @@ func NewBucketCache(config *Config) *BucketCache {
}
// Get returns a cached object.
-func (o *BucketCache) Get(key string) *data.BucketInfo {
- entry, err := o.cache.Get(key)
+func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
+ entry, err := o.cache.Get(formKey(ns, bktName))
if err != nil {
return nil
}
@@ -64,5 +64,9 @@ func (o *BucketCache) Get(key string) *data.BucketInfo {
// Put puts an object to cache.
func (o *BucketCache) Put(bkt *data.BucketInfo) error {
- return o.cache.Set(bkt.Name, bkt)
+ return o.cache.Set(formKey(bkt.Zone, bkt.Name), bkt)
+}
+
+func formKey(ns, name string) string {
+ return name + "." + ns
}
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index fa3c364..757b5be 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -10,6 +10,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
@@ -31,6 +32,7 @@ type Config interface {
ZipCompression() bool
ClientCut() bool
BufferMaxSizeForPut() uint64
+ NamespaceHeader() string
}
type Handler struct {
@@ -190,7 +192,12 @@ func (h *Handler) resolveContainer(ctx context.Context, containerID string) (*ci
}
func (h *Handler) getBucketInfo(ctx context.Context, containerName string, log *zap.Logger) (*data.BucketInfo, error) {
- if bktInfo := h.cache.Get(containerName); bktInfo != nil {
+ ns, err := middleware.GetNamespace(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ if bktInfo := h.cache.Get(ns, containerName); bktInfo != nil {
return bktInfo, nil
}
diff --git a/internal/handler/middleware/util.go b/internal/handler/middleware/util.go
new file mode 100644
index 0000000..284513a
--- /dev/null
+++ b/internal/handler/middleware/util.go
@@ -0,0 +1,26 @@
+package middleware
+
+import (
+ "context"
+ "fmt"
+)
+
+// keyWrapper is wrapper for context keys.
+type keyWrapper string
+
+const nsKey = keyWrapper("namespace")
+
+// GetNamespace extract namespace from context.
+func GetNamespace(ctx context.Context) (string, error) {
+ ns, ok := ctx.Value(nsKey).(string)
+ if !ok {
+ return "", fmt.Errorf("couldn't get namespace from context")
+ }
+
+ return ns, nil
+}
+
+// SetNamespace sets namespace in the context.
+func SetNamespace(ctx context.Context, ns string) context.Context {
+ return context.WithValue(ctx, nsKey, ns)
+}
diff --git a/resolver/resolver.go b/resolver/resolver.go
index e6707e2..e7615d4 100644
--- a/resolver/resolver.go
+++ b/resolver/resolver.go
@@ -6,6 +6,7 @@ import (
"fmt"
"sync"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
@@ -28,9 +29,14 @@ type FrostFS interface {
SystemDNS(context.Context) (string, error)
}
+type Settings interface {
+ FormContainerZone(ns string) (zone string, isDefault bool)
+}
+
type Config struct {
FrostFS FrostFS
RPCAddress string
+ Settings Settings
}
type ContainerResolver struct {
@@ -135,29 +141,43 @@ func (r *ContainerResolver) equals(resolverNames []string) bool {
func newResolver(name string, cfg *Config) (*Resolver, error) {
switch name {
case DNSResolver:
- return NewDNSResolver(cfg.FrostFS)
+ return NewDNSResolver(cfg.FrostFS, cfg.Settings)
case NNSResolver:
- return NewNNSResolver(cfg.RPCAddress)
+ return NewNNSResolver(cfg.RPCAddress, cfg.Settings)
default:
return nil, fmt.Errorf("unknown resolver: %s", name)
}
}
-func NewDNSResolver(frostFS FrostFS) (*Resolver, error) {
+func NewDNSResolver(frostFS FrostFS, settings Settings) (*Resolver, error) {
if frostFS == nil {
return nil, fmt.Errorf("pool must not be nil for DNS resolver")
}
+ if settings == nil {
+ return nil, fmt.Errorf("resolver settings must not be nil for DNS resolver")
+ }
var dns ns.DNS
resolveFunc := func(ctx context.Context, name string) (*cid.ID, error) {
- domain, err := frostFS.SystemDNS(ctx)
+ var err error
+
+ namespace, err := middleware.GetNamespace(ctx)
if err != nil {
- return nil, fmt.Errorf("read system DNS parameter of the FrostFS: %w", err)
+ return nil, err
}
- domain = name + "." + domain
+ zone, isDefault := settings.FormContainerZone(namespace)
+ if isDefault {
+ zone, err = frostFS.SystemDNS(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("read system DNS parameter of the FrostFS: %w", err)
+ }
+ }
+
+ domain := name + "." + zone
cnrID, err := dns.ResolveContainerName(domain)
+
if err != nil {
return nil, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err)
}
@@ -170,17 +190,32 @@ func NewDNSResolver(frostFS FrostFS) (*Resolver, error) {
}, nil
}
-func NewNNSResolver(rpcAddress string) (*Resolver, error) {
+func NewNNSResolver(rpcAddress string, settings Settings) (*Resolver, error) {
+ if rpcAddress == "" {
+ return nil, fmt.Errorf("rpc address must not be empty for NNS resolver")
+ }
+ if settings == nil {
+ return nil, fmt.Errorf("resolver settings must not be nil for NNS resolver")
+ }
+
var nns ns.NNS
if err := nns.Dial(rpcAddress); err != nil {
return nil, fmt.Errorf("could not dial nns: %w", err)
}
- resolveFunc := func(_ context.Context, name string) (*cid.ID, error) {
+ resolveFunc := func(ctx context.Context, name string) (*cid.ID, error) {
var d container.Domain
d.SetName(name)
+ namespace, err := middleware.GetNamespace(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ zone, _ := settings.FormContainerZone(namespace)
+ d.SetZone(zone)
+
cnrID, err := nns.ResolveContainerDomain(d)
if err != nil {
return nil, fmt.Errorf("couldn't resolve container '%s': %w", name, err)
From 2e28b2ac85a905a92f064f58f7aa843fddd4b5f8 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Thu, 7 Dec 2023 16:28:12 +0300
Subject: [PATCH 041/161] Release v0.28.0
Signed-off-by: Alex Vanin
---
CHANGELOG.md | 31 +++++++++++++++++++++++++------
VERSION | 2 +-
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2dd2c9..23878ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,13 +5,8 @@ This document outlines major changes between releases.
## [Unreleased]
### Fixed
-- `grpc` schemas in tree configuration (#62)
### Added
-- Support dump metrics descriptions (#29)
-- Support impersonate bearer token (#40, #45)
-- Tracing support (#20, #44, #60)
-- Object name resolving with tree service (#30)
- Support client side object cut (#70)
- Add `frostfs.client_cut` config param
- Add `frostfs.buffer_max_size_for_put` config param
@@ -20,11 +15,34 @@ This document outlines major changes between releases.
- Add new `logger.destination` config param (#89)
- Add support namespaces (#91)
+### Changed
+
+### Removed
+
+## [0.28.0] - Academy of Sciences - 2023-12-07
+
+### Fixed
+- `grpc` schemas in tree configuration (#62)
+- `GetSubTree` failures (#67)
+- Debian packaging (#69, #90)
+- Get latest version of tree node (#85)
+
+### Added
+- Support dump metrics descriptions (#29)
+- Support impersonate bearer token (#40, #45)
+- Tracing support (#20, #44, #60)
+- Object name resolving with tree service (#30)
+- Metrics for current endpoint status (#77)
+- Soft memory limit with `runtime.soft_memory_limit` (#72)
+- Add selection of the node of the latest version of the object (#85)
+
### Changed
- Update prometheus to v1.15.0 (#35)
- Update go version to 1.19 (#50)
- Finish rebranding (#2)
- Use gate key to form object owner (#66)
+- Move log messages to constants (#36)
+- Uploader and downloader refactor (#73)
### Removed
- Drop `tree.service` param (now endpoints from `peers` section are used) (#59)
@@ -68,4 +86,5 @@ This project is a fork of [NeoFS HTTP Gateway](https://github.com/nspcc-dev/neof
To see CHANGELOG for older versions, refer to https://github.com/nspcc-dev/neofs-http-gw/blob/master/CHANGELOG.md.
[0.27.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/72734ab4...v0.27.0
-[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.27.0...master
+[0.28.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.27.0...v0.28.0
+[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.28.0...master
diff --git a/VERSION b/VERSION
index 0a8bf80..31950da 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.27.0
+v0.28.0
From 0ef3e18ee1bf2df75de4657477e20672d8a5e105 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Tue, 7 Nov 2023 11:00:38 +0300
Subject: [PATCH 042/161] [#92] Set tree request id
Signed-off-by: Denis Kirillov
---
cmd/http-gw/app.go | 3 +++
go.mod | 2 +-
go.sum | 4 ++--
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 1ad1f20..aabb22b 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -7,6 +7,7 @@ import (
"os"
"os/signal"
"runtime/debug"
+ "strconv"
"strings"
"sync"
"syscall"
@@ -566,6 +567,8 @@ func (a *app) tracer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
span.End()
}()
+ appCtx = treepool.SetRequestID(appCtx, strconv.FormatUint(req.ID(), 10))
+
utils.SetContextToRequest(appCtx, req)
h(req)
}
diff --git a/go.mod b/go.mod
index c398358..6791920 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.20
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d
github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
diff --git a/go.sum b/go.sum
index 5d46822..d32d051 100644
--- a/go.sum
+++ b/go.sum
@@ -45,8 +45,8 @@ git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSV
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8 h1:0s2RkATjdtK/5fHjRGsIi8qMvc9IoeMZgMX5ddMwI+I=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230825064515-46a214d065f8/go.mod h1:t1akKcUH7iBrFHX8rSXScYMP17k2kYQXMbZooiL5Juw=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939 h1:jZEepi9yWmqrWgLRQcHQu4YPJaudmd7d2AEhpmM3m4U=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939/go.mod h1:t1akKcUH7iBrFHX8rSXScYMP17k2kYQXMbZooiL5Juw=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
From 627294bf704c5e3ca2e0ac0b23ff0a8e4b92a935 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Tue, 7 Nov 2023 11:07:27 +0300
Subject: [PATCH 043/161] [#92] Support configuring max tree request attempts
Signed-off-by: Denis Kirillov
---
cmd/http-gw/settings.go | 5 +++++
config/config.env | 4 ++++
config/config.yaml | 3 +++
docs/gate-configuration.md | 10 ++++++----
4 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 24e4f37..789dd2f 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -113,6 +113,9 @@ const (
cfgClientCut = "frostfs.client_cut"
// Sets max buffer size for read payload in put operations.
cfgBufferMaxSizeForPut = "frostfs.buffer_max_size_for_put"
+ // Configuration of parameters of requests to FrostFS.
+ // Sets max attempt to make successful tree request.
+ cfgTreePoolMaxAttempts = "frostfs.tree_pool_max_attempts"
// Caching.
cfgBucketsCacheLifetime = "cache.buckets.lifetime"
@@ -527,6 +530,8 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
prm.SetLogger(logger)
prmTree.SetLogger(logger)
+ prmTree.SetMaxRequestAttempts(cfg.GetInt(cfgTreePoolMaxAttempts))
+
var apiGRPCDialOpts []grpc.DialOption
var treeGRPCDialOpts []grpc.DialOption
if cfg.GetBool(cfgTracingEnabled) {
diff --git a/config/config.env b/config/config.env
index be42af9..12f1ba4 100644
--- a/config/config.env
+++ b/config/config.env
@@ -114,3 +114,7 @@ HTTP_GW_CACHE_BUCKETS_SIZE=1000
HTTP_GW_RESOLVE_BUCKET_NAMESPACE_HEADER=X-Frostfs-Namespace
# Namespaces that should be handled as default
HTTP_GW_RESOLVE_BUCKET_DEFAULT_NAMESPACES="" "root"
+
+# Max attempt to make successful tree request.
+# default value is 0 that means the number of attempts equals to number of nodes in pool.
+HTTP_GW_FROSTFS_TREE_POOL_MAX_ATTEMPTS=0
diff --git a/config/config.yaml b/config/config.yaml
index 020b0dd..7ea2748 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -112,6 +112,9 @@ frostfs:
client_cut: false
# Sets max buffer size for read payload in put operations.
buffer_max_size_for_put: 1048576
+ # Max attempt to make successful tree request.
+ # default value is 0 that means the number of attempts equals to number of nodes in pool.
+ tree_pool_max_attempts: 0
# Caching
cache:
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index fe4b50f..bf792b7 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -282,12 +282,14 @@ Contains parameters of requests to FrostFS.
frostfs:
client_cut: false
buffer_max_size_for_put: 1048576 # 1mb
+ tree_pool_max_attempts: 0
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|---------------------------|----------|---------------|---------------|----------------------------------------------------------|
-| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. |
-| `buffer_max_size_for_put` | `uint64` | yes | `1048576` | Sets max buffer size for read payload in put operations. |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|---------------------------|----------|---------------|---------------|---------------------------------------------------------------------------------------------------------------------------|
+| `client_cut` | `bool` | yes | `false` | This flag enables client side object preparing. |
+| `buffer_max_size_for_put` | `uint64` | yes | `1048576` | Sets max buffer size for read payload in put operations. |
+| `tree_pool_max_attempts` | `uint32` | no | `0` | Sets max attempt to make successful tree request. Value 0 means the number of attempts equals to number of nodes in pool. |
### `cache` section
From 5ae75eb9d8ccf16ad3d4bbb6da7047c2589536a0 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 21 Nov 2023 16:37:50 +0300
Subject: [PATCH 044/161] [#94] Update api-go to fix stable marshal of empty
structs
Newer version of api-go does not ignore non-nil empty
structures in protobuf messages, so compatibility with
previous version is preserved.
Signed-off-by: Alex Vanin
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index 6791920..422ded9 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.20
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d
diff --git a/go.sum b/go.sum
index d32d051..aec5152 100644
--- a/go.sum
+++ b/go.sum
@@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44 h1:v6JqBD/VzZx3QSxbaXnUwnnJ1KEYheU4LzLGr3IhsAE=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4 h1:wjLfZ3WCt7qNGsQv+Jl0TXnmtg0uVk/jToKPFTBc/jo=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4/go.mod h1:uY0AYmCznjZdghDnAk7THFIe1Vlg531IxUcus7ZfUJI=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M=
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=
From 2c95250f725322115021958232e1d27ef3244cd6 Mon Sep 17 00:00:00 2001
From: Marina Biryukova
Date: Wed, 27 Dec 2023 13:33:09 +0300
Subject: [PATCH 045/161] [#99] Fix possibility of panic during SIGHUP
Signed-off-by: Marina Biryukova
---
CHANGELOG.md | 1 +
internal/logs/logs.go | 1 +
metrics/service.go | 5 ++++-
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23878ca..79ef8d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ This document outlines major changes between releases.
## [Unreleased]
### Fixed
+- Fix possibility of panic during SIGHUP (#99)
### Added
- Support client side object cut (#70)
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index 9d464c3..c75e91f 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -20,6 +20,7 @@ const (
ServiceHasntStartedSinceItsDisabled = "service hasn't started since it's disabled" // Info in ../../metrics/service.go
ShuttingDownService = "shutting down service" // Info in ../../metrics/service.go
CantShutDownService = "can't shut down service" // Panic in ../../metrics/service.go
+ CantGracefullyShutDownService = "can't gracefully shut down service, force stop" // Error in ../../metrics/service.go
IgnorePartEmptyFormName = "ignore part, empty form name" // Debug in ../../uploader/upload.go
IgnorePartEmptyFilename = "ignore part, empty filename" // Debug in ../../uploader/upload.go
CloseTemporaryMultipartFormFile = "close temporary multipart/form file" // Debug in ../../uploader/upload.go
diff --git a/metrics/service.go b/metrics/service.go
index c025f06..dea5ac0 100644
--- a/metrics/service.go
+++ b/metrics/service.go
@@ -40,6 +40,9 @@ func (ms *Service) ShutDown(ctx context.Context) {
ms.log.Info(logs.ShuttingDownService, zap.String("endpoint", ms.Addr))
err := ms.Shutdown(ctx)
if err != nil {
- ms.log.Panic(logs.CantShutDownService)
+ ms.log.Error(logs.CantGracefullyShutDownService, zap.Error(err))
+ if err = ms.Close(); err != nil {
+ ms.log.Panic(logs.CantShutDownService, zap.Error(err))
+ }
}
}
From 4049255eed5339c6007dba0509a72dd048c1691a Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 24 Jan 2024 15:06:26 +0300
Subject: [PATCH 046/161] [#102] Port release v0.28.1 changelog
Signed-off-by: Alex Vanin
---
CHANGELOG.md | 11 ++++++++++-
VERSION | 2 +-
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79ef8d8..c8022ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,14 @@ This document outlines major changes between releases.
## [Unreleased]
+## [0.28.1] - 2024-01-24
+
+### Added
+- Tree pool traversal limit (#92)
+
+### Update from 0.28.0
+See new `frostfs.tree_pool_max_attempts` config parameter.
+
### Fixed
- Fix possibility of panic during SIGHUP (#99)
@@ -88,4 +96,5 @@ To see CHANGELOG for older versions, refer to https://github.com/nspcc-dev/neofs
[0.27.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/72734ab4...v0.27.0
[0.28.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.27.0...v0.28.0
-[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.28.0...master
+[0.28.1]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.28.0...v0.28.1
+[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.28.1...master
diff --git a/VERSION b/VERSION
index 31950da..244df55 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.28.0
+v0.28.1
From ce4ec032f93004e48c9c3fe9604ad4845f6af730 Mon Sep 17 00:00:00 2001
From: Evgenii Stratonikov
Date: Fri, 26 Jan 2024 11:29:32 +0300
Subject: [PATCH 047/161] [#103] .forgejo: Update dco-go to v3
Signed-off-by: Evgenii Stratonikov
---
.forgejo/workflows/dco.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml
index 3d38c4b..a9c3697 100644
--- a/.forgejo/workflows/dco.yml
+++ b/.forgejo/workflows/dco.yml
@@ -15,6 +15,6 @@ jobs:
go-version: '1.21'
- name: Run commit format checker
- uses: https://git.frostfs.info/TrueCloudLab/dco-go@v1
+ uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
with:
from: adb95642d
From c0389576492d415cf346b32b613d75adc47c92d5 Mon Sep 17 00:00:00 2001
From: Evgenii Stratonikov
Date: Fri, 26 Jan 2024 11:37:29 +0300
Subject: [PATCH 048/161] [#103] .forgejo: Check only PR commits in dco-go
Signed-off-by: Evgenii Stratonikov
---
.forgejo/workflows/dco.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml
index a9c3697..eb23ec5 100644
--- a/.forgejo/workflows/dco.yml
+++ b/.forgejo/workflows/dco.yml
@@ -17,4 +17,4 @@ jobs:
- name: Run commit format checker
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
with:
- from: adb95642d
+ from: 'origin/${{ github.event.pull_request.base.ref }}'
From 54709163613f179fb8298f31617f8a7903b2874d Mon Sep 17 00:00:00 2001
From: Pavel Pogodaev
Date: Mon, 29 Jan 2024 16:02:22 +0300
Subject: [PATCH 049/161] [#104] journald update
We want to have less useless fields in logs
Signed-off-by: Pavel Pogodaev
---
cmd/http-gw/settings.go | 2 +-
go.mod | 2 +-
go.sum | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 789dd2f..045a118 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -421,7 +421,7 @@ func newJournaldLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
c.Level = zap.NewAtomicLevelAt(lvl)
- encoder := zapcore.NewConsoleEncoder(c.EncoderConfig)
+ encoder := zapjournald.NewPartialEncoder(zapcore.NewConsoleEncoder(c.EncoderConfig), zapjournald.SyslogFields)
core := zapjournald.NewCore(zap.NewAtomicLevelAt(lvl), encoder, &journald.Journal{}, zapjournald.SyslogFields)
coreWithContext := core.With([]zapcore.Field{
diff --git a/go.mod b/go.mod
index 422ded9..9388eae 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939
- git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d
+ git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc
diff --git a/go.sum b/go.sum
index aec5152..1bd67bc 100644
--- a/go.sum
+++ b/go.sum
@@ -53,8 +53,8 @@ git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9m
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
git.frostfs.info/TrueCloudLab/tzhash v1.8.0/go.mod h1:dhY+oy274hV8wGvGL4MwwMpdL3GYvaX1a8GQZQHvlF8=
-git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d h1:Z9UuI+jxzPtwQZUMmATdTuA8/8l2jzBY1rVh/gwBDsw=
-git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20231018083019-2b6d84de9a3d/go.mod h1:rQFJJdEOV7KbbMtQYR2lNfiZk+ONRDJSbMCTWxKt8Fw=
+git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02 h1:HeY8n27VyPRQe49l/fzyVMkWEB2fsLJYKp64pwA7tz4=
+git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02/go.mod h1:rQFJJdEOV7KbbMtQYR2lNfiZk+ONRDJSbMCTWxKt8Fw=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
From 7ec9b34d33c08fdf340420c1096fc54c6e918292 Mon Sep 17 00:00:00 2001
From: Artem Tataurov
Date: Fri, 16 Feb 2024 17:50:46 +0300
Subject: [PATCH 050/161] [#105] logger: Fix logging level changing for
journald
Signed-off-by: Artem Tataurov
---
cmd/http-gw/settings.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 045a118..a38ebc5 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -423,7 +423,7 @@ func newJournaldLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
encoder := zapjournald.NewPartialEncoder(zapcore.NewConsoleEncoder(c.EncoderConfig), zapjournald.SyslogFields)
- core := zapjournald.NewCore(zap.NewAtomicLevelAt(lvl), encoder, &journald.Journal{}, zapjournald.SyslogFields)
+ core := zapjournald.NewCore(c.Level, encoder, &journald.Journal{}, zapjournald.SyslogFields)
coreWithContext := core.With([]zapcore.Field{
zapjournald.SyslogFacility(zapjournald.LogDaemon),
zapjournald.SyslogIdentifier(),
From 007d278caa2b80f3a7d780df7b95c81876916d5c Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Thu, 29 Feb 2024 12:12:44 +0300
Subject: [PATCH 051/161] [#107] Close server listener on error
Signed-off-by: Denis Kirillov
---
cmd/http-gw/server.go | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/cmd/http-gw/server.go b/cmd/http-gw/server.go
index c5852d8..f8a20d9 100644
--- a/cmd/http-gw/server.go
+++ b/cmd/http-gw/server.go
@@ -68,7 +68,8 @@ func newServer(ctx context.Context, serverInfo ServerInfo) (*server, error) {
if serverInfo.TLS.Enabled {
if err = tlsProvider.UpdateCert(serverInfo.TLS.CertFile, serverInfo.TLS.KeyFile); err != nil {
- return nil, fmt.Errorf("failed to update cert: %w", err)
+ lnErr := ln.Close()
+ return nil, fmt.Errorf("failed to update cert (listener close: %v): %w", lnErr, err)
}
ln = tls.NewListener(ln, &tls.Config{
From 88e32ddd7fe062822b04d1c07d9ed6cac6ebbe4b Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Thu, 29 Feb 2024 12:17:22 +0300
Subject: [PATCH 052/161] [#107] Add return on error in tokenizer middleware
Signed-off-by: Denis Kirillov
---
cmd/http-gw/app.go | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index aabb22b..4532520 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -549,8 +549,9 @@ func (a *app) tokenizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
appCtx, err := tokens.StoreBearerTokenAppCtx(a.ctx, req)
if err != nil {
- a.log.Error(logs.CouldNotFetchAndStoreBearerToken, zap.Error(err))
+ a.log.Error(logs.CouldNotFetchAndStoreBearerToken, zap.Uint64("id", req.ID()), zap.Error(err))
response.Error(req, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
+ return
}
utils.SetContextToRequest(appCtx, req)
h(req)
From 5ded105c09a7caff90d1e73f47d0ff314fd3f5d3 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Thu, 29 Feb 2024 12:50:56 +0300
Subject: [PATCH 053/161] [#107] Check query unescape errors
Signed-off-by: Denis Kirillov
---
internal/handler/download.go | 12 ++++++++++--
internal/handler/handler.go | 25 +++++++++++++++++++------
internal/logs/logs.go | 3 +--
3 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 06a247a..a7aee64 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -82,8 +82,16 @@ func (h *Handler) addObjectToZip(zw *zip.Writer, obj *object.Object) (io.Writer,
// DownloadZipped handles zip by prefix requests.
func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
scid, _ := c.UserValue("cid").(string)
- prefix, _ := url.QueryUnescape(c.UserValue("prefix").(string))
- log := h.log.With(zap.String("cid", scid), zap.String("prefix", prefix))
+ prefix, _ := c.UserValue("prefix").(string)
+
+ prefix, err := url.QueryUnescape(prefix)
+ if err != nil {
+ h.log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("prefix", prefix), zap.Uint64("id", c.ID()), zap.Error(err))
+ response.Error(c, "could not unescape prefix: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ log := h.log.With(zap.String("cid", scid), zap.String("prefix", prefix), zap.Uint64("id", c.ID()))
ctx := utils.GetContextFromRequest(c)
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 757b5be..f88dff1 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -131,12 +131,25 @@ func (h *Handler) byObjectName(req *fasthttp.RequestCtx, f func(context.Context,
// byAttribute is a wrapper similar to byAddress.
func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
- var (
- scid, _ = c.UserValue("cid").(string)
- key, _ = url.QueryUnescape(c.UserValue("attr_key").(string))
- val, _ = url.QueryUnescape(c.UserValue("attr_val").(string))
- log = h.log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val))
- )
+ scid, _ := c.UserValue("cid").(string)
+ key, _ := c.UserValue("attr_key").(string)
+ val, _ := c.UserValue("attr_val").(string)
+
+ key, err := url.QueryUnescape(key)
+ if err != nil {
+ h.log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_key", key), zap.Uint64("id", c.ID()), zap.Error(err))
+ response.Error(c, "could not unescape attr_key: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ val, err = url.QueryUnescape(val)
+ if err != nil {
+ h.log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_val", val), zap.Uint64("id", c.ID()), zap.Error(err))
+ response.Error(c, "could not unescape attr_val: "+err.Error(), fasthttp.StatusBadRequest)
+ return
+ }
+
+ log := h.log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val))
ctx := utils.GetContextFromRequest(c)
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index c75e91f..84954c3 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -4,7 +4,6 @@ const (
CouldntParseCreationDate = "couldn't parse creation date" // Info in ../../downloader/*
CouldNotDetectContentTypeFromPayload = "could not detect Content-Type from payload" // Error in ../../downloader/download.go
CouldNotReceiveObject = "could not receive object" // Error in ../../downloader/download.go
- WrongContainerID = "wrong container id" // Error in ../../downloader/download.go and uploader/upload.go
WrongObjectID = "wrong object id" // Error in ../../downloader/download.go
GetLatestObjectVersion = "get latest object version" // Error in ../../downloader/download.go
ObjectWasDeleted = "object was deleted" // Error in ../../downloader/download.go
@@ -73,5 +72,5 @@ const (
InvalidCacheEntryType = "invalid cache entry type" // Warn in ../cache/buckets.go
InvalidLifetimeUsingDefaultValue = "invalid lifetime, using default value (in seconds)" // Error in ../../cmd/http-gw/settings.go
InvalidCacheSizeUsingDefaultValue = "invalid cache size, using default value" // Error in ../../cmd/http-gw/settings.go
-
+ FailedToUnescapeQuery = "failed to unescape query"
)
From c6383fc1355e8462d6dc1054f2a0cf7d5e29225c Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Thu, 29 Feb 2024 12:52:52 +0300
Subject: [PATCH 054/161] [#107] Update CHANGELOG.md
Signed-off-by: Denis Kirillov
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8022ef..f763e5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ See new `frostfs.tree_pool_max_attempts` config parameter.
### Fixed
- Fix possibility of panic during SIGHUP (#99)
+- Handle query unescape and invalid bearer token errors (#107)
### Added
- Support client side object cut (#70)
From 6695ebe5a0dd37900c786d9da28a13cf237a7cc2 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 27 Mar 2024 19:20:21 +0300
Subject: [PATCH 055/161] [#110] Test HTTP/2 requests
Signed-off-by: Alex Vanin
---
cmd/http-gw/server_test.go | 119 +++++++++++++++++++++++++++++++++++++
go.mod | 2 +-
2 files changed, 120 insertions(+), 1 deletion(-)
create mode 100644 cmd/http-gw/server_test.go
diff --git a/cmd/http-gw/server_test.go b/cmd/http-gw/server_test.go
new file mode 100644
index 0000000..a937366
--- /dev/null
+++ b/cmd/http-gw/server_test.go
@@ -0,0 +1,119 @@
+package main
+
+import (
+ "context"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/tls"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/pem"
+ "fmt"
+ "math/big"
+ "net"
+ "net/http"
+ "os"
+ "path"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "golang.org/x/net/http2"
+)
+
+const (
+ expHeaderKey = "Foo"
+ expHeaderValue = "Bar"
+)
+
+func TestHTTP2TLS(t *testing.T) {
+ ctx := context.Background()
+ certPath, keyPath := prepareTestCerts(t)
+
+ srv := &http.Server{
+ Handler: http.HandlerFunc(testHandler),
+ }
+
+ tlsListener, err := newServer(ctx, ServerInfo{
+ Address: ":0",
+ TLS: ServerTLSInfo{
+ Enabled: true,
+ CertFile: certPath,
+ KeyFile: keyPath,
+ },
+ })
+ require.NoError(t, err)
+ port := tlsListener.Listener().Addr().(*net.TCPAddr).Port
+ addr := fmt.Sprintf("https://localhost:%d", port)
+
+ go func() {
+ _ = srv.Serve(tlsListener.Listener())
+ }()
+
+ // Server is running, now send HTTP/2 request
+
+ tlsClientConfig := &tls.Config{
+ InsecureSkipVerify: true,
+ }
+
+ cliHTTP1 := http.Client{Transport: &http.Transport{TLSClientConfig: tlsClientConfig}}
+ cliHTTP2 := http.Client{Transport: &http2.Transport{TLSClientConfig: tlsClientConfig}}
+
+ req, err := http.NewRequest("GET", addr, nil)
+ require.NoError(t, err)
+ req.Header[expHeaderKey] = []string{expHeaderValue}
+
+ resp, err := cliHTTP1.Do(req)
+ require.NoError(t, err)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ resp, err = cliHTTP2.Do(req)
+ require.NoError(t, err)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func testHandler(resp http.ResponseWriter, req *http.Request) {
+ hdr, ok := req.Header[expHeaderKey]
+ if !ok || len(hdr) != 1 || hdr[0] != expHeaderValue {
+ resp.WriteHeader(http.StatusBadRequest)
+ } else {
+ resp.WriteHeader(http.StatusOK)
+ }
+}
+
+func prepareTestCerts(t *testing.T) (certPath, keyPath string) {
+ privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+ require.NoError(t, err)
+
+ template := x509.Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{CommonName: "localhost"},
+ NotBefore: time.Now(),
+ NotAfter: time.Now().Add(time.Hour * 24 * 365),
+ KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
+ BasicConstraintsValid: true,
+ }
+
+ derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
+ require.NoError(t, err)
+
+ dir := t.TempDir()
+ certPath = path.Join(dir, "cert.pem")
+ keyPath = path.Join(dir, "key.pem")
+
+ certFile, err := os.Create(certPath)
+ require.NoError(t, err)
+ defer certFile.Close()
+
+ keyFile, err := os.Create(keyPath)
+ require.NoError(t, err)
+ defer keyFile.Close()
+
+ err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+ require.NoError(t, err)
+
+ err = pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
+ require.NoError(t, err)
+
+ return certPath, keyPath
+}
diff --git a/go.mod b/go.mod
index 9388eae..dd2896c 100644
--- a/go.mod
+++ b/go.mod
@@ -22,6 +22,7 @@ require (
go.opentelemetry.io/otel/trace v1.16.0
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
+ golang.org/x/net v0.10.0
google.golang.org/grpc v1.55.0
)
@@ -103,7 +104,6 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
- golang.org/x/net v0.10.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/term v0.8.0 // indirect
From f39b3aa93a3e590f8e4fba415180e3e99f02843e Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 27 Mar 2024 19:20:49 +0300
Subject: [PATCH 056/161] [#110] Add "h2" as next proto to allow HTTP/2
requests in http.Serve
Signed-off-by: Alex Vanin
---
cmd/http-gw/server.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/cmd/http-gw/server.go b/cmd/http-gw/server.go
index f8a20d9..694e9ee 100644
--- a/cmd/http-gw/server.go
+++ b/cmd/http-gw/server.go
@@ -74,6 +74,7 @@ func newServer(ctx context.Context, serverInfo ServerInfo) (*server, error) {
ln = tls.NewListener(ln, &tls.Config{
GetCertificate: tlsProvider.GetCertificate,
+ NextProtos: []string{"h2"}, // required to enable HTTP/2 requests in `http.Serve`
})
}
From a95dc6c8c7d690259ea47a723e8a93db0eb193b6 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Wed, 27 Mar 2024 19:26:37 +0300
Subject: [PATCH 057/161] [#110] Update CHANGELOG
Signed-off-by: Alex Vanin
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f763e5a..6da67f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ See new `frostfs.tree_pool_max_attempts` config parameter.
### Fixed
- Fix possibility of panic during SIGHUP (#99)
- Handle query unescape and invalid bearer token errors (#107)
+- Fix HTTP/2 requests (#110)
### Added
- Support client side object cut (#70)
From 11965deb4188e78bdbe92b5d4b17e41620f2a86d Mon Sep 17 00:00:00 2001
From: Pavel Pogodaev
Date: Tue, 26 Mar 2024 14:34:20 +0300
Subject: [PATCH 058/161] [#100] server auto re-binding
Signed-off-by: Pavel Pogodaev
---
CHANGELOG.md | 1 +
cmd/http-gw/app.go | 132 +++++++++++++++++++++++++++++++------
cmd/http-gw/settings.go | 21 +++++-
config/config.env | 3 +
config/config.yaml | 1 +
docs/gate-configuration.md | 2 +
internal/logs/logs.go | 4 ++
7 files changed, 142 insertions(+), 22 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6da67f5..105ac41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ This document outlines major changes between releases.
### Added
- Tree pool traversal limit (#92)
+- Add new `reconnect_interval` config param (#100)
### Update from 0.28.0
See new `frostfs.tree_pool_max_attempts` config parameter.
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 4532520..2a20d86 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -2,6 +2,7 @@ package main
import (
"context"
+ "errors"
"fmt"
"net/http"
"os"
@@ -57,7 +58,10 @@ type (
metrics *gateMetrics
services []*metrics.Service
settings *appSettings
- servers []Server
+
+ servers []Server
+ unbindServers []ServerInfo
+ mu sync.RWMutex
}
// App is an interface for the main gateway function.
@@ -78,6 +82,8 @@ type (
// appSettings stores reloading parameters, so it has to provide getters and setters which use RWMutex.
appSettings struct {
+ reconnectInterval time.Duration
+
mu sync.RWMutex
defaultTimestamp bool
zipCompression bool
@@ -199,8 +205,9 @@ func (s *appSettings) setBufferMaxSizeForPut(val uint64) {
}
func (a *app) initAppSettings() {
- a.settings = &appSettings{}
-
+ a.settings = &appSettings{
+ reconnectInterval: fetchReconnectInterval(a.cfg),
+ }
a.updateSettings()
}
@@ -399,16 +406,22 @@ func (a *app) Serve() {
a.startServices()
a.initServers(a.ctx)
- for i := range a.servers {
+ servs := a.getServers()
+
+ for i := range servs {
go func(i int) {
- a.log.Info(logs.StartingServer, zap.String("address", a.servers[i].Address()))
- if err := a.webServer.Serve(a.servers[i].Listener()); err != nil && err != http.ErrServerClosed {
- a.metrics.MarkUnhealthy(a.servers[i].Address())
+ a.log.Info(logs.StartingServer, zap.String("address", servs[i].Address()))
+ if err := a.webServer.Serve(servs[i].Listener()); err != nil && err != http.ErrServerClosed {
+ a.metrics.MarkUnhealthy(servs[i].Address())
a.log.Fatal(logs.ListenAndServe, zap.Error(err))
}
}(i)
}
+ if len(a.unbindServers) != 0 {
+ a.scheduleReconnect(a.ctx, a.webServer)
+ }
+
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGHUP)
@@ -598,7 +611,7 @@ func (a *app) AppParams() *utils.AppParams {
}
func (a *app) initServers(ctx context.Context) {
- serversInfo := fetchServers(a.cfg)
+ serversInfo := fetchServers(a.cfg, a.log)
a.servers = make([]Server, 0, len(serversInfo))
for _, serverInfo := range serversInfo {
@@ -608,6 +621,7 @@ func (a *app) initServers(ctx context.Context) {
}
srv, err := newServer(ctx, serverInfo)
if err != nil {
+ a.unbindServers = append(a.unbindServers, serverInfo)
a.metrics.MarkUnhealthy(serverInfo.Address)
a.log.Warn(logs.FailedToAddServer, append(fields, zap.Error(err))...)
continue
@@ -624,21 +638,24 @@ func (a *app) initServers(ctx context.Context) {
}
func (a *app) updateServers() error {
- serversInfo := fetchServers(a.cfg)
+ serversInfo := fetchServers(a.cfg, a.log)
+
+ a.mu.Lock()
+ defer a.mu.Unlock()
var found bool
for _, serverInfo := range serversInfo {
- index := a.serverIndex(serverInfo.Address)
- if index == -1 {
- continue
- }
-
- if serverInfo.TLS.Enabled {
- if err := a.servers[index].UpdateCert(serverInfo.TLS.CertFile, serverInfo.TLS.KeyFile); err != nil {
- return fmt.Errorf("failed to update tls certs: %w", err)
+ ser := a.getServer(serverInfo.Address)
+ if ser != nil {
+ if serverInfo.TLS.Enabled {
+ if err := ser.UpdateCert(serverInfo.TLS.CertFile, serverInfo.TLS.KeyFile); err != nil {
+ return fmt.Errorf("failed to update tls certs: %w", err)
+ }
+ found = true
}
+ } else if unbind := a.updateUnbindServerInfo(serverInfo); unbind {
+ found = true
}
- found = true
}
if !found {
@@ -648,13 +665,29 @@ func (a *app) updateServers() error {
return nil
}
-func (a *app) serverIndex(address string) int {
+func (a *app) getServers() []Server {
+ a.mu.RLock()
+ defer a.mu.RUnlock()
+ return a.servers
+}
+
+func (a *app) getServer(address string) Server {
for i := range a.servers {
if a.servers[i].Address() == address {
- return i
+ return a.servers[i]
}
}
- return -1
+ return nil
+}
+
+func (a *app) updateUnbindServerInfo(info ServerInfo) bool {
+ for i := range a.unbindServers {
+ if a.unbindServers[i].Address == info.Address {
+ a.unbindServers[i] = info
+ return true
+ }
+ }
+ return false
}
func (a *app) initTracing(ctx context.Context) {
@@ -727,3 +760,60 @@ func (s *appSettings) setDefaultNamespaces(namespaces []string) {
s.defaultNamespaces = namespaces
s.mu.Unlock()
}
+
+func (a *app) scheduleReconnect(ctx context.Context, srv *fasthttp.Server) {
+ go func() {
+ t := time.NewTicker(a.settings.reconnectInterval)
+ defer t.Stop()
+ for {
+ select {
+ case <-t.C:
+ if a.tryReconnect(ctx, srv) {
+ return
+ }
+ t.Reset(a.settings.reconnectInterval)
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+}
+
+func (a *app) tryReconnect(ctx context.Context, sr *fasthttp.Server) bool {
+ a.mu.Lock()
+ defer a.mu.Unlock()
+
+ a.log.Info(logs.ServerReconnecting)
+ var failedServers []ServerInfo
+
+ for _, serverInfo := range a.unbindServers {
+ fields := []zap.Field{
+ zap.String("address", serverInfo.Address), zap.Bool("tls enabled", serverInfo.TLS.Enabled),
+ zap.String("tls cert", serverInfo.TLS.CertFile), zap.String("tls key", serverInfo.TLS.KeyFile),
+ }
+
+ srv, err := newServer(ctx, serverInfo)
+ if err != nil {
+ a.log.Warn(logs.ServerReconnectFailed, zap.Error(err))
+ failedServers = append(failedServers, serverInfo)
+ a.metrics.MarkUnhealthy(serverInfo.Address)
+ continue
+ }
+
+ go func() {
+ a.log.Info(logs.StartingServer, zap.String("address", srv.Address()))
+ a.metrics.MarkHealthy(serverInfo.Address)
+ if err = sr.Serve(srv.Listener()); err != nil && !errors.Is(err, http.ErrServerClosed) {
+ a.log.Warn(logs.ListenAndServe, zap.Error(err))
+ a.metrics.MarkUnhealthy(serverInfo.Address)
+ }
+ }()
+
+ a.servers = append(a.servers, srv)
+ a.log.Info(logs.ServerReconnectedSuccessfully, fields...)
+ }
+
+ a.unbindServers = failedServers
+
+ return len(a.unbindServers) == 0
+}
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index a38ebc5..0d97dcb 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -51,11 +51,15 @@ const (
defaultNamespaceHeader = "X-Frostfs-Namespace"
+ defaultReconnectInterval = time.Minute
+
cfgServer = "server"
cfgTLSEnabled = "tls.enabled"
cfgTLSCertFile = "tls.cert_file"
cfgTLSKeyFile = "tls.key_file"
+ cfgReconnectInterval = "reconnect_interval"
+
// Web.
cfgWebReadBufferSize = "web.read_buffer_size"
cfgWebWriteBufferSize = "web.write_buffer_size"
@@ -454,8 +458,18 @@ func getLogLevel(v *viper.Viper) (zapcore.Level, error) {
return lvl, nil
}
-func fetchServers(v *viper.Viper) []ServerInfo {
+func fetchReconnectInterval(cfg *viper.Viper) time.Duration {
+ reconnect := cfg.GetDuration(cfgReconnectInterval)
+ if reconnect <= 0 {
+ reconnect = defaultReconnectInterval
+ }
+
+ return reconnect
+}
+
+func fetchServers(v *viper.Viper, log *zap.Logger) []ServerInfo {
var servers []ServerInfo
+ seen := make(map[string]struct{})
for i := 0; ; i++ {
key := cfgServer + "." + strconv.Itoa(i) + "."
@@ -470,6 +484,11 @@ func fetchServers(v *viper.Viper) []ServerInfo {
break
}
+ if _, ok := seen[serverInfo.Address]; ok {
+ log.Warn(logs.WarnDuplicateAddress, zap.String("address", serverInfo.Address))
+ continue
+ }
+ seen[serverInfo.Address] = struct{}{}
servers = append(servers, serverInfo)
}
diff --git a/config/config.env b/config/config.env
index 12f1ba4..05b83b3 100644
--- a/config/config.env
+++ b/config/config.env
@@ -26,6 +26,9 @@ HTTP_GW_SERVER_1_TLS_ENABLED=true
HTTP_GW_SERVER_1_TLS_CERT_FILE=/path/to/tls/cert
HTTP_GW_SERVER_1_TLS_KEY_FILE=/path/to/tls/key
+# How often to reconnect to the servers
+HTTP_GW_RECONNECT_INTERVAL: 1m
+
# Nodes configuration.
# This configuration make the gateway use the first node (grpc://s01.frostfs.devenv:8080)
# while it's healthy. Otherwise, the gateway use the second node (grpc://s01.frostfs.devenv:8080)
diff --git a/config/config.yaml b/config/config.yaml
index 7ea2748..7f8077b 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -55,6 +55,7 @@ peers:
priority: 2
weight: 9
+reconnect_interval: 1m
web:
# Per-connection buffer size for requests' reading.
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index bf792b7..8e3daad 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -72,6 +72,7 @@ stream_timeout: 10s
request_timeout: 5s
rebalance_timer: 30s
pool_error_threshold: 100
+reconnect_interval: 1m
```
| Parameter | Type | SIGHUP reload | Default value | Description |
@@ -83,6 +84,7 @@ pool_error_threshold: 100
| `request_timeout` | `duration` | | `15s` | Timeout to check node health during rebalance. |
| `rebalance_timer` | `duration` | | `60s` | Interval to check node health. |
| `pool_error_threshold` | `uint32` | | `100` | The number of errors on connection after which node is considered as unhealthy. |
+| `reconnect_interval` | `duration` | no | `1m` | Listeners reconnection interval. |
# `wallet` section
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index 84954c3..0ab5dbf 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -73,4 +73,8 @@ const (
InvalidLifetimeUsingDefaultValue = "invalid lifetime, using default value (in seconds)" // Error in ../../cmd/http-gw/settings.go
InvalidCacheSizeUsingDefaultValue = "invalid cache size, using default value" // Error in ../../cmd/http-gw/settings.go
FailedToUnescapeQuery = "failed to unescape query"
+ ServerReconnecting = "reconnecting server..."
+ ServerReconnectedSuccessfully = "server reconnected successfully"
+ ServerReconnectFailed = "failed to reconnect server"
+ WarnDuplicateAddress = "duplicate address"
)
From 16d6e6c34e3676330118d89e3e108348aea76d64 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Fri, 3 May 2024 15:29:51 +0300
Subject: [PATCH 059/161] [#112] tokens: Extend test coverage
Signed-off-by: Roman Loginov
---
tokens/bearer-token_test.go | 193 +++++++++++++++++++++++++++++++-----
1 file changed, 168 insertions(+), 25 deletions(-)
diff --git a/tokens/bearer-token_test.go b/tokens/bearer-token_test.go
index cc54e74..6fb3bf4 100644
--- a/tokens/bearer-token_test.go
+++ b/tokens/bearer-token_test.go
@@ -23,19 +23,29 @@ func makeTestCookie(value []byte) *fasthttp.RequestHeader {
func makeTestHeader(value []byte) *fasthttp.RequestHeader {
header := new(fasthttp.RequestHeader)
if value != nil {
- header.Set(fasthttp.HeaderAuthorization, bearerTokenHdr+" "+string(value))
+ header.Set(fasthttp.HeaderAuthorization, string(value))
}
return header
}
-func Test_fromCookie(t *testing.T) {
+func makeBearer(value string) string {
+ return bearerTokenHdr + " " + value
+}
+
+func TestBearerTokenFromCookie(t *testing.T) {
cases := []struct {
name string
actual []byte
expect []byte
}{
- {name: "empty"},
- {name: "normal", actual: []byte("TOKEN"), expect: []byte("TOKEN")},
+ {
+ name: "empty",
+ },
+ {
+ name: "normal",
+ actual: []byte("TOKEN"),
+ expect: []byte("TOKEN"),
+ },
}
for _, tt := range cases {
@@ -45,14 +55,31 @@ func Test_fromCookie(t *testing.T) {
}
}
-func Test_fromHeader(t *testing.T) {
+func TestBearerTokenFromHeader(t *testing.T) {
+ validToken := "token"
+ tokenWithoutPrefix := "invalid-token"
+
cases := []struct {
name string
actual []byte
expect []byte
}{
- {name: "empty"},
- {name: "normal", actual: []byte("TOKEN"), expect: []byte("TOKEN")},
+ {
+ name: "empty",
+ },
+ {
+ name: "token without the bearer prefix",
+ actual: []byte(tokenWithoutPrefix),
+ },
+ {
+ name: "token without payload",
+ actual: []byte(makeBearer("")),
+ },
+ {
+ name: "normal",
+ actual: []byte(makeBearer(validToken)),
+ expect: []byte(validToken),
+ },
}
for _, tt := range cases {
@@ -62,7 +89,7 @@ func Test_fromHeader(t *testing.T) {
}
}
-func Test_fetchBearerToken(t *testing.T) {
+func TestFetchBearerToken(t *testing.T) {
key, err := keys.NewPrivateKey()
require.NoError(t, err)
var uid user.ID
@@ -75,43 +102,77 @@ func Test_fetchBearerToken(t *testing.T) {
require.NotEmpty(t, t64)
cases := []struct {
- name string
-
+ name string
cookie string
header string
-
error string
+ nilCtx bool
expect *bearer.Token
}{
- {name: "empty"},
-
- {name: "bad base64 header", header: "WRONG BASE64", error: "can't base64-decode bearer token"},
- {name: "bad base64 cookie", cookie: "WRONG BASE64", error: "can't base64-decode bearer token"},
-
- {name: "header token unmarshal error", header: "dGVzdAo=", error: "can't unmarshal bearer token"},
- {name: "cookie token unmarshal error", cookie: "dGVzdAo=", error: "can't unmarshal bearer token"},
-
+ {
+ name: "empty",
+ },
+ {
+ name: "nil context",
+ nilCtx: true,
+ },
+ {
+ name: "bad base64 header",
+ header: "WRONG BASE64",
+ error: "can't base64-decode bearer token",
+ },
+ {
+ name: "bad base64 cookie",
+ cookie: "WRONG BASE64",
+ error: "can't base64-decode bearer token",
+ },
+ {
+ name: "header token unmarshal error",
+ header: "dGVzdAo=",
+ error: "can't unmarshal bearer token",
+ },
+ {
+ name: "cookie token unmarshal error",
+ cookie: "dGVzdAo=",
+ error: "can't unmarshal bearer token",
+ },
{
name: "bad header and cookie",
header: "WRONG BASE64",
cookie: "dGVzdAo=",
error: "can't unmarshal bearer token",
},
-
{
name: "bad header, but good cookie",
header: "dGVzdAo=",
cookie: t64,
expect: tkn,
},
-
- {name: "ok for header", header: t64, expect: tkn},
- {name: "ok for cookie", cookie: t64, expect: tkn},
+ {
+ name: "bad cookie, but good header",
+ header: t64,
+ cookie: "dGVzdAo=",
+ expect: tkn,
+ },
+ {
+ name: "ok for header",
+ header: t64,
+ expect: tkn,
+ },
+ {
+ name: "ok for cookie",
+ cookie: t64,
+ expect: tkn,
+ },
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
- ctx := makeTestRequest(tt.cookie, tt.header)
+ var ctx *fasthttp.RequestCtx
+ if !tt.nilCtx {
+ ctx = makeTestRequest(tt.cookie, tt.header)
+ }
+
actual, err := fetchBearerToken(ctx)
if tt.error == "" {
@@ -139,7 +200,7 @@ func makeTestRequest(cookie, header string) *fasthttp.RequestCtx {
return ctx
}
-func Test_checkAndPropagateBearerToken(t *testing.T) {
+func TestCheckAndPropagateBearerToken(t *testing.T) {
key, err := keys.NewPrivateKey()
require.NoError(t, err)
var uid user.ID
@@ -162,3 +223,85 @@ func Test_checkAndPropagateBearerToken(t *testing.T) {
require.NoError(t, err)
require.Equal(t, tkn, actual)
}
+
+func TestLoadBearerToken(t *testing.T) {
+ ctx := context.Background()
+ token := new(bearer.Token)
+
+ cases := []struct {
+ name string
+ appCtx context.Context
+ error string
+ }{
+ {
+ name: "token is missing in the context",
+ appCtx: ctx,
+ error: "found empty bearer token",
+ },
+ {
+ name: "normal",
+ appCtx: context.WithValue(ctx, bearerTokenKey, token),
+ },
+ }
+
+ for _, tt := range cases {
+ t.Run(tt.name, func(t *testing.T) {
+ tkn, err := LoadBearerToken(tt.appCtx)
+
+ if tt.error == "" {
+ require.NoError(t, err)
+ require.Equal(t, token, tkn)
+
+ return
+ }
+
+ require.Contains(t, err.Error(), tt.error)
+ })
+ }
+}
+
+func TestStoreBearerTokenAppCtx(t *testing.T) {
+ key, err := keys.NewPrivateKey()
+ require.NoError(t, err)
+ var uid user.ID
+ user.IDFromKey(&uid, key.PrivateKey.PublicKey)
+
+ tkn := new(bearer.Token)
+ tkn.ForUser(uid)
+
+ t64 := base64.StdEncoding.EncodeToString(tkn.Marshal())
+ require.NotEmpty(t, t64)
+
+ cases := []struct {
+ name string
+ req *fasthttp.RequestCtx
+ error string
+ }{
+ {
+ name: "invalid token",
+ req: makeTestRequest("dGVzdAo=", ""),
+ error: "can't unmarshal bearer token",
+ },
+ {
+ name: "normal",
+ req: makeTestRequest(t64, ""),
+ },
+ }
+
+ for _, tt := range cases {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx, err := StoreBearerTokenAppCtx(context.Background(), tt.req)
+
+ if tt.error == "" {
+ require.NoError(t, err)
+ actualToken, ok := ctx.Value(bearerTokenKey).(*bearer.Token)
+ require.True(t, ok)
+ require.Equal(t, tkn, actualToken)
+
+ return
+ }
+
+ require.Contains(t, err.Error(), tt.error)
+ })
+ }
+}
From c851c0529c134021189ba7437dccd7a7fea22e65 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Mon, 6 May 2024 20:34:49 +0300
Subject: [PATCH 060/161] [#112] Add integration test with bearer token
Signed-off-by: Roman Loginov
---
cmd/http-gw/integration_test.go | 94 +++++++++++++++++++++++++++++++--
1 file changed, 89 insertions(+), 5 deletions(-)
diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go
index f76c3ce..5d21094 100644
--- a/cmd/http-gw/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -6,25 +6,30 @@ import (
"archive/zip"
"bytes"
"context"
+ "encoding/base64"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
+ "os"
"sort"
"testing"
"time"
containerv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
+ "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
@@ -47,11 +52,17 @@ func TestIntegration(t *testing.T) {
rootCtx := context.Background()
aioImage := "truecloudlab/frostfs-aio:"
versions := []string{
- "1.2.7", // frostfs-storage v0.36.0 RC
+ "1.2.7",
+ "1.3.0",
}
key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb")
require.NoError(t, err)
+ file, err := os.CreateTemp("", "wallet")
+ require.NoError(t, err)
+ defer os.Remove(file.Name())
+ makeTempWallet(t, key, file.Name())
+
var ownerID user.ID
user.IDFromKey(&ownerID, key.PrivateKey.PublicKey)
@@ -59,12 +70,16 @@ func TestIntegration(t *testing.T) {
ctx, cancel2 := context.WithCancel(rootCtx)
aioContainer := createDockerContainer(ctx, t, aioImage+version)
- server, cancel := runServer()
+ server, cancel := runServer(file.Name())
clientPool := getPool(ctx, t, key)
CID, err := createContainer(ctx, t, clientPool, ownerID, version)
require.NoError(t, err, version)
+ token := makeBearerToken(t, key, ownerID, version)
+
t.Run("simple put "+version, func(t *testing.T) { simplePut(ctx, t, clientPool, CID, version) })
+ t.Run("put with bearer token in header"+version, func(t *testing.T) { putWithBearerTokenInHeader(ctx, t, clientPool, CID, token) })
+ t.Run("put with bearer token in cookie"+version, func(t *testing.T) { putWithBearerTokenInCookie(ctx, t, clientPool, CID, token) })
t.Run("put with duplicate keys "+version, func(t *testing.T) { putWithDuplicateKeys(t, CID) })
t.Run("simple get "+version, func(t *testing.T) { simpleGet(ctx, t, clientPool, ownerID, CID, version) })
t.Run("get by attribute "+version, func(t *testing.T) { getByAttr(ctx, t, clientPool, ownerID, CID, version) })
@@ -79,10 +94,13 @@ func TestIntegration(t *testing.T) {
}
}
-func runServer() (App, context.CancelFunc) {
+func runServer(pathToWallet string) (App, context.CancelFunc) {
cancelCtx, cancel := context.WithCancel(context.Background())
v := getDefaultConfig()
+ v.Set(cfgWalletPath, pathToWallet)
+ v.Set(cfgWalletPassphrase, "")
+
l, lvl := newStdoutLogger(zapcore.DebugLevel)
application := newApp(cancelCtx, WithConfig(v), WithLogger(l, lvl))
go application.Serve()
@@ -98,7 +116,38 @@ func simplePut(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID, vers
makePutRequestAndCheck(ctx, t, p, CID, url)
}
+func putWithBearerTokenInHeader(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID, token string) {
+ url := testHost + "/upload/" + CID.String()
+
+ request, content, attributes := makePutRequest(t, url)
+ request.Header.Set("Authorization", "Bearer "+token)
+ resp, err := http.DefaultClient.Do(request)
+ require.NoError(t, err)
+
+ checkPutResponse(ctx, t, p, CID, resp, content, attributes)
+}
+
+func putWithBearerTokenInCookie(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID, token string) {
+ url := testHost + "/upload/" + CID.String()
+
+ request, content, attributes := makePutRequest(t, url)
+ request.AddCookie(&http.Cookie{Name: "Bearer", Value: token})
+ resp, err := http.DefaultClient.Do(request)
+ require.NoError(t, err)
+
+ checkPutResponse(ctx, t, p, CID, resp, content, attributes)
+}
+
func makePutRequestAndCheck(ctx context.Context, t *testing.T, p *pool.Pool, cnrID cid.ID, url string) {
+ request, content, attributes := makePutRequest(t, url)
+
+ resp, err := http.DefaultClient.Do(request)
+ require.NoError(t, err)
+
+ checkPutResponse(ctx, t, p, cnrID, resp, content, attributes)
+}
+
+func makePutRequest(t *testing.T, url string) (*http.Request, string, map[string]string) {
content := "content of file"
keyAttr, valAttr := "User-Attribute", "user value"
attributes := map[string]string{
@@ -120,9 +169,10 @@ func makePutRequestAndCheck(ctx context.Context, t *testing.T, p *pool.Pool, cnr
request.Header.Set("Content-Type", w.FormDataContentType())
request.Header.Set("X-Attribute-"+keyAttr, valAttr)
- resp, err := http.DefaultClient.Do(request)
- require.NoError(t, err)
+ return request, content, attributes
+}
+func checkPutResponse(ctx context.Context, t *testing.T, p *pool.Pool, cnrID cid.ID, resp *http.Response, content string, attributes map[string]string) {
defer func() {
err := resp.Body.Close()
require.NoError(t, err)
@@ -476,3 +526,37 @@ func putObject(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID
return id
}
+
+func makeBearerToken(t *testing.T, key *keys.PrivateKey, ownerID user.ID, version string) string {
+ tkn := new(bearer.Token)
+ tkn.ForUser(ownerID)
+ tkn.SetExp(10000)
+
+ if version == "1.2.7" {
+ tkn.SetEACLTable(*eacl.NewTable())
+ } else {
+ tkn.SetImpersonate(true)
+ }
+
+ err := tkn.Sign(key.PrivateKey)
+ require.NoError(t, err)
+
+ t64 := base64.StdEncoding.EncodeToString(tkn.Marshal())
+ require.NotEmpty(t, t64)
+
+ return t64
+}
+
+func makeTempWallet(t *testing.T, key *keys.PrivateKey, path string) {
+ w, err := wallet.NewWallet(path)
+ require.NoError(t, err)
+
+ acc := wallet.NewAccountFromPrivateKey(key)
+ err = acc.Encrypt("", w.Scrypt)
+ require.NoError(t, err)
+
+ w.AddAccount(acc)
+
+ err = w.Save()
+ require.NoError(t, err)
+}
From 5b7b872dcd507f78e2b4ac317973d2ddf9e031f2 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Tue, 7 May 2024 17:58:41 +0300
Subject: [PATCH 061/161] [#112] Update net to v0.23.0
Signed-off-by: Roman Loginov
---
go.mod | 10 +++++-----
go.sum | 20 ++++++++++----------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/go.mod b/go.mod
index dd2896c..d862839 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,7 @@ require (
go.opentelemetry.io/otel/trace v1.16.0
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
- golang.org/x/net v0.10.0
+ golang.org/x/net v0.23.0
google.golang.org/grpc v1.55.0
)
@@ -103,11 +103,11 @@ require (
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/crypto v0.9.0 // indirect
+ golang.org/x/crypto v0.21.0 // indirect
golang.org/x/sync v0.2.0 // indirect
- golang.org/x/sys v0.8.0 // indirect
- golang.org/x/term v0.8.0 // indirect
- golang.org/x/text v0.9.0 // indirect
+ golang.org/x/sys v0.18.0 // indirect
+ golang.org/x/term v0.18.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
diff --git a/go.sum b/go.sum
index 1bd67bc..be1f16d 100644
--- a/go.sum
+++ b/go.sum
@@ -1024,8 +1024,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
-golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1118,8 +1118,8 @@ golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1245,13 +1245,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
+golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1261,8 +1261,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
From b73a4a25b37a291679de7cddcfbc21832a71665d Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Wed, 5 Jun 2024 12:29:55 +0300
Subject: [PATCH 062/161] [#115] go.mod: Update vulnerable dependencies
Signed-off-by: Denis Kirillov
---
go.mod | 14 ++++++++------
go.sum | 35 ++++++++++++++++++++++++-----------
2 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/go.mod b/go.mod
index d862839..f30c263 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module git.frostfs.info/TrueCloudLab/frostfs-http-gw
-go 1.20
+go 1.21
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4
@@ -23,7 +23,7 @@ require (
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
golang.org/x/net v0.23.0
- google.golang.org/grpc v1.55.0
+ google.golang.org/grpc v1.61.1
)
require (
@@ -55,7 +55,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
- github.com/google/uuid v1.3.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
@@ -104,13 +104,15 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
- golang.org/x/sync v0.2.0 // indirect
+ golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
- google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
- google.golang.org/protobuf v1.30.0 // indirect
+ google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
+ google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index be1f16d..b372c25 100644
--- a/go.sum
+++ b/go.sum
@@ -241,6 +241,7 @@ github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
github.com/containerd/continuity v0.2.2 h1:QSqfxcn8c+12slxwu00AtzXrsami0MJb/MQs9lOLHLA=
+github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk=
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
@@ -374,6 +375,7 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
+github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -436,7 +438,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
-github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
+github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
+github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -492,7 +495,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -515,8 +519,9 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
@@ -607,6 +612,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -828,6 +834,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -995,6 +1002,7 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
+go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -1144,8 +1152,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
-golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1410,8 +1418,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
+google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
+google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
+google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
+google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1437,8 +1449,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
-google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
+google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
+google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1453,8 +1465,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -1464,6 +1476,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
From 5a87ee7625b8bc12eaab88ce0bbaf5582e04b1fd Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Wed, 5 Jun 2024 14:00:01 +0300
Subject: [PATCH 063/161] [#115] Fix ci build go version
Signed-off-by: Denis Kirillov
---
.docker/Dockerfile | 2 +-
.forgejo/workflows/builds.yml | 2 +-
.forgejo/workflows/tests.yml | 4 ++--
.forgejo/workflows/vulncheck.yml | 2 +-
Makefile | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.docker/Dockerfile b/.docker/Dockerfile
index 8b450a4..d39fba1 100644
--- a/.docker/Dockerfile
+++ b/.docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.21-alpine as basebuilder
+FROM golang:1.22-alpine as basebuilder
RUN apk add --update make bash ca-certificates
FROM basebuilder as builder
diff --git a/.forgejo/workflows/builds.yml b/.forgejo/workflows/builds.yml
index 97ac86b..17f1f2e 100644
--- a/.forgejo/workflows/builds.yml
+++ b/.forgejo/workflows/builds.yml
@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.20', '1.21' ]
+ go_versions: [ '1.21', '1.22' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index 14b9edf..74e0b2c 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -10,7 +10,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
- go-version: '1.21'
+ go-version: '1.22'
cache: true
- name: Install linters
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.20', '1.21' ]
+ go_versions: [ '1.21', '1.22' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
diff --git a/.forgejo/workflows/vulncheck.yml b/.forgejo/workflows/vulncheck.yml
index 0139e89..7a82bc3 100644
--- a/.forgejo/workflows/vulncheck.yml
+++ b/.forgejo/workflows/vulncheck.yml
@@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v3
with:
- go-version: '1.21'
+ go-version: '1.22'
- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest
diff --git a/Makefile b/Makefile
index d02d41b..04cfea4 100755
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
REPO ?= $(shell go list -m)
VERSION ?= $(shell git describe --tags --match "v*" --dirty --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
-GO_VERSION ?= 1.20
+GO_VERSION ?= 1.22
LINT_VERSION ?= 1.54.0
TRUECLOUDLAB_LINT_VERSION ?= 0.0.2
BUILD ?= $(shell date -u --iso=seconds)
From 23ed3ab86e68c275f17fb7956459a818b72bfd27 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Mon, 3 Jun 2024 01:42:01 +0300
Subject: [PATCH 064/161] [#114] Update frostfs-sdk-go version with support EC
Signed-off-by: Roman Loginov
---
go.mod | 4 ++--
go.sum | 8 ++++----
internal/handler/upload.go | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/go.mod b/go.mod
index f30c263..a34a092 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,9 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.21
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
diff --git a/go.sum b/go.sum
index b372c25..0cde076 100644
--- a/go.sum
+++ b/go.sum
@@ -37,16 +37,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4 h1:wjLfZ3WCt7qNGsQv+Jl0TXnmtg0uVk/jToKPFTBc/jo=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20231121085847-241a9f1ad0a4/go.mod h1:uY0AYmCznjZdghDnAk7THFIe1Vlg531IxUcus7ZfUJI=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3 h1:H5GvrVlowIMWfzqQkhY0p0myooJxQ1sMRVSFfXawwWg=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M=
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/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939 h1:jZEepi9yWmqrWgLRQcHQu4YPJaudmd7d2AEhpmM3m4U=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20231107114540-ab75edd70939/go.mod h1:t1akKcUH7iBrFHX8rSXScYMP17k2kYQXMbZooiL5Juw=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f h1:vBLC1OSGMSn7lRJv/p1of0veifuBdZdztVrF9Vn+UFk=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f/go.mod h1:4AObM67VUqkXQJlODTFThFnuMGEuK8h9DrAXHDZqvCU=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index 935b51b..1b18755 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -129,7 +129,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
obj := object.New()
obj.SetContainerID(bktInfo.CID)
- obj.SetOwnerID(h.ownerID)
+ obj.SetOwnerID(*h.ownerID)
obj.SetAttributes(attributes...)
var prm pool.PrmObjectPut
From 826dd0cdbe2511f505e06e44dcf7696119a8d6fe Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Fri, 14 Jun 2024 16:43:58 +0300
Subject: [PATCH 065/161] [#117] Fix integration test after updating
dependencies
Signed-off-by: Denis Kirillov
---
cmd/http-gw/integration_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go
index 5d21094..cae40a5 100644
--- a/cmd/http-gw/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -506,7 +506,7 @@ func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, o
func putObject(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID, content string, attributes map[string]string) oid.ID {
obj := object.New()
obj.SetContainerID(CID)
- obj.SetOwnerID(&ownerID)
+ obj.SetOwnerID(ownerID)
var attrs []object.Attribute
for key, val := range attributes {
From 3741e3b003e40715c258f17638da88c43c1d92df Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Mon, 17 Jun 2024 17:56:43 +0300
Subject: [PATCH 066/161] [#117] Add mocked handler for tests
Signed-off-by: Denis Kirillov
---
cmd/http-gw/app.go | 7 +-
internal/frostfs/frostfs.go | 260 +++++++++++++++++++++++++++
internal/handler/download.go | 31 ++--
internal/handler/frostfs_mock.go | 260 +++++++++++++++++++++++++++
internal/handler/handler.go | 124 +++++++++++--
internal/handler/handler_test.go | 296 +++++++++++++++++++++++++++++++
internal/handler/head.go | 37 ++--
internal/handler/reader.go | 20 ++-
internal/handler/upload.go | 24 ++-
utils/attributes.go | 16 +-
utils/params.go | 17 --
utils/util.go | 26 ---
12 files changed, 1005 insertions(+), 113 deletions(-)
create mode 100644 internal/frostfs/frostfs.go
create mode 100644 internal/handler/frostfs_mock.go
create mode 100644 internal/handler/handler_test.go
delete mode 100644 utils/params.go
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 2a20d86..1d12122 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -16,6 +16,7 @@ import (
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
@@ -600,10 +601,10 @@ func (a *app) reqNamespace(h fasthttp.RequestHandler) fasthttp.RequestHandler {
}
}
-func (a *app) AppParams() *utils.AppParams {
- return &utils.AppParams{
+func (a *app) AppParams() *handler.AppParams {
+ return &handler.AppParams{
Logger: a.log,
- Pool: a.pool,
+ FrostFS: frostfs.NewFrostFS(a.pool),
Owner: a.owner,
Resolver: a.resolver,
Cache: cache.NewBucketCache(getCacheOptions(a.cfg, a.log)),
diff --git a/internal/frostfs/frostfs.go b/internal/frostfs/frostfs.go
new file mode 100644
index 0000000..dde560b
--- /dev/null
+++ b/internal/frostfs/frostfs.go
@@ -0,0 +1,260 @@
+package frostfs
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+// FrostFS represents virtual connection to the FrostFS network.
+// It is used to provide an interface to dependent packages
+// which work with FrostFS.
+type FrostFS struct {
+ pool *pool.Pool
+}
+
+// NewFrostFS creates new FrostFS using provided pool.Pool.
+func NewFrostFS(p *pool.Pool) *FrostFS {
+ return &FrostFS{
+ pool: p,
+ }
+}
+
+// Container implements frostfs.FrostFS interface method.
+func (x *FrostFS) Container(ctx context.Context, layerPrm handler.PrmContainer) (*container.Container, error) {
+ prm := pool.PrmContainerGet{
+ ContainerID: layerPrm.ContainerID,
+ }
+
+ res, err := x.pool.GetContainer(ctx, prm)
+ if err != nil {
+ return nil, handleObjectError("read container via connection pool", err)
+ }
+
+ return &res, nil
+}
+
+// CreateObject implements frostfs.FrostFS interface method.
+func (x *FrostFS) CreateObject(ctx context.Context, prm handler.PrmObjectCreate) (oid.ID, error) {
+ var prmPut pool.PrmObjectPut
+ prmPut.SetHeader(*prm.Object)
+ prmPut.SetPayload(prm.Payload)
+ prmPut.SetClientCut(prm.ClientCut)
+ prmPut.WithoutHomomorphicHash(prm.WithoutHomomorphicHash)
+ prmPut.SetBufferMaxSize(prm.BufferMaxSize)
+
+ if prm.BearerToken != nil {
+ prmPut.UseBearer(*prm.BearerToken)
+ }
+
+ idObj, err := x.pool.PutObject(ctx, prmPut)
+ return idObj, handleObjectError("save object via connection pool", err)
+}
+
+// wraps io.ReadCloser and transforms Read errors related to access violation
+// to frostfs.ErrAccessDenied.
+type payloadReader struct {
+ io.ReadCloser
+}
+
+func (x payloadReader) Read(p []byte) (int, error) {
+ n, err := x.ReadCloser.Read(p)
+ if err != nil && errors.Is(err, io.EOF) {
+ return n, err
+ }
+ return n, handleObjectError("read payload", err)
+}
+
+// ReadObject implements frostfs.FrostFS interface method.
+func (x *FrostFS) ReadObject(ctx context.Context, prm handler.PrmObjectRead) (*handler.ObjectPart, error) {
+ var prmGet pool.PrmObjectGet
+ prmGet.SetAddress(prm.Address)
+
+ if prm.BearerToken != nil {
+ prmGet.UseBearer(*prm.BearerToken)
+ }
+
+ if prm.WithHeader {
+ if prm.WithPayload {
+ res, err := x.pool.GetObject(ctx, prmGet)
+ if err != nil {
+ return nil, handleObjectError("init full object reading via connection pool", err)
+ }
+
+ defer res.Payload.Close()
+
+ payload, err := io.ReadAll(res.Payload)
+ if err != nil {
+ return nil, handleObjectError("read full object payload", err)
+ }
+
+ res.Header.SetPayload(payload)
+
+ return &handler.ObjectPart{
+ Head: &res.Header,
+ }, nil
+ }
+
+ var prmHead pool.PrmObjectHead
+ prmHead.SetAddress(prm.Address)
+
+ if prm.BearerToken != nil {
+ prmHead.UseBearer(*prm.BearerToken)
+ }
+
+ hdr, err := x.pool.HeadObject(ctx, prmHead)
+ if err != nil {
+ return nil, handleObjectError("read object header via connection pool", err)
+ }
+
+ return &handler.ObjectPart{
+ Head: &hdr,
+ }, nil
+ } else if prm.PayloadRange[0]+prm.PayloadRange[1] == 0 {
+ res, err := x.pool.GetObject(ctx, prmGet)
+ if err != nil {
+ return nil, handleObjectError("init full payload range reading via connection pool", err)
+ }
+
+ return &handler.ObjectPart{
+ Payload: res.Payload,
+ }, nil
+ }
+
+ var prmRange pool.PrmObjectRange
+ prmRange.SetAddress(prm.Address)
+ prmRange.SetOffset(prm.PayloadRange[0])
+ prmRange.SetLength(prm.PayloadRange[1])
+
+ if prm.BearerToken != nil {
+ prmRange.UseBearer(*prm.BearerToken)
+ }
+
+ res, err := x.pool.ObjectRange(ctx, prmRange)
+ if err != nil {
+ return nil, handleObjectError("init payload range reading via connection pool", err)
+ }
+
+ return &handler.ObjectPart{
+ Payload: payloadReader{&res},
+ }, nil
+}
+
+// SearchObjects implements frostfs.FrostFS interface method.
+func (x *FrostFS) SearchObjects(ctx context.Context, prm handler.PrmObjectSearch) (handler.ResObjectSearch, error) {
+ var prmSearch pool.PrmObjectSearch
+ prmSearch.SetContainerID(prm.Container)
+ prmSearch.SetFilters(prm.Filters)
+
+ if prm.BearerToken != nil {
+ prmSearch.UseBearer(*prm.BearerToken)
+ }
+
+ res, err := x.pool.SearchObjects(ctx, prmSearch)
+ if err != nil {
+ return nil, handleObjectError("init object search via connection pool", err)
+ }
+
+ return &res, nil
+}
+
+// GetEpochDurations implements frostfs.FrostFS interface method.
+func (x *FrostFS) GetEpochDurations(ctx context.Context) (*utils.EpochDurations, error) {
+ networkInfo, err := x.pool.NetworkInfo(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ res := &utils.EpochDurations{
+ CurrentEpoch: networkInfo.CurrentEpoch(),
+ MsPerBlock: networkInfo.MsPerBlock(),
+ BlockPerEpoch: networkInfo.EpochDuration(),
+ }
+
+ if res.BlockPerEpoch == 0 {
+ return nil, fmt.Errorf("EpochDuration is empty")
+ }
+ return res, nil
+}
+
+// ResolverFrostFS represents virtual connection to the FrostFS network.
+// It implements resolver.FrostFS.
+type ResolverFrostFS struct {
+ pool *pool.Pool
+}
+
+// NewResolverFrostFS creates new ResolverFrostFS using provided pool.Pool.
+func NewResolverFrostFS(p *pool.Pool) *ResolverFrostFS {
+ return &ResolverFrostFS{pool: p}
+}
+
+// SystemDNS implements resolver.FrostFS interface method.
+func (x *ResolverFrostFS) SystemDNS(ctx context.Context) (string, error) {
+ networkInfo, err := x.pool.NetworkInfo(ctx)
+ if err != nil {
+ return "", handleObjectError("read network info via client", err)
+ }
+
+ domain := networkInfo.RawNetworkParameter("SystemDNS")
+ if domain == nil {
+ return "", errors.New("system DNS parameter not found or empty")
+ }
+
+ return string(domain), nil
+}
+
+func handleObjectError(msg string, err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if reason, ok := IsErrObjectAccessDenied(err); ok {
+ return fmt.Errorf("%s: %w: %s", msg, handler.ErrAccessDenied, reason)
+ }
+
+ if IsTimeoutError(err) {
+ return fmt.Errorf("%s: %w: %s", msg, handler.ErrGatewayTimeout, err.Error())
+ }
+
+ return fmt.Errorf("%s: %w", msg, err)
+}
+
+func UnwrapErr(err error) error {
+ unwrappedErr := errors.Unwrap(err)
+ for unwrappedErr != nil {
+ err = unwrappedErr
+ unwrappedErr = errors.Unwrap(err)
+ }
+
+ return err
+}
+
+func IsErrObjectAccessDenied(err error) (string, bool) {
+ err = UnwrapErr(err)
+ switch err := err.(type) {
+ default:
+ return "", false
+ case *apistatus.ObjectAccessDenied:
+ return err.Reason(), true
+ }
+}
+
+func IsTimeoutError(err error) bool {
+ if strings.Contains(err.Error(), "timeout") ||
+ errors.Is(err, context.DeadlineExceeded) {
+ return true
+ }
+
+ return status.Code(UnwrapErr(err)) == codes.DeadlineExceeded
+}
diff --git a/internal/handler/download.go b/internal/handler/download.go
index a7aee64..07fe3e9 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -17,7 +17,6 @@ import (
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -46,19 +45,20 @@ func (h *Handler) DownloadByAttribute(c *fasthttp.RequestCtx) {
h.byAttribute(c, h.receiveFile)
}
-func (h *Handler) search(ctx context.Context, cid *cid.ID, key, val string, op object.SearchMatchType) (pool.ResObjectSearch, error) {
+func (h *Handler) search(ctx context.Context, cnrID *cid.ID, key, val string, op object.SearchMatchType) (ResObjectSearch, error) {
filters := object.NewSearchFilters()
filters.AddRootFilter()
filters.AddFilter(key, val, op)
- var prm pool.PrmObjectSearch
- prm.SetContainerID(*cid)
- prm.SetFilters(filters)
- if btoken := bearerToken(ctx); btoken != nil {
- prm.UseBearer(*btoken)
+ prm := PrmObjectSearch{
+ PrmAuth: PrmAuth{
+ BearerToken: bearerToken(ctx),
+ },
+ Container: *cnrID,
+ Filters: filters,
}
- return h.pool.SearchObjects(ctx, prm)
+ return h.frostfs.SearchObjects(ctx, prm)
}
func (h *Handler) addObjectToZip(zw *zip.Writer, obj *object.Object) (io.Writer, error) {
@@ -153,18 +153,21 @@ func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
}
func (h *Handler) zipObject(ctx context.Context, zipWriter *zip.Writer, addr oid.Address, btoken *bearer.Token, bufZip []byte) error {
- var prm pool.PrmObjectGet
- prm.SetAddress(addr)
- if btoken != nil {
- prm.UseBearer(*btoken)
+ prm := PrmObjectRead{
+ PrmAuth: PrmAuth{
+ BearerToken: btoken,
+ },
+ Address: addr,
+ WithHeader: true,
+ WithPayload: true,
}
- resGet, err := h.pool.GetObject(ctx, prm)
+ resGet, err := h.frostfs.ReadObject(ctx, prm)
if err != nil {
return fmt.Errorf("get FrostFS object: %v", err)
}
- objWriter, err := h.addObjectToZip(zipWriter, &resGet.Header)
+ objWriter, err := h.addObjectToZip(zipWriter, resGet.Head)
if err != nil {
return fmt.Errorf("zip create header: %v", err)
}
diff --git a/internal/handler/frostfs_mock.go b/internal/handler/frostfs_mock.go
new file mode 100644
index 0000000..85ae874
--- /dev/null
+++ b/internal/handler/frostfs_mock.go
@@ -0,0 +1,260 @@
+package handler
+
+import (
+ "bytes"
+ "context"
+ "crypto/rand"
+ "crypto/sha256"
+ "fmt"
+ "io"
+ "strings"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
+ apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
+ "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
+)
+
+type TestFrostFS struct {
+ objects map[string]*object.Object
+ containers map[string]*container.Container
+ accessList map[string]bool
+ key *keys.PrivateKey
+}
+
+func NewTestFrostFS(key *keys.PrivateKey) *TestFrostFS {
+ return &TestFrostFS{
+ objects: make(map[string]*object.Object),
+ containers: make(map[string]*container.Container),
+ accessList: make(map[string]bool),
+ key: key,
+ }
+}
+
+func (t *TestFrostFS) ContainerID(name string) (*cid.ID, error) {
+ for id, cnr := range t.containers {
+ if container.Name(*cnr) == name {
+ var cnrID cid.ID
+ return &cnrID, cnrID.DecodeString(id)
+ }
+ }
+ return nil, fmt.Errorf("not found")
+}
+
+func (t *TestFrostFS) SetContainer(cnrID cid.ID, cnr *container.Container) {
+ t.containers[cnrID.EncodeToString()] = cnr
+}
+
+// AllowUserOperation grants access to object operations.
+// Empty userID and objID means any user and object respectively.
+func (t *TestFrostFS) AllowUserOperation(cnrID cid.ID, userID user.ID, op acl.Op, objID oid.ID) {
+ t.accessList[fmt.Sprintf("%s/%s/%s/%s", cnrID, userID, op, objID)] = true
+}
+
+func (t *TestFrostFS) Container(_ context.Context, prm PrmContainer) (*container.Container, error) {
+ for k, v := range t.containers {
+ if k == prm.ContainerID.EncodeToString() {
+ return v, nil
+ }
+ }
+
+ return nil, fmt.Errorf("container not found %s", prm.ContainerID)
+}
+
+func (t *TestFrostFS) requestOwner(btoken *bearer.Token) user.ID {
+ if btoken != nil {
+ return bearer.ResolveIssuer(*btoken)
+ }
+
+ var owner user.ID
+ user.IDFromKey(&owner, t.key.PrivateKey.PublicKey)
+ return owner
+}
+
+func (t *TestFrostFS) ReadObject(_ context.Context, prm PrmObjectRead) (*ObjectPart, error) {
+ sAddr := prm.Address.EncodeToString()
+
+ if obj, ok := t.objects[sAddr]; ok {
+ owner := t.requestOwner(prm.BearerToken)
+
+ if !t.isAllowed(prm.Address.Container(), owner, acl.OpObjectGet, prm.Address.Object()) {
+ return nil, ErrAccessDenied
+ }
+
+ payload := obj.Payload()
+
+ if prm.PayloadRange[0]+prm.PayloadRange[1] > 0 {
+ off := prm.PayloadRange[0]
+ payload = payload[off : off+prm.PayloadRange[1]]
+ }
+
+ return &ObjectPart{
+ Head: obj,
+ Payload: io.NopCloser(bytes.NewReader(payload)),
+ }, nil
+ }
+
+ return nil, fmt.Errorf("%w: %s", &apistatus.ObjectNotFound{}, prm.Address)
+}
+func (t *TestFrostFS) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID, error) {
+ b := make([]byte, 32)
+ if _, err := io.ReadFull(rand.Reader, b); err != nil {
+ return oid.ID{}, err
+ }
+ var id oid.ID
+ id.SetSHA256(sha256.Sum256(b))
+ prm.Object.SetID(id)
+
+ attrs := prm.Object.Attributes()
+ if prm.ClientCut {
+ a := object.NewAttribute()
+ a.SetKey("s3-client-cut")
+ a.SetValue("true")
+ attrs = append(attrs, *a)
+ }
+
+ prm.Object.SetAttributes(attrs...)
+
+ if prm.Payload != nil {
+ all, err := io.ReadAll(prm.Payload)
+ if err != nil {
+ return oid.ID{}, err
+ }
+ prm.Object.SetPayload(all)
+ prm.Object.SetPayloadSize(uint64(len(all)))
+ var hash checksum.Checksum
+ checksum.Calculate(&hash, checksum.SHA256, all)
+ prm.Object.SetPayloadChecksum(hash)
+ }
+
+ cnrID, _ := prm.Object.ContainerID()
+ objID, _ := prm.Object.ID()
+
+ owner := t.requestOwner(prm.BearerToken)
+
+ if !t.isAllowed(cnrID, owner, acl.OpObjectPut, objID) {
+ return oid.ID{}, ErrAccessDenied
+ }
+
+ addr := newAddress(cnrID, objID)
+ t.objects[addr.EncodeToString()] = prm.Object
+ return objID, nil
+}
+
+type resObjectSearchMock struct {
+ res []oid.ID
+}
+
+func (r *resObjectSearchMock) Read(buf []oid.ID) (int, error) {
+ for i := range buf {
+ if i > len(r.res)-1 {
+ return len(r.res), io.EOF
+ }
+ buf[i] = r.res[i]
+ }
+
+ r.res = r.res[len(buf):]
+
+ return len(buf), nil
+}
+
+func (r *resObjectSearchMock) Iterate(f func(oid.ID) bool) error {
+ for _, id := range r.res {
+ if f(id) {
+ return nil
+ }
+ }
+
+ return nil
+}
+
+func (r *resObjectSearchMock) Close() {}
+
+func (t *TestFrostFS) SearchObjects(_ context.Context, prm PrmObjectSearch) (ResObjectSearch, error) {
+ if !t.isAllowed(prm.Container, t.requestOwner(prm.BearerToken), acl.OpObjectSearch, oid.ID{}) {
+ return nil, ErrAccessDenied
+ }
+
+ cidStr := prm.Container.EncodeToString()
+ var res []oid.ID
+
+ if len(prm.Filters) == 1 { // match root filter
+ for k, v := range t.objects {
+ if strings.Contains(k, cidStr) {
+ id, _ := v.ID()
+ res = append(res, id)
+ }
+ }
+ return &resObjectSearchMock{res: res}, nil
+ }
+
+ filter := prm.Filters[1]
+ if len(prm.Filters) != 2 ||
+ filter.Operation() != object.MatchCommonPrefix && filter.Operation() != object.MatchStringEqual {
+ return nil, fmt.Errorf("usupported filters")
+ }
+
+ for k, v := range t.objects {
+ if strings.Contains(k, cidStr) && isMatched(v.Attributes(), filter) {
+ id, _ := v.ID()
+ res = append(res, id)
+ }
+ }
+
+ return &resObjectSearchMock{res: res}, nil
+}
+
+func isMatched(attributes []object.Attribute, filter object.SearchFilter) bool {
+ for _, attr := range attributes {
+ if attr.Key() == filter.Header() {
+ switch filter.Operation() {
+ case object.MatchStringEqual:
+ return attr.Value() == filter.Value()
+ case object.MatchCommonPrefix:
+ return strings.HasPrefix(attr.Value(), filter.Value())
+ default:
+ return false
+ }
+ }
+ }
+
+ return false
+}
+
+func (t *TestFrostFS) GetEpochDurations(context.Context) (*utils.EpochDurations, error) {
+ return &utils.EpochDurations{
+ CurrentEpoch: 10,
+ MsPerBlock: 1000,
+ BlockPerEpoch: 100,
+ }, nil
+}
+
+func (t *TestFrostFS) isAllowed(cnrID cid.ID, userID user.ID, op acl.Op, objID oid.ID) bool {
+ keysToCheck := []string{
+ fmt.Sprintf("%s/%s/%s/%s", cnrID, userID, op, objID),
+ fmt.Sprintf("%s/%s/%s/%s", cnrID, userID, op, oid.ID{}),
+ fmt.Sprintf("%s/%s/%s/%s", cnrID, user.ID{}, op, objID),
+ fmt.Sprintf("%s/%s/%s/%s", cnrID, user.ID{}, op, oid.ID{}),
+ }
+
+ for _, key := range keysToCheck {
+ if t.accessList[key] {
+ return true
+ }
+ }
+ return false
+}
+
+func newAddress(cnr cid.ID, obj oid.ID) oid.Address {
+ var addr oid.Address
+ addr.SetContainer(cnr)
+ addr.SetObject(obj)
+ return addr
+}
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index f88dff1..c87551e 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -12,16 +12,15 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
@@ -35,20 +34,125 @@ type Config interface {
NamespaceHeader() string
}
+// PrmContainer groups parameters of FrostFS.Container operation.
+type PrmContainer struct {
+ // Container identifier.
+ ContainerID cid.ID
+}
+
+// PrmAuth groups authentication parameters for the FrostFS operation.
+type PrmAuth struct {
+ // Bearer token to be used for the operation. Overlaps PrivateKey. Optional.
+ BearerToken *bearer.Token
+}
+
+// PrmObjectRead groups parameters of FrostFS.ReadObject operation.
+type PrmObjectRead struct {
+ // Authentication parameters.
+ PrmAuth
+
+ // Address to read the object header from.
+ Address oid.Address
+
+ // Flag to read object header.
+ WithHeader bool
+
+ // Flag to read object payload. False overlaps payload range.
+ WithPayload bool
+
+ // Offset-length range of the object payload to be read.
+ PayloadRange [2]uint64
+}
+
+// ObjectPart represents partially read FrostFS object.
+type ObjectPart struct {
+ // Object header with optional in-memory payload part.
+ Head *object.Object
+
+ // Object payload part encapsulated in io.Reader primitive.
+ // Returns ErrAccessDenied on read access violation.
+ Payload io.ReadCloser
+}
+
+// PrmObjectCreate groups parameters of FrostFS.CreateObject operation.
+type PrmObjectCreate struct {
+ // Authentication parameters.
+ PrmAuth
+
+ Object *object.Object
+
+ // Object payload encapsulated in io.Reader primitive.
+ Payload io.Reader
+
+ // Enables client side object preparing.
+ ClientCut bool
+
+ // Disables using Tillich-Zémor hash for payload.
+ WithoutHomomorphicHash bool
+
+ // Sets max buffer size to read payload.
+ BufferMaxSize uint64
+}
+
+// PrmObjectSearch groups parameters of FrostFS.sear SearchObjects operation.
+type PrmObjectSearch struct {
+ // Authentication parameters.
+ PrmAuth
+
+ // Container to select the objects from.
+ Container cid.ID
+
+ Filters object.SearchFilters
+}
+
+type ResObjectSearch interface {
+ Read(buf []oid.ID) (int, error)
+ Iterate(f func(oid.ID) bool) error
+ Close()
+}
+
+var (
+ // ErrAccessDenied is returned from FrostFS in case of access violation.
+ ErrAccessDenied = errors.New("access denied")
+ // ErrGatewayTimeout is returned from FrostFS in case of timeout, deadline exceeded etc.
+ ErrGatewayTimeout = errors.New("gateway timeout")
+)
+
+// FrostFS represents virtual connection to FrostFS network.
+type FrostFS interface {
+ Container(context.Context, PrmContainer) (*container.Container, error)
+ ReadObject(context.Context, PrmObjectRead) (*ObjectPart, error)
+ CreateObject(context.Context, PrmObjectCreate) (oid.ID, error)
+ SearchObjects(context.Context, PrmObjectSearch) (ResObjectSearch, error)
+ utils.EpochInfoFetcher
+}
+
+type ContainerResolver interface {
+ Resolve(ctx context.Context, name string) (*cid.ID, error)
+}
+
type Handler struct {
log *zap.Logger
- pool *pool.Pool
+ frostfs FrostFS
ownerID *user.ID
config Config
- containerResolver *resolver.ContainerResolver
+ containerResolver ContainerResolver
tree *tree.Tree
cache *cache.BucketCache
}
-func New(params *utils.AppParams, config Config, tree *tree.Tree) *Handler {
+type AppParams struct {
+ Logger *zap.Logger
+ FrostFS FrostFS
+ Owner *user.ID
+ Resolver ContainerResolver
+ Cache *cache.BucketCache
+}
+
+func New(params *AppParams, config Config, tree *tree.Tree) *Handler {
return &Handler{
log: params.Logger,
- pool: params.Pool,
+ frostfs: params.FrostFS,
ownerID: params.Owner,
config: config,
containerResolver: params.Resolver,
@@ -235,8 +339,8 @@ func (h *Handler) getBucketInfo(ctx context.Context, containerName string, log *
}
func (h *Handler) readContainer(ctx context.Context, cnrID cid.ID) (*data.BucketInfo, error) {
- prm := pool.PrmContainerGet{ContainerID: cnrID}
- res, err := h.pool.GetContainer(ctx, prm)
+ prm := PrmContainer{ContainerID: cnrID}
+ res, err := h.frostfs.Container(ctx, prm)
if err != nil {
return nil, fmt.Errorf("get frostfs container '%s': %w", cnrID.String(), err)
}
@@ -246,12 +350,12 @@ func (h *Handler) readContainer(ctx context.Context, cnrID cid.ID) (*data.Bucket
Name: cnrID.EncodeToString(),
}
- if domain := container.ReadDomain(res); domain.Name() != "" {
+ if domain := container.ReadDomain(*res); domain.Name() != "" {
bktInfo.Name = domain.Name()
bktInfo.Zone = domain.Zone()
}
- bktInfo.HomomorphicHashDisabled = container.IsHomomorphicHashingDisabled(res)
+ bktInfo.HomomorphicHashDisabled = container.IsHomomorphicHashingDisabled(*res)
return bktInfo, err
}
diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go
new file mode 100644
index 0000000..ed67f88
--- /dev/null
+++ b/internal/handler/handler_test.go
@@ -0,0 +1,296 @@
+package handler
+
+import (
+ "archive/zip"
+ "bytes"
+ "context"
+ "encoding/json"
+ "io"
+ "mime/multipart"
+ "net/http"
+ "testing"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
+ "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
+ "github.com/stretchr/testify/require"
+ "github.com/valyala/fasthttp"
+ "go.uber.org/zap"
+)
+
+type treeClientMock struct {
+}
+
+func (t *treeClientMock) GetNodes(context.Context, *tree.GetNodesParams) ([]tree.NodeResponse, error) {
+ return nil, nil
+}
+
+type configMock struct {
+}
+
+func (c *configMock) DefaultTimestamp() bool {
+ return false
+}
+
+func (c *configMock) ZipCompression() bool {
+ return false
+}
+
+func (c *configMock) ClientCut() bool {
+ return false
+}
+
+func (c *configMock) BufferMaxSizeForPut() uint64 {
+ return 0
+}
+
+func (c *configMock) NamespaceHeader() string {
+ return ""
+}
+
+type handlerContext struct {
+ key *keys.PrivateKey
+ owner user.ID
+
+ h *Handler
+ frostfs *TestFrostFS
+ tree *treeClientMock
+ cfg *configMock
+}
+
+func (hc *handlerContext) Handler() *Handler {
+ return hc.h
+}
+
+func prepareHandlerContext() (*handlerContext, error) {
+ logger, err := zap.NewDevelopment()
+ if err != nil {
+ return nil, err
+ }
+
+ key, err := keys.NewPrivateKey()
+ if err != nil {
+ return nil, err
+ }
+
+ var owner user.ID
+ user.IDFromKey(&owner, key.PrivateKey.PublicKey)
+
+ testFrostFS := NewTestFrostFS(key)
+
+ testResolver := &resolver.Resolver{Name: "test_resolver"}
+ testResolver.SetResolveFunc(func(_ context.Context, name string) (*cid.ID, error) {
+ return testFrostFS.ContainerID(name)
+ })
+
+ params := &AppParams{
+ Logger: logger,
+ FrostFS: testFrostFS,
+ Owner: &owner,
+ Resolver: testResolver,
+ Cache: cache.NewBucketCache(&cache.Config{
+ Size: 1,
+ Lifetime: 1,
+ Logger: logger,
+ }),
+ }
+
+ treeMock := &treeClientMock{}
+ cfgMock := &configMock{}
+
+ handler := New(params, cfgMock, tree.NewTree(treeMock))
+
+ return &handlerContext{
+ key: key,
+ owner: owner,
+ h: handler,
+ frostfs: testFrostFS,
+ tree: treeMock,
+ cfg: cfgMock,
+ }, nil
+}
+
+func (hc *handlerContext) prepareContainer(name string, basicACL acl.Basic) (cid.ID, *container.Container, error) {
+ var pp netmap.PlacementPolicy
+ err := pp.DecodeString("REP 1")
+ if err != nil {
+ return cid.ID{}, nil, err
+ }
+
+ var cnr container.Container
+ cnr.Init()
+ cnr.SetOwner(hc.owner)
+ cnr.SetPlacementPolicy(pp)
+ cnr.SetBasicACL(basicACL)
+
+ var domain container.Domain
+ domain.SetName(name)
+ container.WriteDomain(&cnr, domain)
+ container.SetName(&cnr, name)
+ container.SetCreationTime(&cnr, time.Now())
+
+ cnrID := cidtest.ID()
+
+ for op := acl.OpObjectGet; op < acl.OpObjectHash; op++ {
+ hc.frostfs.AllowUserOperation(cnrID, hc.owner, op, oid.ID{})
+ if basicACL.IsOpAllowed(op, acl.RoleOthers) {
+ hc.frostfs.AllowUserOperation(cnrID, user.ID{}, op, oid.ID{})
+ }
+ }
+
+ return cnrID, &cnr, nil
+}
+
+func TestBasic(t *testing.T) {
+ hc, err := prepareHandlerContext()
+ require.NoError(t, err)
+
+ bktName := "bucket"
+ cnrID, cnr, err := hc.prepareContainer(bktName, acl.PublicRWExtended)
+ require.NoError(t, err)
+ hc.frostfs.SetContainer(cnrID, cnr)
+
+ ctx := context.Background()
+ ctx = middleware.SetNamespace(ctx, "")
+
+ content := "hello"
+ r, err := prepareUploadRequest(ctx, cnrID.EncodeToString(), content)
+ require.NoError(t, err)
+
+ hc.Handler().Upload(r)
+ require.Equal(t, r.Response.StatusCode(), http.StatusOK)
+
+ var putRes putResponse
+ err = json.Unmarshal(r.Response.Body(), &putRes)
+ require.NoError(t, err)
+
+ obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID]
+ attr := object.NewAttribute()
+ attr.SetKey(object.AttributeFilePath)
+ attr.SetValue(objFileName)
+ obj.SetAttributes(append(obj.Attributes(), *attr)...)
+
+ t.Run("get", func(t *testing.T) {
+ r = prepareGetRequest(ctx, cnrID.EncodeToString(), putRes.ObjectID)
+ hc.Handler().DownloadByAddressOrBucketName(r)
+ require.Equal(t, content, string(r.Response.Body()))
+ })
+
+ t.Run("head", func(t *testing.T) {
+ r = prepareGetRequest(ctx, cnrID.EncodeToString(), putRes.ObjectID)
+ hc.Handler().HeadByAddressOrBucketName(r)
+ require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
+ require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
+ })
+
+ t.Run("get by attribute", func(t *testing.T) {
+ r = prepareGetByAttributeRequest(ctx, bktName, keyAttr, valAttr)
+ hc.Handler().DownloadByAttribute(r)
+ require.Equal(t, content, string(r.Response.Body()))
+ })
+
+ t.Run("head by attribute", func(t *testing.T) {
+ r = prepareGetByAttributeRequest(ctx, bktName, keyAttr, valAttr)
+ hc.Handler().HeadByAttribute(r)
+ require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
+ require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
+ })
+
+ t.Run("zip", func(t *testing.T) {
+ r = prepareGetZipped(ctx, bktName, "")
+ hc.Handler().DownloadZipped(r)
+
+ readerAt := bytes.NewReader(r.Response.Body())
+ zipReader, err := zip.NewReader(readerAt, int64(len(r.Response.Body())))
+ require.NoError(t, err)
+ require.Len(t, zipReader.File, 1)
+ require.Equal(t, objFileName, zipReader.File[0].Name)
+ f, err := zipReader.File[0].Open()
+ require.NoError(t, err)
+ defer func() {
+ inErr := f.Close()
+ require.NoError(t, inErr)
+ }()
+ data, err := io.ReadAll(f)
+ require.NoError(t, err)
+ require.Equal(t, content, string(data))
+ })
+}
+
+func prepareUploadRequest(ctx context.Context, bucket, content string) (*fasthttp.RequestCtx, error) {
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", bucket)
+ return r, fillMultipartBody(r, content)
+}
+
+func prepareGetRequest(ctx context.Context, bucket, objID string) *fasthttp.RequestCtx {
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", bucket)
+ r.SetUserValue("oid", objID)
+ return r
+}
+
+func prepareGetByAttributeRequest(ctx context.Context, bucket, attrKey, attrVal string) *fasthttp.RequestCtx {
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", bucket)
+ r.SetUserValue("attr_key", attrKey)
+ r.SetUserValue("attr_val", attrVal)
+ return r
+}
+
+func prepareGetZipped(ctx context.Context, bucket, prefix string) *fasthttp.RequestCtx {
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", bucket)
+ r.SetUserValue("prefix", prefix)
+ return r
+}
+
+const (
+ keyAttr = "User-Attribute"
+ valAttr = "user value"
+ objFileName = "newFile.txt"
+)
+
+func fillMultipartBody(r *fasthttp.RequestCtx, content string) error {
+ attributes := map[string]string{
+ object.AttributeFileName: objFileName,
+ keyAttr: valAttr,
+ }
+
+ var buff bytes.Buffer
+ w := multipart.NewWriter(&buff)
+ fw, err := w.CreateFormFile("file", attributes[object.AttributeFileName])
+ if err != nil {
+ return err
+ }
+
+ if _, err = io.Copy(fw, bytes.NewBufferString(content)); err != nil {
+ return err
+ }
+
+ if err = w.Close(); err != nil {
+ return err
+ }
+
+ r.Request.SetBodyStream(&buff, buff.Len())
+ r.Request.Header.Set("Content-Type", w.FormDataContentType())
+ r.Request.Header.Set("X-Attribute-"+keyAttr, valAttr)
+
+ return nil
+}
diff --git a/internal/handler/head.go b/internal/handler/head.go
index 9418567..2a17f64 100644
--- a/internal/handler/head.go
+++ b/internal/handler/head.go
@@ -11,7 +11,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -30,21 +29,23 @@ func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid
btoken := bearerToken(ctx)
- var prm pool.PrmObjectHead
- prm.SetAddress(objectAddress)
- if btoken != nil {
- prm.UseBearer(*btoken)
+ prm := PrmObjectRead{
+ PrmAuth: PrmAuth{
+ BearerToken: btoken,
+ },
+ Address: objectAddress,
+ WithHeader: true,
}
- obj, err := h.pool.HeadObject(ctx, prm)
+ obj, err := h.frostfs.ReadObject(ctx, prm)
if err != nil {
req.handleFrostFSErr(err, start)
return
}
- req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(obj.PayloadSize(), 10))
+ req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(obj.Head.PayloadSize(), 10))
var contentType string
- for _, attr := range obj.Attributes() {
+ for _, attr := range obj.Head.Attributes() {
key := attr.Key()
val := attr.Value()
if !isValidToken(key) || !isValidValue(val) {
@@ -70,22 +71,24 @@ func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid
}
}
- idsToResponse(&req.Response, &obj)
+ idsToResponse(&req.Response, obj.Head)
if len(contentType) == 0 {
- contentType, _, err = readContentType(obj.PayloadSize(), func(sz uint64) (io.Reader, error) {
- var prmRange pool.PrmObjectRange
- prmRange.SetAddress(objectAddress)
- prmRange.SetLength(sz)
- if btoken != nil {
- prmRange.UseBearer(*btoken)
+ contentType, _, err = readContentType(obj.Head.PayloadSize(), func(sz uint64) (io.Reader, error) {
+ prmRange := PrmObjectRead{
+ PrmAuth: PrmAuth{
+ BearerToken: btoken,
+ },
+ Address: objectAddress,
+ WithPayload: true,
+ PayloadRange: [2]uint64{0, sz},
}
- resObj, err := h.pool.ObjectRange(ctx, prmRange)
+ resObj, err := h.frostfs.ReadObject(ctx, prmRange)
if err != nil {
return nil, err
}
- return &resObj, nil
+ return resObj.Payload, nil
})
if err != nil && err != io.EOF {
req.handleFrostFSErr(err, start)
diff --git a/internal/handler/reader.go b/internal/handler/reader.go
index 76801f7..b48ac6d 100644
--- a/internal/handler/reader.go
+++ b/internal/handler/reader.go
@@ -14,7 +14,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -56,13 +55,16 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
filename string
)
- var prm pool.PrmObjectGet
- prm.SetAddress(objectAddress)
- if btoken := bearerToken(ctx); btoken != nil {
- prm.UseBearer(*btoken)
+ prm := PrmObjectRead{
+ PrmAuth: PrmAuth{
+ BearerToken: bearerToken(ctx),
+ },
+ Address: objectAddress,
+ WithHeader: true,
+ WithPayload: true,
}
- rObj, err := h.pool.GetObject(ctx, prm)
+ rObj, err := h.frostfs.ReadObject(ctx, prm)
if err != nil {
req.handleFrostFSErr(err, start)
return
@@ -74,11 +76,11 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
dis = "attachment"
}
- payloadSize := rObj.Header.PayloadSize()
+ payloadSize := rObj.Head.PayloadSize()
req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
var contentType string
- for _, attr := range rObj.Header.Attributes() {
+ for _, attr := range rObj.Head.Attributes() {
key := attr.Key()
val := attr.Value()
if !isValidToken(key) || !isValidValue(val) {
@@ -107,7 +109,7 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
}
}
- idsToResponse(&req.Response, &rObj.Header)
+ idsToResponse(&req.Response, rObj.Head)
if len(contentType) == 0 {
// determine the Content-Type from the payload head
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index 1b18755..cea2250 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -15,7 +15,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -98,7 +97,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
}
}
- if err = utils.PrepareExpirationHeader(req, h.pool, filtered, now); err != nil {
+ if err = utils.PrepareExpirationHeader(req, h.frostfs, filtered, now); err != nil {
log.Error(logs.CouldNotPrepareExpirationHeader, zap.Error(err))
response.Error(req, "could not prepare expiration header: "+err.Error(), fasthttp.StatusBadRequest)
return
@@ -132,19 +131,18 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
obj.SetOwnerID(*h.ownerID)
obj.SetAttributes(attributes...)
- var prm pool.PrmObjectPut
- prm.SetHeader(*obj)
- prm.SetPayload(file)
- prm.SetClientCut(h.config.ClientCut())
- prm.SetBufferMaxSize(h.config.BufferMaxSizeForPut())
- prm.WithoutHomomorphicHash(bktInfo.HomomorphicHashDisabled)
-
- bt := h.fetchBearerToken(ctx)
- if bt != nil {
- prm.UseBearer(*bt)
+ prm := PrmObjectCreate{
+ PrmAuth: PrmAuth{
+ BearerToken: h.fetchBearerToken(ctx),
+ },
+ Object: obj,
+ Payload: file,
+ ClientCut: h.config.ClientCut(),
+ WithoutHomomorphicHash: bktInfo.HomomorphicHashDisabled,
+ BufferMaxSize: h.config.BufferMaxSizeForPut(),
}
- if idObj, err = h.pool.PutObject(ctx, prm); err != nil {
+ if idObj, err = h.frostfs.CreateObject(ctx, prm); err != nil {
h.handlePutFrostFSErr(req, err)
return
}
diff --git a/utils/attributes.go b/utils/attributes.go
index cfa3e3a..4d277a9 100644
--- a/utils/attributes.go
+++ b/utils/attributes.go
@@ -11,10 +11,18 @@ import (
"time"
"unicode"
"unicode/utf8"
-
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
)
+type EpochDurations struct {
+ CurrentEpoch uint64
+ MsPerBlock int64
+ BlockPerEpoch uint64
+}
+
+type EpochInfoFetcher interface {
+ GetEpochDurations(context.Context) (*EpochDurations, error)
+}
+
const (
UserAttributeHeaderPrefix = "X-Attribute-"
)
@@ -151,7 +159,7 @@ func title(str string) string {
return string(r0) + str[size:]
}
-func PrepareExpirationHeader(ctx context.Context, p *pool.Pool, headers map[string]string, now time.Time) error {
+func PrepareExpirationHeader(ctx context.Context, epochFetcher EpochInfoFetcher, headers map[string]string, now time.Time) error {
formatsNum := 0
index := -1
for i, transformer := range transformers {
@@ -165,7 +173,7 @@ func PrepareExpirationHeader(ctx context.Context, p *pool.Pool, headers map[stri
case 0:
return nil
case 1:
- epochDuration, err := GetEpochDurations(ctx, p)
+ epochDuration, err := epochFetcher.GetEpochDurations(ctx)
if err != nil {
return fmt.Errorf("couldn't get epoch durations from network info: %w", err)
}
diff --git a/utils/params.go b/utils/params.go
deleted file mode 100644
index f27ff71..0000000
--- a/utils/params.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package utils
-
-import (
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
- "go.uber.org/zap"
-)
-
-type AppParams struct {
- Logger *zap.Logger
- Pool *pool.Pool
- Owner *user.ID
- Resolver *resolver.ContainerResolver
- Cache *cache.BucketCache
-}
diff --git a/utils/util.go b/utils/util.go
index a328769..d513817 100644
--- a/utils/util.go
+++ b/utils/util.go
@@ -2,36 +2,10 @@ package utils
import (
"context"
- "fmt"
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/valyala/fasthttp"
)
-type EpochDurations struct {
- CurrentEpoch uint64
- MsPerBlock int64
- BlockPerEpoch uint64
-}
-
-func GetEpochDurations(ctx context.Context, p *pool.Pool) (*EpochDurations, error) {
- networkInfo, err := p.NetworkInfo(ctx)
- if err != nil {
- return nil, err
- }
-
- res := &EpochDurations{
- CurrentEpoch: networkInfo.CurrentEpoch(),
- MsPerBlock: networkInfo.MsPerBlock(),
- BlockPerEpoch: networkInfo.EpochDuration(),
- }
-
- if res.BlockPerEpoch == 0 {
- return nil, fmt.Errorf("EpochDuration is empty")
- }
- return res, nil
-}
-
// SetContextToRequest adds new context to fasthttp request.
func SetContextToRequest(ctx context.Context, c *fasthttp.RequestCtx) {
c.SetUserValue("context", ctx)
From 27478995b5889cc79bdd5c477f4d97c02d5e4509 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Mon, 24 Jun 2024 16:54:55 +0300
Subject: [PATCH 067/161] [#118] Replace ACLs with polices in readme
Signed-off-by: Alex Vanin
---
README.md | 87 ++++++++++++++++++++++---------------------------------
1 file changed, 35 insertions(+), 52 deletions(-)
diff --git a/README.md b/README.md
index 6e19d31..aa982db 100644
--- a/README.md
+++ b/README.md
@@ -466,13 +466,13 @@ You can always upload files to public containers (open for anyone to put
objects into), but for restricted containers you need to explicitly allow PUT
operations for a request signed with your HTTP Gateway keys.
-If your don't want to manage gateway's secret keys and adjust eACL rules when
+If you don't want to manage gateway's secret keys and adjust policies when
gateway configuration changes (new gate, key rotation, etc) or you plan to use
public services, there is an option to let your application backend (or you) to
-issue Bearer Tokens ans pass them from the client via gate down to FrostFS level
+issue Bearer Tokens and pass them from the client via gate down to FrostFS level
to grant access.
-FrostFS Bearer Token basically is a container owner-signed ACL data (refer to FrostFS
+FrostFS Bearer Token basically is a container owner-signed policy (refer to FrostFS
documentation for more details). There are two options to pass them to gateway:
* "Authorization" header with "Bearer" type and base64-encoded token in
credentials field
@@ -482,33 +482,31 @@ For example, you have a mobile application frontend with a backend part storing
data in FrostFS. When a user authorizes in the mobile app, the backend issues a FrostFS
Bearer token and provides it to the frontend. Then, the mobile app may generate
some data and upload it via any available FrostFS HTTP Gateway by adding
-the corresponding header to the upload request. Accessing the ACL protected data
+the corresponding header to the upload request. Accessing policy protected data
works the same way.
##### Example
-In order to generate a bearer token, you need to have wallet (which will be used to sign the token) and
-the address of the sender who will do the request to FrostFS (in our case, it's a gateway wallet address).
+In order to generate a bearer token, you need to have wallet (which will be used to sign the token)
-Suppose we have:
-* **NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3** (token owner (gateway address))
+1. Suppose you have a container with private policy for wallet key
-Firstly, we need to encode the container id and the sender address to base64 (now it's base58).
-So use **base58** and **base64** utils.
-
-1. Encoding token owner id:
```
-$ echo 'NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3' | base58 --decode | base64
-# output: NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg==
+$ frostfs-cli container create -r --wallet -policy --basic-acl 0 --await
+CID: 9dfzyvq82JnFqp5svxcREf2iy6XNuifYcJPusEDnGK9Z
+
+$ frostfs-cli ape-manager add -r --wallet \
+ --target-type container --target-name 9dfzyvq82JnFqp5svxcREf2iy6XNuifYcJPusEDnGK9Z \
+ --rule "allow Object.* RequestCondition:"\$Actor:publicKey"=03b09baabff3f6107c7e9acb8721a6fc5618d45b50247a314d82e548702cce8cd5 *" \
+ --chain-id
```
-2. Form a Bearer token (10000 is lifetime expiration in epoch) and save it to **bearer.json**:
+
+2. Form a Bearer token (10000 is lifetime expiration in epoch) to impersonate
+ HTTP Gateway request as wallet signed request and save it to **bearer.json**:
```
{
"body": {
"allowImpersonate": true,
- "ownerID": {
- "value": "NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg=="
- },
"lifetime": {
"exp": "10000",
"nbf": "0",
@@ -521,7 +519,7 @@ $ echo 'NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3' | base58 --decode | base64
3. Sign it with the wallet:
```
-$ frostfs-cli util sign bearer-token --from bearer.json --to signed.json -w ./wallet.json
+$ frostfs-cli util sign bearer-token --from bearer.json --to signed.json -w
```
4. Encode to base64 to use in header:
@@ -542,47 +540,32 @@ $ curl -F 'file=@cat.jpeg;filename=cat.jpeg' -H "Authorization: Bearer Ck4KKgoEC
# }
```
-##### Note
-For the token to work correctly, you need to create a container with a basic ACL that:
-1. Allow PUT operation to others
-2. Doesn't set "final" bit
+##### Note: Bearer Token owner
+
+You can specify exact key who can use Bearer Token (gateway wallet address).
+To do this, encode wallet address in base64 format
-For example:
```
-$ frostfs-cli -w ./wallet.json --basic-acl 0x0FFFCFFF -r 192.168.130.72:8080 container create --policy "REP 3" --await
+$ echo 'NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3' | base58 --decode | base64
+# output: NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg==
```
-To deny access to a container without a token, set the eACL rules:
-```
-$ frostfs-cli -w ./wallet.json -r 192.168.130.72:8080 container set-eacl --table eacl.json --await --cid BJeErH9MWmf52VsR1mLWKkgF3pRm3FkubYxM7TZkBP4K
-```
-
-File **eacl.json**:
+Then specify this value in Bearer Token Json
```
{
- "version": {
- "major": 0,
- "minor": 0
- },
- "containerID": {
- "value": "mRnZWzewzxjzIPa7Fqlfqdl3TM1KpJ0YnsXsEhafJJg="
- },
- "records": [
- {
- "operation": "PUT",
- "action": "DENY",
- "filters": [],
- "targets": [
- {
- "role": "OTHERS",
- "keys": []
- }
- ]
- }
- ]
-}
+ "body": {
+ "ownerID": {
+ "value": "NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg=="
+ },
+ ...
```
+##### Note: Policy override
+
+Instead of impersonation, you can define the set of policies that will be applied
+to the request sender. This allows to restrict access to specific operation and
+specific objects without giving full impersonation control to the token user.
+
### Metrics and Pprof
If enabled, Prometheus metrics are available at `localhost:8084` endpoint
From 0f22ca43c126df8deb75145fea115176c78d49fd Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 25 Jun 2024 15:31:46 +0300
Subject: [PATCH 068/161] [#117] Fix FrostFS interface usage
HTTP Gateway expects io.Reader to work with
payload, however `WithPayload` flag reads whole
payload into header object.
Signed-off-by: Alex Vanin
---
internal/frostfs/frostfs.go | 46 ++++++++----------------------------
internal/handler/download.go | 4 +---
internal/handler/handler.go | 6 -----
internal/handler/head.go | 4 +---
internal/handler/reader.go | 4 +---
5 files changed, 13 insertions(+), 51 deletions(-)
diff --git a/internal/frostfs/frostfs.go b/internal/frostfs/frostfs.go
index dde560b..fc41420 100644
--- a/internal/frostfs/frostfs.go
+++ b/internal/frostfs/frostfs.go
@@ -85,43 +85,16 @@ func (x *FrostFS) ReadObject(ctx context.Context, prm handler.PrmObjectRead) (*h
prmGet.UseBearer(*prm.BearerToken)
}
- if prm.WithHeader {
- if prm.WithPayload {
- res, err := x.pool.GetObject(ctx, prmGet)
- if err != nil {
- return nil, handleObjectError("init full object reading via connection pool", err)
- }
+ // The code below must be reworked. It was copied from frostfs-s3-gw
+ // to create similar mocks for unit and fuzzing tests.
+ //
+ // However, this code was changed due to specific of expected responses
+ // from HTTP gateway. HTTP Gateway requires two types of responses:
+ // * payload as io.Reader + HEAD request
+ // * only payload as io.Reader
+ // Therefore all unused params were deleted and code was simplified.
- defer res.Payload.Close()
-
- payload, err := io.ReadAll(res.Payload)
- if err != nil {
- return nil, handleObjectError("read full object payload", err)
- }
-
- res.Header.SetPayload(payload)
-
- return &handler.ObjectPart{
- Head: &res.Header,
- }, nil
- }
-
- var prmHead pool.PrmObjectHead
- prmHead.SetAddress(prm.Address)
-
- if prm.BearerToken != nil {
- prmHead.UseBearer(*prm.BearerToken)
- }
-
- hdr, err := x.pool.HeadObject(ctx, prmHead)
- if err != nil {
- return nil, handleObjectError("read object header via connection pool", err)
- }
-
- return &handler.ObjectPart{
- Head: &hdr,
- }, nil
- } else if prm.PayloadRange[0]+prm.PayloadRange[1] == 0 {
+ if prm.PayloadRange[0]+prm.PayloadRange[1] == 0 {
res, err := x.pool.GetObject(ctx, prmGet)
if err != nil {
return nil, handleObjectError("init full payload range reading via connection pool", err)
@@ -129,6 +102,7 @@ func (x *FrostFS) ReadObject(ctx context.Context, prm handler.PrmObjectRead) (*h
return &handler.ObjectPart{
Payload: res.Payload,
+ Head: &res.Header,
}, nil
}
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 07fe3e9..480254c 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -157,9 +157,7 @@ func (h *Handler) zipObject(ctx context.Context, zipWriter *zip.Writer, addr oid
PrmAuth: PrmAuth{
BearerToken: btoken,
},
- Address: addr,
- WithHeader: true,
- WithPayload: true,
+ Address: addr,
}
resGet, err := h.frostfs.ReadObject(ctx, prm)
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index c87551e..197649e 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -54,12 +54,6 @@ type PrmObjectRead struct {
// Address to read the object header from.
Address oid.Address
- // Flag to read object header.
- WithHeader bool
-
- // Flag to read object payload. False overlaps payload range.
- WithPayload bool
-
// Offset-length range of the object payload to be read.
PayloadRange [2]uint64
}
diff --git a/internal/handler/head.go b/internal/handler/head.go
index 2a17f64..96d1f49 100644
--- a/internal/handler/head.go
+++ b/internal/handler/head.go
@@ -33,8 +33,7 @@ func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid
PrmAuth: PrmAuth{
BearerToken: btoken,
},
- Address: objectAddress,
- WithHeader: true,
+ Address: objectAddress,
}
obj, err := h.frostfs.ReadObject(ctx, prm)
@@ -80,7 +79,6 @@ func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid
BearerToken: btoken,
},
Address: objectAddress,
- WithPayload: true,
PayloadRange: [2]uint64{0, sz},
}
diff --git a/internal/handler/reader.go b/internal/handler/reader.go
index b48ac6d..81694bc 100644
--- a/internal/handler/reader.go
+++ b/internal/handler/reader.go
@@ -59,9 +59,7 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
PrmAuth: PrmAuth{
BearerToken: bearerToken(ctx),
},
- Address: objectAddress,
- WithHeader: true,
- WithPayload: true,
+ Address: objectAddress,
}
rObj, err := h.frostfs.ReadObject(ctx, prm)
From 1737f1d95fdc918ef1d7c8a91075fa9f978c37f6 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 25 Jun 2024 16:12:30 +0300
Subject: [PATCH 069/161] [#117] Update tests
Signed-off-by: Alex Vanin
---
cmd/http-gw/integration_test.go | 1 +
internal/handler/frostfs_mock.go | 1 +
2 files changed, 2 insertions(+)
diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go
index cae40a5..e888ed6 100644
--- a/cmd/http-gw/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -54,6 +54,7 @@ func TestIntegration(t *testing.T) {
versions := []string{
"1.2.7",
"1.3.0",
+ "1.5.0",
}
key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb")
require.NoError(t, err)
diff --git a/internal/handler/frostfs_mock.go b/internal/handler/frostfs_mock.go
index 85ae874..eb59fb6 100644
--- a/internal/handler/frostfs_mock.go
+++ b/internal/handler/frostfs_mock.go
@@ -93,6 +93,7 @@ func (t *TestFrostFS) ReadObject(_ context.Context, prm PrmObjectRead) (*ObjectP
if prm.PayloadRange[0]+prm.PayloadRange[1] > 0 {
off := prm.PayloadRange[0]
payload = payload[off : off+prm.PayloadRange[1]]
+ obj = nil // GetRange does not return header values
}
return &ObjectPart{
From d9cbd302b18be699751bbc40f953be8a0abe41ad Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 25 Jun 2024 19:01:58 +0300
Subject: [PATCH 070/161] [#121] Add canonicalizer
Some headers might be passed in non-canonical way
by proxy servers, such as 'Authorization' header.
Server does not normalize headers, so we can get
custom object attributes. Therefore, app has to normalize
all non object attribute headers by itself.
Signed-off-by: Alex Vanin
---
cmd/http-gw/app.go | 45 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 39 insertions(+), 6 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 1d12122..3d02faa 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -1,6 +1,7 @@
package main
import (
+ "bytes"
"context"
"errors"
"fmt"
@@ -534,15 +535,15 @@ func (a *app) configureRouter(handler *handler.Handler) {
response.Error(r, "Method Not Allowed", fasthttp.StatusMethodNotAllowed)
}
- r.POST("/upload/{cid}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.Upload)))))
+ r.POST("/upload/{cid}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.Upload))))))
a.log.Info(logs.AddedPathUploadCid)
- r.GET("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAddressOrBucketName)))))
- r.HEAD("/get/{cid}/{oid:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAddressOrBucketName)))))
+ r.GET("/get/{cid}/{oid:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAddressOrBucketName))))))
+ r.HEAD("/get/{cid}/{oid:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAddressOrBucketName))))))
a.log.Info(logs.AddedPathGetCidOid)
- r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAttribute)))))
- r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAttribute)))))
+ r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAttribute))))))
+ r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAttribute))))))
a.log.Info(logs.AddedPathGetByAttributeCidAttrKeyAttrVal)
- r.GET("/zip/{cid}/{prefix:*}", a.logger(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadZipped)))))
+ r.GET("/zip/{cid}/{prefix:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadZipped))))))
a.log.Info(logs.AddedPathZipCidPrefix)
a.webServer.Handler = r.Handler
@@ -559,6 +560,38 @@ func (a *app) logger(h fasthttp.RequestHandler) fasthttp.RequestHandler {
}
}
+func (a *app) canonicalizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
+ return func(req *fasthttp.RequestCtx) {
+ // regardless of DisableHeaderNamesNormalizing setting, some headers
+ // MUST be normalized in order to process execution. They are normalized
+ // here.
+
+ toAddKeys := make([][]byte, 0, 10)
+ toAddValues := make([][]byte, 0, 10)
+ prefix := []byte(utils.UserAttributeHeaderPrefix)
+
+ req.Request.Header.VisitAll(func(k, v []byte) {
+ if bytes.HasPrefix(k, prefix) {
+ return
+ }
+ toAddKeys = append(toAddKeys, k)
+ toAddValues = append(toAddValues, v)
+ })
+
+ // this is safe to do after all headers were read into header structure
+ req.Request.Header.EnableNormalizing()
+
+ for i := range toAddKeys {
+ req.Request.Header.SetBytesKV(toAddKeys[i], toAddValues[i])
+ }
+
+ // return normalization setting back
+ req.Request.Header.DisableNormalizing()
+
+ h(req)
+ }
+}
+
func (a *app) tokenizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
appCtx, err := tokens.StoreBearerTokenAppCtx(a.ctx, req)
From 16545bd3b0149367417e6b329d10c377af31d2f8 Mon Sep 17 00:00:00 2001
From: Marina Biryukova
Date: Fri, 5 Jul 2024 13:24:54 +0300
Subject: [PATCH 071/161] [#124] Update SDK version
Signed-off-by: Marina Biryukova
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index a34a092..f9a04b1 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.21
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240705093617-560cbbd1f1e4
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
diff --git a/go.sum b/go.sum
index 0cde076..5baaf63 100644
--- a/go.sum
+++ b/go.sum
@@ -45,8 +45,8 @@ git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSV
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f h1:vBLC1OSGMSn7lRJv/p1of0veifuBdZdztVrF9Vn+UFk=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f/go.mod h1:4AObM67VUqkXQJlODTFThFnuMGEuK8h9DrAXHDZqvCU=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240705093617-560cbbd1f1e4 h1:izmHYpkz7cPr2Zpudxxh0wvrtAIxYywEG+uraghVSlo=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240705093617-560cbbd1f1e4/go.mod h1:4AObM67VUqkXQJlODTFThFnuMGEuK8h9DrAXHDZqvCU=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
From 418767c8ec57e00a238472924751b6d2a2f2c1a8 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Fri, 19 Jul 2024 17:48:01 +0300
Subject: [PATCH 072/161] [#129] Update FrostFS API and remove unused code
Signed-off-by: Alex Vanin
---
go.mod | 49 ++--
go.sum | 278 ++++++----------------
internal/frostfs/services/pool_wrapper.go | 24 --
3 files changed, 99 insertions(+), 252 deletions(-)
diff --git a/go.mod b/go.mod
index f9a04b1..493a003 100644
--- a/go.mod
+++ b/go.mod
@@ -3,31 +3,31 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.21
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240705093617-560cbbd1f1e4
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
- github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc
- github.com/prometheus/client_golang v1.15.1
- github.com/prometheus/client_model v0.3.0
+ github.com/nspcc-dev/neo-go v0.106.2
+ github.com/prometheus/client_golang v1.19.0
+ github.com/prometheus/client_model v0.5.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
github.com/ssgreg/journald v1.0.0
- github.com/stretchr/testify v1.8.3
+ github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.13.0
github.com/valyala/fasthttp v1.34.0
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
- go.uber.org/zap v1.24.0
- golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
+ go.uber.org/zap v1.27.0
+ golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/net v0.23.0
- google.golang.org/grpc v1.61.1
+ google.golang.org/grpc v1.62.0
)
require (
- git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb // indirect
+ git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e // indirect
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
@@ -42,7 +42,7 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/cgroups v1.0.3 // indirect
github.com/containerd/containerd v1.6.2 // indirect
- github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
+ github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
@@ -55,33 +55,32 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
+ github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
- github.com/gorilla/websocket v1.5.0 // indirect
+ github.com/gorilla/websocket v1.5.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
- github.com/hashicorp/golang-lru v0.6.0 // indirect
- github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect
+ github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/klauspost/compress v1.16.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
- github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/sys/mount v0.3.2 // indirect
github.com/moby/sys/mountinfo v0.6.1 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
- github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect
- github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce // indirect
- github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
+ github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect
+ github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d // indirect
+ github.com/nspcc-dev/rfc6979 v0.2.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/common v0.42.0 // indirect
- github.com/prometheus/procfs v0.9.0 // indirect
+ github.com/prometheus/common v0.48.0 // indirect
+ github.com/prometheus/procfs v0.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
@@ -93,6 +92,7 @@ require (
github.com/twmb/murmur3 v1.1.8 // indirect
github.com/urfave/cli v1.22.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
+ go.etcd.io/bbolt v1.3.9 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect
@@ -101,17 +101,16 @@ require (
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
- go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
- golang.org/x/sync v0.5.0 // indirect
+ golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
- google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
+ google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index 5baaf63..73b2798 100644
--- a/go.sum
+++ b/go.sum
@@ -37,16 +37,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3 h1:H5GvrVlowIMWfzqQkhY0p0myooJxQ1sMRVSFfXawwWg=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o=
-git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M=
-git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb/go.mod h1:nkR5gaGeez3Zv2SE7aceP0YwxG2FzIB5cGKpQO2vV2o=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164 h1:XxvwQKJT/f16qS3df5PBQPRYKkhy0/A7zH6644QpKD0=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o=
+git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e h1:kcBqZBiFIUBATUqEuvVigtkJJWQ2Gug/eYXn967o3M4=
+git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
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-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240705093617-560cbbd1f1e4 h1:izmHYpkz7cPr2Zpudxxh0wvrtAIxYywEG+uraghVSlo=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240705093617-560cbbd1f1e4/go.mod h1:4AObM67VUqkXQJlODTFThFnuMGEuK8h9DrAXHDZqvCU=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36 h1:MV/vKJWLQT34RRbXYvkNKFYGNjL5bRNuCQMXkbC7fLI=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36/go.mod h1:vluJ/+yQMcq8ZIZZSA7Te+JKClr0lgtRErjICvb8wto=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
@@ -71,10 +71,6 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0=
-github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig=
-github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I=
-github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
@@ -105,31 +101,20 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
-github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
-github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
-github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs=
-github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
-github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
-github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
-github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
-github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
-github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -138,24 +123,14 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
+github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
+github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
-github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
-github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
-github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
-github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
-github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
-github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
-github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
-github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
-github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
-github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
-github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
-github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
@@ -166,9 +141,7 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
@@ -193,6 +166,10 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
+github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
+github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc=
+github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
@@ -303,8 +280,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
@@ -315,13 +292,11 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
-github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
-github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@@ -363,17 +338,13 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fasthttp/router v1.4.1 h1:3xPUO+hy/HAkgGDSd5sX5w18cyGDIFbC7vip8KwPDk8=
github.com/fasthttp/router v1.4.1/go.mod h1:4P0Kq4C882tA2evBKDW7De7hGfWmvV8FN+zqt8Lu49Q=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
-github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -390,12 +361,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -414,13 +381,11 @@ github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
@@ -438,8 +403,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
-github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
-github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
+github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
+github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -454,7 +419,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -474,12 +438,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -494,7 +455,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
@@ -535,8 +495,8 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
-github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
+github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -553,14 +513,12 @@ github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
-github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU=
-github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
+github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
+github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
+github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
+github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -571,31 +529,22 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
-github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
-github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
@@ -610,7 +559,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -629,21 +577,14 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
-github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
-github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
@@ -652,6 +593,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
+github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
+github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
github.com/moby/sys/mount v0.3.2 h1:uq/CiGDZPvr+c85RYHtKIUORFbmavBUyWH3E1NEyjqI=
@@ -669,74 +612,49 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw=
-github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY=
-github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
-github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
-github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y=
-github.com/nspcc-dev/dbft v0.0.0-20220902113116-58a5e763e647/go.mod h1:g9xisXmX9NP9MjioaTe862n9SlZTrP+6PVUWLBYOr98=
-github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
-github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg=
-github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
-github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU=
-github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg=
-github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM=
-github.com/nspcc-dev/neo-go v0.99.4/go.mod h1:mKTolfRUfKjFso5HPvGSQtUZc70n0VKBMs16eGuC5gA=
-github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc h1:fySIWvUQsitK5e5qYIHnTDCXuPpwzz89SEUEIyY11sg=
-github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc/go.mod h1:s9QhjMC784MWqTURovMbyYduIJc86mnCruxcMiAebpc=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220927123257-24c107e3a262/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce h1:vLGuUNDkmQrWMa4rr4vTd1u8ULqejWxVmNz1L7ocTEI=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg=
-github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
-github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
-github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
-github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
-github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
-github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs=
-github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
-github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40=
-github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
-github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
-github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
+github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk=
+github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
+github.com/nspcc-dev/neo-go v0.106.2 h1:KXSJ2J5Oacc7LrX3r4jvnC8ihKqHs5NB21q4f2S3r9o=
+github.com/nspcc-dev/neo-go v0.106.2/go.mod h1:Ojwfx3/lv0VTeEHMpQ17g0wTnXcCSoFQVq5GEeCZmGo=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
+github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM=
+github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -772,7 +690,6 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -787,32 +704,24 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
-github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
-github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
-github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
+github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
+github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
-github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
-github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
-github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
-github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
+github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
+github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -824,17 +733,14 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
-github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
-github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -859,7 +765,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
@@ -901,15 +806,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
-github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
-github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
@@ -917,7 +820,6 @@ github.com/testcontainers/testcontainers-go v0.13.0 h1:OUujSlEGsXVo/ykPVZk3KanBN
github.com/testcontainers/testcontainers-go v0.13.0/go.mod h1:z1abufU633Eb/FmSBTzV6ntZAC1eZBYPtaFsn4nPuDk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@@ -933,8 +835,6 @@ github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfY
github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4=
github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
-github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo=
-github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
@@ -953,17 +853,15 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
-github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
+go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
+go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -995,23 +893,15 @@ go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJP
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
-go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
-go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
-go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
-go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
-golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1021,15 +911,12 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
@@ -1044,8 +931,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
-golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1070,8 +957,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1119,12 +1006,9 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
@@ -1137,9 +1021,7 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1151,17 +1033,14 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
-golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1178,14 +1057,11 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1213,7 +1089,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1240,23 +1115,19 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
@@ -1280,7 +1151,6 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1302,7 +1172,6 @@ golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDq
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1333,7 +1202,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
-golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -1343,11 +1211,14 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
+golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -1418,12 +1289,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
-google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
-google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
-google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
+google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
+google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
+google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A=
+google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1447,10 +1318,9 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
-google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
+google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
+google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1464,10 +1334,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
-gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1489,6 +1357,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1498,6 +1367,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -1549,6 +1419,8 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
+rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
diff --git a/internal/frostfs/services/pool_wrapper.go b/internal/frostfs/services/pool_wrapper.go
index 039d575..f7b0a26 100644
--- a/internal/frostfs/services/pool_wrapper.go
+++ b/internal/frostfs/services/pool_wrapper.go
@@ -35,30 +35,6 @@ func (n GetNodeByPathResponseInfoWrapper) GetMeta() []tree.Meta {
return res
}
-type GetSubTreeResponseBodyWrapper struct {
- response *grpcService.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() []tree.Meta {
- res := make([]tree.Meta, len(n.response.Meta))
- for i, value := range n.response.Meta {
- res[i] = value
- }
- return res
-}
-
type PoolWrapper struct {
p *treepool.Pool
}
From 9e2d1208cb20a2993de526061f859e2dc0ce3533 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Fri, 19 Jul 2024 17:55:34 +0300
Subject: [PATCH 073/161] [#129] Remove resolver duplicate
Signed-off-by: Alex Vanin
---
cmd/http-gw/app.go | 2 +-
resolver/frostfs.go | 35 -----------------------------------
2 files changed, 1 insertion(+), 36 deletions(-)
delete mode 100644 resolver/frostfs.go
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 3d02faa..561598f 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -223,7 +223,7 @@ func (a *app) initResolver() {
func (a *app) getResolverConfig() ([]string, *resolver.Config) {
resolveCfg := &resolver.Config{
- FrostFS: resolver.NewFrostFSResolver(a.pool),
+ FrostFS: frostfs.NewResolverFrostFS(a.pool),
RPCAddress: a.cfg.GetString(cfgRPCEndpoint),
Settings: a.settings,
}
diff --git a/resolver/frostfs.go b/resolver/frostfs.go
deleted file mode 100644
index aa7a751..0000000
--- a/resolver/frostfs.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package resolver
-
-import (
- "context"
- "errors"
- "fmt"
-
- "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
-)
-
-// FrostFSResolver represents virtual connection to the FrostFS network.
-// It implements resolver.FrostFS.
-type FrostFSResolver struct {
- pool *pool.Pool
-}
-
-// NewFrostFSResolver creates new FrostFSResolver using provided pool.Pool.
-func NewFrostFSResolver(p *pool.Pool) *FrostFSResolver {
- return &FrostFSResolver{pool: p}
-}
-
-// SystemDNS implements resolver.FrostFS interface method.
-func (x *FrostFSResolver) SystemDNS(ctx context.Context) (string, error) {
- networkInfo, err := x.pool.NetworkInfo(ctx)
- if err != nil {
- return "", fmt.Errorf("read network info via client: %w", err)
- }
-
- domain := networkInfo.RawNetworkParameter("SystemDNS")
- if domain == nil {
- return "", errors.New("system DNS parameter not found or empty")
- }
-
- return string(domain), nil
-}
From f20ea67b4663a1a97bbf5bfa122679a8bd1d0247 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 16 Jul 2024 14:58:47 +0300
Subject: [PATCH 074/161] Release v0.30.0
Signed-off-by: Alex Vanin
---
CHANGELOG.md | 38 +++++++++++++++++++++++++++++---------
VERSION | 2 +-
2 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 105ac41..cf47b00 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,19 +4,30 @@ This document outlines major changes between releases.
## [Unreleased]
-## [0.28.1] - 2024-01-24
+## [0.30.0] - Kangshung - 2024-07-22
+
+### Fixed
+- Handle query unescape and invalid bearer token errors (#107)
+- Fix HTTP/2 requests (#110)
### Added
-- Tree pool traversal limit (#92)
- Add new `reconnect_interval` config param (#100)
+- Erasure coding support in placement policy (#114)
+- HTTP Header canonicalizer for well-known headers (#121)
-### Update from 0.28.0
-See new `frostfs.tree_pool_max_attempts` config parameter.
+### Changed
+- Improve test coverage (#112, #117)
+- Bumped vulnerable dependencies (#115)
+- Replace extended ACL examples with policies in README (#118)
+
+### Removed
+
+## [0.29.0] - Zemu - 2024-05-27
### Fixed
- Fix possibility of panic during SIGHUP (#99)
-- Handle query unescape and invalid bearer token errors (#107)
-- Fix HTTP/2 requests (#110)
+- Handle query unescape and invalid bearer token errors (#108)
+- Fix log-level change on SIGHUP (#105)
### Added
- Support client side object cut (#70)
@@ -24,12 +35,19 @@ See new `frostfs.tree_pool_max_attempts` config parameter.
- Add `frostfs.buffer_max_size_for_put` config param
- Add bucket/container caching
- Disable homomorphic hash for PUT if it's disabled in container itself
-- Add new `logger.destination` config param (#89)
+- Add new `logger.destination` config param with journald support (#89, #104)
- Add support namespaces (#91)
### Changed
+- Replace atomics with mutex for reloadable params (#74)
-### Removed
+## [0.28.1] - 2024-01-24
+
+### Added
+- Tree pool traversal limit (#92)
+
+### Update from 0.28.0
+See new `frostfs.tree_pool_max_attempts` config parameter.
## [0.28.0] - Academy of Sciences - 2023-12-07
@@ -100,4 +118,6 @@ To see CHANGELOG for older versions, refer to https://github.com/nspcc-dev/neofs
[0.27.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/72734ab4...v0.27.0
[0.28.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.27.0...v0.28.0
[0.28.1]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.28.0...v0.28.1
-[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.28.1...master
+[0.29.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.28.1...v0.29.0
+[0.30.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.29.0...v0.30.0
+[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.30.0...master
diff --git a/VERSION b/VERSION
index 244df55..9388ecb 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.28.1
+v0.30.0
From fcf99d9a599a6dd39b7fc8a5c0cd10970c8255eb Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Mon, 15 Jul 2024 16:35:08 +0300
Subject: [PATCH 075/161] [#127] Split FrostFS ReadObject to separate methods
Signed-off-by: Denis Kirillov
---
internal/frostfs/frostfs.go | 56 ++++++++++++++++++--------------
internal/handler/download.go | 6 ++--
internal/handler/frostfs_mock.go | 51 +++++++++++++++++++----------
internal/handler/handler.go | 34 +++++++++++++++----
internal/handler/head.go | 20 +++++-------
internal/handler/reader.go | 10 +++---
6 files changed, 109 insertions(+), 68 deletions(-)
diff --git a/internal/frostfs/frostfs.go b/internal/frostfs/frostfs.go
index fc41420..2610564 100644
--- a/internal/frostfs/frostfs.go
+++ b/internal/frostfs/frostfs.go
@@ -11,6 +11,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"google.golang.org/grpc/codes"
@@ -76,8 +77,25 @@ func (x payloadReader) Read(p []byte) (int, error) {
return n, handleObjectError("read payload", err)
}
-// ReadObject implements frostfs.FrostFS interface method.
-func (x *FrostFS) ReadObject(ctx context.Context, prm handler.PrmObjectRead) (*handler.ObjectPart, error) {
+// HeadObject implements frostfs.FrostFS interface method.
+func (x *FrostFS) HeadObject(ctx context.Context, prm handler.PrmObjectHead) (*object.Object, error) {
+ var prmHead pool.PrmObjectHead
+ prmHead.SetAddress(prm.Address)
+
+ if prm.BearerToken != nil {
+ prmHead.UseBearer(*prm.BearerToken)
+ }
+
+ res, err := x.pool.HeadObject(ctx, prmHead)
+ if err != nil {
+ return nil, handleObjectError("read object header via connection pool", err)
+ }
+
+ return &res, nil
+}
+
+// GetObject implements frostfs.FrostFS interface method.
+func (x *FrostFS) GetObject(ctx context.Context, prm handler.PrmObjectGet) (*handler.Object, error) {
var prmGet pool.PrmObjectGet
prmGet.SetAddress(prm.Address)
@@ -85,27 +103,19 @@ func (x *FrostFS) ReadObject(ctx context.Context, prm handler.PrmObjectRead) (*h
prmGet.UseBearer(*prm.BearerToken)
}
- // The code below must be reworked. It was copied from frostfs-s3-gw
- // to create similar mocks for unit and fuzzing tests.
- //
- // However, this code was changed due to specific of expected responses
- // from HTTP gateway. HTTP Gateway requires two types of responses:
- // * payload as io.Reader + HEAD request
- // * only payload as io.Reader
- // Therefore all unused params were deleted and code was simplified.
-
- if prm.PayloadRange[0]+prm.PayloadRange[1] == 0 {
- res, err := x.pool.GetObject(ctx, prmGet)
- if err != nil {
- return nil, handleObjectError("init full payload range reading via connection pool", err)
- }
-
- return &handler.ObjectPart{
- Payload: res.Payload,
- Head: &res.Header,
- }, nil
+ res, err := x.pool.GetObject(ctx, prmGet)
+ if err != nil {
+ return nil, handleObjectError("init full object reading via connection pool", err)
}
+ return &handler.Object{
+ Header: res.Header,
+ Payload: res.Payload,
+ }, nil
+}
+
+// RangeObject implements frostfs.FrostFS interface method.
+func (x *FrostFS) RangeObject(ctx context.Context, prm handler.PrmObjectRange) (io.ReadCloser, error) {
var prmRange pool.PrmObjectRange
prmRange.SetAddress(prm.Address)
prmRange.SetOffset(prm.PayloadRange[0])
@@ -120,9 +130,7 @@ func (x *FrostFS) ReadObject(ctx context.Context, prm handler.PrmObjectRead) (*h
return nil, handleObjectError("init payload range reading via connection pool", err)
}
- return &handler.ObjectPart{
- Payload: payloadReader{&res},
- }, nil
+ return payloadReader{&res}, nil
}
// SearchObjects implements frostfs.FrostFS interface method.
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 480254c..88109a6 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -153,19 +153,19 @@ func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
}
func (h *Handler) zipObject(ctx context.Context, zipWriter *zip.Writer, addr oid.Address, btoken *bearer.Token, bufZip []byte) error {
- prm := PrmObjectRead{
+ prm := PrmObjectGet{
PrmAuth: PrmAuth{
BearerToken: btoken,
},
Address: addr,
}
- resGet, err := h.frostfs.ReadObject(ctx, prm)
+ resGet, err := h.frostfs.GetObject(ctx, prm)
if err != nil {
return fmt.Errorf("get FrostFS object: %v", err)
}
- objWriter, err := h.addObjectToZip(zipWriter, resGet.Head)
+ objWriter, err := h.addObjectToZip(zipWriter, &resGet.Header)
if err != nil {
return fmt.Errorf("zip create header: %v", err)
}
diff --git a/internal/handler/frostfs_mock.go b/internal/handler/frostfs_mock.go
index eb59fb6..9f4378a 100644
--- a/internal/handler/frostfs_mock.go
+++ b/internal/handler/frostfs_mock.go
@@ -78,32 +78,49 @@ func (t *TestFrostFS) requestOwner(btoken *bearer.Token) user.ID {
return owner
}
-func (t *TestFrostFS) ReadObject(_ context.Context, prm PrmObjectRead) (*ObjectPart, error) {
- sAddr := prm.Address.EncodeToString()
+func (t *TestFrostFS) retrieveObject(addr oid.Address, btoken *bearer.Token) (*object.Object, error) {
+ sAddr := addr.EncodeToString()
if obj, ok := t.objects[sAddr]; ok {
- owner := t.requestOwner(prm.BearerToken)
+ owner := t.requestOwner(btoken)
- if !t.isAllowed(prm.Address.Container(), owner, acl.OpObjectGet, prm.Address.Object()) {
+ if !t.isAllowed(addr.Container(), owner, acl.OpObjectGet, addr.Object()) {
return nil, ErrAccessDenied
}
- payload := obj.Payload()
-
- if prm.PayloadRange[0]+prm.PayloadRange[1] > 0 {
- off := prm.PayloadRange[0]
- payload = payload[off : off+prm.PayloadRange[1]]
- obj = nil // GetRange does not return header values
- }
-
- return &ObjectPart{
- Head: obj,
- Payload: io.NopCloser(bytes.NewReader(payload)),
- }, nil
+ return obj, nil
}
- return nil, fmt.Errorf("%w: %s", &apistatus.ObjectNotFound{}, prm.Address)
+ return nil, fmt.Errorf("%w: %s", &apistatus.ObjectNotFound{}, addr)
}
+
+func (t *TestFrostFS) HeadObject(_ context.Context, prm PrmObjectHead) (*object.Object, error) {
+ return t.retrieveObject(prm.Address, prm.BearerToken)
+}
+
+func (t *TestFrostFS) GetObject(_ context.Context, prm PrmObjectGet) (*Object, error) {
+ obj, err := t.retrieveObject(prm.Address, prm.BearerToken)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Object{
+ Header: *obj,
+ Payload: io.NopCloser(bytes.NewReader(obj.Payload())),
+ }, nil
+}
+
+func (t *TestFrostFS) RangeObject(_ context.Context, prm PrmObjectRange) (io.ReadCloser, error) {
+ obj, err := t.retrieveObject(prm.Address, prm.BearerToken)
+ if err != nil {
+ return nil, err
+ }
+
+ off := prm.PayloadRange[0]
+ payload := obj.Payload()[off : off+prm.PayloadRange[1]]
+ return io.NopCloser(bytes.NewReader(payload)), nil
+}
+
func (t *TestFrostFS) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID, error) {
b := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 197649e..0bbcdb9 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -46,8 +46,26 @@ type PrmAuth struct {
BearerToken *bearer.Token
}
-// PrmObjectRead groups parameters of FrostFS.ReadObject operation.
-type PrmObjectRead struct {
+// PrmObjectHead groups parameters of FrostFS.HeadObject operation.
+type PrmObjectHead struct {
+ // Authentication parameters.
+ PrmAuth
+
+ // Address to read the object header from.
+ Address oid.Address
+}
+
+// PrmObjectGet groups parameters of FrostFS.GetObject operation.
+type PrmObjectGet struct {
+ // Authentication parameters.
+ PrmAuth
+
+ // Address to read the object header from.
+ Address oid.Address
+}
+
+// PrmObjectRange groups parameters of FrostFS.RangeObject operation.
+type PrmObjectRange struct {
// Authentication parameters.
PrmAuth
@@ -58,10 +76,10 @@ type PrmObjectRead struct {
PayloadRange [2]uint64
}
-// ObjectPart represents partially read FrostFS object.
-type ObjectPart struct {
- // Object header with optional in-memory payload part.
- Head *object.Object
+// Object represents FrostFS object.
+type Object struct {
+ // Object header (doesn't contain payload).
+ Header object.Object
// Object payload part encapsulated in io.Reader primitive.
// Returns ErrAccessDenied on read access violation.
@@ -115,7 +133,9 @@ var (
// FrostFS represents virtual connection to FrostFS network.
type FrostFS interface {
Container(context.Context, PrmContainer) (*container.Container, error)
- ReadObject(context.Context, PrmObjectRead) (*ObjectPart, error)
+ HeadObject(context.Context, PrmObjectHead) (*object.Object, error)
+ GetObject(context.Context, PrmObjectGet) (*Object, error)
+ RangeObject(context.Context, PrmObjectRange) (io.ReadCloser, error)
CreateObject(context.Context, PrmObjectCreate) (oid.ID, error)
SearchObjects(context.Context, PrmObjectSearch) (ResObjectSearch, error)
utils.EpochInfoFetcher
diff --git a/internal/handler/head.go b/internal/handler/head.go
index 96d1f49..f0a1e94 100644
--- a/internal/handler/head.go
+++ b/internal/handler/head.go
@@ -29,22 +29,22 @@ func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid
btoken := bearerToken(ctx)
- prm := PrmObjectRead{
+ prm := PrmObjectHead{
PrmAuth: PrmAuth{
BearerToken: btoken,
},
Address: objectAddress,
}
- obj, err := h.frostfs.ReadObject(ctx, prm)
+ obj, err := h.frostfs.HeadObject(ctx, prm)
if err != nil {
req.handleFrostFSErr(err, start)
return
}
- req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(obj.Head.PayloadSize(), 10))
+ req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(obj.PayloadSize(), 10))
var contentType string
- for _, attr := range obj.Head.Attributes() {
+ for _, attr := range obj.Attributes() {
key := attr.Key()
val := attr.Value()
if !isValidToken(key) || !isValidValue(val) {
@@ -70,11 +70,11 @@ func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid
}
}
- idsToResponse(&req.Response, obj.Head)
+ idsToResponse(&req.Response, obj)
if len(contentType) == 0 {
- contentType, _, err = readContentType(obj.Head.PayloadSize(), func(sz uint64) (io.Reader, error) {
- prmRange := PrmObjectRead{
+ contentType, _, err = readContentType(obj.PayloadSize(), func(sz uint64) (io.Reader, error) {
+ prmRange := PrmObjectRange{
PrmAuth: PrmAuth{
BearerToken: btoken,
},
@@ -82,11 +82,7 @@ func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid
PayloadRange: [2]uint64{0, sz},
}
- resObj, err := h.frostfs.ReadObject(ctx, prmRange)
- if err != nil {
- return nil, err
- }
- return resObj.Payload, nil
+ return h.frostfs.RangeObject(ctx, prmRange)
})
if err != nil && err != io.EOF {
req.handleFrostFSErr(err, start)
diff --git a/internal/handler/reader.go b/internal/handler/reader.go
index 81694bc..65d258b 100644
--- a/internal/handler/reader.go
+++ b/internal/handler/reader.go
@@ -55,14 +55,14 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
filename string
)
- prm := PrmObjectRead{
+ prm := PrmObjectGet{
PrmAuth: PrmAuth{
BearerToken: bearerToken(ctx),
},
Address: objectAddress,
}
- rObj, err := h.frostfs.ReadObject(ctx, prm)
+ rObj, err := h.frostfs.GetObject(ctx, prm)
if err != nil {
req.handleFrostFSErr(err, start)
return
@@ -74,11 +74,11 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
dis = "attachment"
}
- payloadSize := rObj.Head.PayloadSize()
+ payloadSize := rObj.Header.PayloadSize()
req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
var contentType string
- for _, attr := range rObj.Head.Attributes() {
+ for _, attr := range rObj.Header.Attributes() {
key := attr.Key()
val := attr.Value()
if !isValidToken(key) || !isValidValue(val) {
@@ -107,7 +107,7 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
}
}
- idsToResponse(&req.Response, rObj.Head)
+ idsToResponse(&req.Response, &rObj.Header)
if len(contentType) == 0 {
// determine the Content-Type from the payload head
From 5ee09790f05cc70cd0bf274d996435ba03a56554 Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Fri, 16 Aug 2024 12:56:38 +0300
Subject: [PATCH 076/161] [#126] Fix docker warnings
Signed-off-by: Roman Loginov
---
.docker/Dockerfile | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.docker/Dockerfile b/.docker/Dockerfile
index d39fba1..f45c864 100644
--- a/.docker/Dockerfile
+++ b/.docker/Dockerfile
@@ -1,9 +1,9 @@
-FROM golang:1.22-alpine as basebuilder
+FROM golang:1.22-alpine AS basebuilder
RUN apk add --update make bash ca-certificates
-FROM basebuilder as builder
-ENV GOGC off
-ENV CGO_ENABLED 0
+FROM basebuilder AS builder
+ENV GOGC=off
+ENV CGO_ENABLED=0
ARG BUILD=now
ARG VERSION=dev
ARG REPO=repository
From 151e5bc1c835a62f1d950647031109b4f02530f8 Mon Sep 17 00:00:00 2001
From: Nikita Zinkevich
Date: Fri, 23 Aug 2024 13:19:08 +0300
Subject: [PATCH 077/161] [#132] Update Go version
Signed-off-by: Nikita Zinkevich
---
.forgejo/workflows/builds.yml | 2 +-
.forgejo/workflows/dco.yml | 2 +-
.forgejo/workflows/tests.yml | 6 +++---
.golangci.yml | 3 ++-
.pre-commit-config.yaml | 5 -----
CHANGELOG.md | 3 +++
Makefile | 4 ++--
go.mod | 2 +-
internal/handler/reader_test.go | 2 +-
utils/tracing.go | 4 ++--
10 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/.forgejo/workflows/builds.yml b/.forgejo/workflows/builds.yml
index 17f1f2e..490a97c 100644
--- a/.forgejo/workflows/builds.yml
+++ b/.forgejo/workflows/builds.yml
@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.21', '1.22' ]
+ go_versions: [ '1.22', '1.23' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml
index eb23ec5..4acd633 100644
--- a/.forgejo/workflows/dco.yml
+++ b/.forgejo/workflows/dco.yml
@@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v3
with:
- go-version: '1.21'
+ go-version: '1.23'
- name: Run commit format checker
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index 74e0b2c..db7f986 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -10,7 +10,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
- go-version: '1.22'
+ go-version: '1.23'
cache: true
- name: Install linters
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.21', '1.22' ]
+ go_versions: [ '1.22', '1.23' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
@@ -38,4 +38,4 @@ jobs:
run: make dep
- name: Run tests
- run: make test
\ No newline at end of file
+ run: make test
diff --git a/.golangci.yml b/.golangci.yml
index 5459bde..d9f93eb 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,7 +12,8 @@ run:
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
- format: tab
+ formats:
+ - format: tab
# all available settings of specific linters
linters-settings:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e97fc23..3c963be 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -30,11 +30,6 @@ repos:
hooks:
- id: shellcheck
- - repo: https://github.com/golangci/golangci-lint
- rev: v1.51.2
- hooks:
- - id: golangci-lint
-
- repo: local
hooks:
- id: make-lint-install
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf47b00..b322c96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ This document outlines major changes between releases.
## [Unreleased]
+### Changed
+- Update go version to 1.22 (#132)
+
## [0.30.0] - Kangshung - 2024-07-22
### Fixed
diff --git a/Makefile b/Makefile
index 04cfea4..372b89b 100755
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,8 @@
REPO ?= $(shell go list -m)
VERSION ?= $(shell git describe --tags --match "v*" --dirty --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
GO_VERSION ?= 1.22
-LINT_VERSION ?= 1.54.0
-TRUECLOUDLAB_LINT_VERSION ?= 0.0.2
+LINT_VERSION ?= 1.60.3
+TRUECLOUDLAB_LINT_VERSION ?= 0.0.6
BUILD ?= $(shell date -u --iso=seconds)
HUB_IMAGE ?= truecloudlab/frostfs-http-gw
diff --git a/go.mod b/go.mod
index 493a003..4e4bf9c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module git.frostfs.info/TrueCloudLab/frostfs-http-gw
-go 1.21
+go 1.22
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164
diff --git a/internal/handler/reader_test.go b/internal/handler/reader_test.go
index 73899ca..c63a734 100644
--- a/internal/handler/reader_test.go
+++ b/internal/handler/reader_test.go
@@ -35,7 +35,7 @@ func TestDetector(t *testing.T) {
} {
t.Run(tc.Name, func(t *testing.T) {
contentType, data, err := readContentType(uint64(len(tc.Expected)),
- func(sz uint64) (io.Reader, error) {
+ func(uint64) (io.Reader, error) {
return strings.NewReader(tc.Expected), nil
},
)
diff --git a/utils/tracing.go b/utils/tracing.go
index 14c059a..c8e467d 100644
--- a/utils/tracing.go
+++ b/utils/tracing.go
@@ -30,12 +30,12 @@ func (c *httpCarrier) Set(key string, value string) {
func (c *httpCarrier) Keys() []string {
dict := make(map[string]interface{})
c.r.Request.Header.VisitAll(
- func(key, value []byte) {
+ func(key, _ []byte) {
dict[string(key)] = true
},
)
c.r.Response.Header.VisitAll(
- func(key, value []byte) {
+ func(key, _ []byte) {
dict[string(key)] = true
},
)
From ca426fff4df117b5ce119d7a5f123b11cc99a7a1 Mon Sep 17 00:00:00 2001
From: Roman Ognev
Date: Fri, 30 Aug 2024 20:06:01 +0300
Subject: [PATCH 078/161] [#135] Add fuzzing tests for handlers
Signed-off-by: Roman Ognev
---
Makefile | 36 +-
README.md | 23 +
go.mod | 1 +
go.sum | 2 +
internal/handler/handler_fuzz_test.go | 580 ++++++++++++++++++++++++++
5 files changed, 641 insertions(+), 1 deletion(-)
create mode 100644 internal/handler/handler_fuzz_test.go
diff --git a/Makefile b/Makefile
index 372b89b..c1f4f50 100755
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,11 @@ PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
sed "s/-/~/")-${OS_RELEASE}
.PHONY: debpackage debclean
+FUZZ_NGFUZZ_DIR ?= ""
+FUZZ_TIMEOUT ?= 30
+FUZZ_FUNCTIONS ?= "all"
+FUZZ_AUX ?= ""
+
# Make all binaries
all: $(BINS)
$(BINS): $(DIRS) dep
@@ -78,6 +83,35 @@ cover:
@go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic
@go tool cover -html=coverage.txt -o coverage.html
+# Run fuzzing
+CLANG := $(shell which clang-17 2>/dev/null)
+.PHONY: check-clang all
+check-clang:
+ifeq ($(CLANG),)
+ @echo "clang-17 is not installed. Please install it before proceeding - https://apt.llvm.org/llvm.sh "
+ @exit 1
+endif
+
+.PHONY: check-ngfuzz all
+check-ngfuzz:
+ @if [ -z "$(FUZZ_NGFUZZ_DIR)" ]; then \
+ echo "Please set a variable FUZZ_NGFUZZ_DIR to specify path to the ngfuzz"; \
+ exit 1; \
+ fi
+
+.PHONY: install-fuzzing-deps
+install-fuzzing-deps: check-clang check-ngfuzz
+
+.PHONY: fuzz
+fuzz: install-fuzzing-deps
+ @START_PATH=$$(pwd); \
+ ROOT_PATH=$$(realpath --relative-to=$(FUZZ_NGFUZZ_DIR) $$START_PATH) ; \
+ cd $(FUZZ_NGFUZZ_DIR) && \
+ ./ngfuzz -clean && \
+ ./ngfuzz -fuzz $(FUZZ_FUNCTIONS) -rootdir $$ROOT_PATH -timeout $(FUZZ_TIMEOUT) $(FUZZ_AUX) && \
+ ./ngfuzz -report
+
+
# Reformat code
fmt:
@echo "⇒ Processing gofmt check"
@@ -149,7 +183,7 @@ version:
# Clean up
clean:
rm -rf vendor
- rm -rf $(BINDIR)
+ rm -rf $(BINDIR)
# Package for Debian
debpackage:
diff --git a/README.md b/README.md
index aa982db..019b8ff 100644
--- a/README.md
+++ b/README.md
@@ -575,3 +575,26 @@ See [configuration](./docs/gate-configuration.md).
## Credits
Please see [CREDITS](CREDITS.md) for details.
+
+## Fuzzing
+
+To run fuzzing tests use the following command:
+
+```shell
+$ make fuzz
+```
+
+This command will install dependencies for the fuzzing process and run existing fuzzing tests.
+
+You can also use the following arguments:
+
+```
+FUZZ_TIMEOUT - time to run each fuzzing test (default 30)
+FUZZ_FUNCTIONS - fuzzing tests that will be started (default "all")
+FUZZ_AUX - additional parameters for the fuzzer (for example, "-debug")
+FUZZ_NGFUZZ_DIR - path to ngfuzz tool
+````
+
+## Credits
+
+Please see [CREDITS](CREDITS.md) for details.
diff --git a/go.mod b/go.mod
index 4e4bf9c..accedfb 100644
--- a/go.mod
+++ b/go.mod
@@ -17,6 +17,7 @@ require (
github.com/ssgreg/journald v1.0.0
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.13.0
+ github.com/trailofbits/go-fuzz-utils v0.0.0-20230413173806-58c38daa3cb4
github.com/valyala/fasthttp v1.34.0
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
diff --git a/go.sum b/go.sum
index 73b2798..07eccfa 100644
--- a/go.sum
+++ b/go.sum
@@ -820,6 +820,8 @@ github.com/testcontainers/testcontainers-go v0.13.0 h1:OUujSlEGsXVo/ykPVZk3KanBN
github.com/testcontainers/testcontainers-go v0.13.0/go.mod h1:z1abufU633Eb/FmSBTzV6ntZAC1eZBYPtaFsn4nPuDk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/trailofbits/go-fuzz-utils v0.0.0-20230413173806-58c38daa3cb4 h1:GpfJ7OdNjS7BFTVwNCUI9L4aCJOFRbr5fdHqjdhoYE8=
+github.com/trailofbits/go-fuzz-utils v0.0.0-20230413173806-58c38daa3cb4/go.mod h1:f3jBhpWvuZmue0HZK52GzRHJOYHYSILs/c8+K2S/J+o=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
diff --git a/internal/handler/handler_fuzz_test.go b/internal/handler/handler_fuzz_test.go
new file mode 100644
index 0000000..ad2ae6e
--- /dev/null
+++ b/internal/handler/handler_fuzz_test.go
@@ -0,0 +1,580 @@
+//go:build gofuzz
+// +build gofuzz
+
+package handler
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "mime/multipart"
+ "net/http"
+ "testing"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
+ go_fuzz_utils "github.com/trailofbits/go-fuzz-utils"
+ "github.com/valyala/fasthttp"
+)
+
+const (
+ fuzzSuccessExitCode = 0
+ fuzzFailExitCode = -1
+)
+
+func prepareStrings(tp *go_fuzz_utils.TypeProvider, count int) ([]string, error) {
+ array := make([]string, count)
+ var err error
+
+ for i := 0; i < count; i++ {
+ err = tp.Reset()
+ if err != nil {
+ return nil, err
+ }
+
+ array[i], err = tp.GetString()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return array, nil
+}
+
+func prepareBools(tp *go_fuzz_utils.TypeProvider, count int) ([]bool, error) {
+ array := make([]bool, count)
+ var err error
+
+ for i := 0; i < count; i++ {
+ err = tp.Reset()
+ if err != nil {
+ return nil, err
+ }
+
+ array[i], err = tp.GetBool()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return array, nil
+}
+
+func getRandomDeterministicPositiveIntInRange(tp *go_fuzz_utils.TypeProvider, max int) (int, error) {
+ count, err := tp.GetInt()
+ if err != nil {
+ return -1, err
+ }
+ count = count % max
+ if count < 0 {
+ count += max
+ }
+ return count, nil
+}
+
+func generateHeaders(tp *go_fuzz_utils.TypeProvider, r *fasthttp.Request, params []string) error {
+ count, err := tp.GetInt()
+ if err != nil {
+ return err
+ }
+ count = count % len(params)
+ if count < 0 {
+ count += len(params)
+ }
+
+ for i := 0; i < count; i++ {
+ position, err := tp.GetInt()
+ if err != nil {
+ return err
+ }
+ position = position % len(params)
+ if position < 0 {
+ position += len(params)
+ }
+
+ v, err := tp.GetString()
+ if err != nil {
+ return err
+ }
+
+ r.Header.Set(params[position], v)
+
+ }
+
+ return nil
+}
+
+func maybeFillRandom(tp *go_fuzz_utils.TypeProvider, initValue string) (string, error) {
+ rnd, err := tp.GetBool()
+ if err != nil {
+ return "", err
+ }
+ if rnd == true {
+ initValue, err = tp.GetString()
+ if err != nil {
+ return "", err
+ }
+ }
+ return initValue, nil
+}
+
+func upload(tp *go_fuzz_utils.TypeProvider) (context.Context, *handlerContext, cid.ID, *fasthttp.RequestCtx, string, string, string, error) {
+ hc, err := prepareHandlerContext()
+ if err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+
+ aclList := []acl.Basic{
+ acl.Private,
+ acl.PrivateExtended,
+ acl.PublicRO,
+ acl.PublicROExtended,
+ acl.PublicRW,
+ acl.PublicRWExtended,
+ acl.PublicAppend,
+ acl.PublicAppendExtended,
+ }
+
+ pos, err := getRandomDeterministicPositiveIntInRange(tp, len(aclList))
+ if err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+ acl := aclList[pos]
+
+ strings, err := prepareStrings(tp, 6)
+ if err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+ bktName := strings[0]
+ objFileName := strings[1]
+ valAttr := strings[2]
+ keyAttr := strings[3]
+
+ if len(bktName) == 0 {
+ return nil, nil, cid.ID{}, nil, "", "", "", errors.New("not enought buckets")
+ }
+
+ cnrID, cnr, err := hc.prepareContainer(bktName, acl)
+ if err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+
+ hc.frostfs.SetContainer(cnrID, cnr)
+
+ ctx := context.Background()
+ ctx = middleware.SetNamespace(ctx, "")
+
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", cnrID.EncodeToString())
+
+ attributes := map[string]string{
+ object.AttributeFileName: objFileName,
+ keyAttr: valAttr,
+ }
+
+ var buff bytes.Buffer
+ w := multipart.NewWriter(&buff)
+ fw, err := w.CreateFormFile("file", attributes[object.AttributeFileName])
+ if err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+
+ content, err := tp.GetBytes()
+ if err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+
+ if _, err = io.Copy(fw, bytes.NewReader(content)); err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+
+ if err = w.Close(); err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+
+ r.Request.SetBodyStream(&buff, buff.Len())
+ r.Request.Header.Set("Content-Type", w.FormDataContentType())
+ r.Request.Header.Set("X-Attribute-"+keyAttr, valAttr)
+
+ err = generateHeaders(tp, &r.Request, []string{"X-Attribute-", "X-Attribute-DupKey", "X-Attribute-MyAttribute", "X-Attribute-System-DupKey", "X-Attribute-System-Expiration-Epoch1", "X-Attribute-SYSTEM-Expiration-Epoch2", "X-Attribute-system-Expiration-Epoch3", "X-Attribute-User-Attribute", "X-Attribute-", "X-Attribute-FileName", "X-Attribute-FROSTFS", "X-Attribute-neofs", "X-Attribute-SYSTEM", "X-Attribute-System-Expiration-Duration", "X-Attribute-System-Expiration-Epoch", "X-Attribute-System-Expiration-RFC3339", "X-Attribute-System-Expiration-Timestamp", "X-Attribute-Timestamp", "X-Attribute-" + strings[4], "X-Attribute-System-" + strings[5]})
+ if err != nil {
+ return nil, nil, cid.ID{}, nil, "", "", "", err
+ }
+
+ hc.Handler().Upload(r)
+
+ if r.Response.StatusCode() != http.StatusOK {
+ return nil, nil, cid.ID{}, nil, "", "", "", errors.New("error on upload")
+ }
+
+ return ctx, hc, cnrID, r, objFileName, keyAttr, valAttr, nil
+}
+
+func InitFuzzUpload() {
+
+}
+
+func DoFuzzUpload(input []byte) int {
+ // FUZZER INIT
+ if len(input) < 100 {
+ return fuzzFailExitCode
+ }
+
+ tp, err := go_fuzz_utils.NewTypeProvider(input)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ _, _, _, _, _, _, _, err = upload(tp)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ return fuzzSuccessExitCode
+}
+
+func FuzzUpload(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ DoFuzzUpload(data)
+ })
+}
+
+func downloadOrHead(tp *go_fuzz_utils.TypeProvider, ctx context.Context, hc *handlerContext, cnrID cid.ID, resp *fasthttp.RequestCtx, filename string) (*fasthttp.RequestCtx, error) {
+
+ var putRes putResponse
+
+ defer func() {
+ if r := recover(); r != nil {
+ panic(resp)
+ }
+ }()
+
+ data := resp.Response.Body()
+ err := json.Unmarshal(data, &putRes)
+
+ if err != nil {
+ return nil, err
+ }
+
+ obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID]
+ attr := object.NewAttribute()
+ attr.SetKey(object.AttributeFilePath)
+
+ filename, err = maybeFillRandom(tp, filename)
+ if err != nil {
+ return nil, err
+ }
+
+ attr.SetValue(filename)
+ obj.SetAttributes(append(obj.Attributes(), *attr)...)
+
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+
+ cid := cnrID.EncodeToString()
+ cid, err = maybeFillRandom(tp, cid)
+ if err != nil {
+ return nil, err
+ }
+ oid := putRes.ObjectID
+ oid, err = maybeFillRandom(tp, oid)
+ if err != nil {
+ return nil, err
+ }
+ r.SetUserValue("cid", cid)
+ r.SetUserValue("oid", oid)
+
+ rnd, err := tp.GetBool()
+ if err != nil {
+ return nil, err
+ }
+ if rnd == true {
+ r.SetUserValue("download", "true")
+ }
+
+ return r, nil
+}
+
+func InitFuzzGet() {
+
+}
+
+func DoFuzzGet(input []byte) int {
+ // FUZZER INIT
+ if len(input) < 100 {
+ return fuzzFailExitCode
+ }
+
+ tp, err := go_fuzz_utils.NewTypeProvider(input)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ ctx, hc, cnrID, resp, filename, _, _, err := upload(tp)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ r, err := downloadOrHead(tp, ctx, hc, cnrID, resp, filename)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ hc.Handler().DownloadByAddressOrBucketName(r)
+
+ return fuzzSuccessExitCode
+}
+
+func FuzzGet(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ DoFuzzUpload(data)
+ })
+}
+
+func InitFuzzHead() {
+
+}
+
+func DoFuzzHead(input []byte) int {
+ // FUZZER INIT
+ if len(input) < 100 {
+ return fuzzFailExitCode
+ }
+
+ tp, err := go_fuzz_utils.NewTypeProvider(input)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ ctx, hc, cnrID, resp, filename, _, _, err := upload(tp)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ r, err := downloadOrHead(tp, ctx, hc, cnrID, resp, filename)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ hc.Handler().HeadByAddressOrBucketName(r)
+
+ return fuzzSuccessExitCode
+}
+
+func FuzzHead(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ DoFuzzHead(data)
+ })
+}
+
+func InitFuzzDownloadByAttribute() {
+
+}
+
+func DoFuzzDownloadByAttribute(input []byte) int {
+ // FUZZER INIT
+ if len(input) < 100 {
+ return fuzzFailExitCode
+ }
+
+ tp, err := go_fuzz_utils.NewTypeProvider(input)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ ctx, hc, cnrID, _, _, attrKey, attrVal, err := upload(tp)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ cid := cnrID.EncodeToString()
+ cid, err = maybeFillRandom(tp, cid)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ attrKey, err = maybeFillRandom(tp, attrKey)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ attrVal, err = maybeFillRandom(tp, attrVal)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", cid)
+ r.SetUserValue("attr_key", attrKey)
+ r.SetUserValue("attr_val", attrVal)
+
+ hc.Handler().DownloadByAttribute(r)
+
+ return fuzzSuccessExitCode
+}
+
+func FuzzDownloadByAttribute(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ DoFuzzDownloadByAttribute(data)
+ })
+}
+
+func InitFuzzHeadByAttribute() {
+
+}
+
+func DoFuzzHeadByAttribute(input []byte) int {
+ // FUZZER INIT
+ if len(input) < 100 {
+ return fuzzFailExitCode
+ }
+
+ tp, err := go_fuzz_utils.NewTypeProvider(input)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ ctx, hc, cnrID, _, _, attrKey, attrVal, err := upload(tp)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ cid := cnrID.EncodeToString()
+ cid, err = maybeFillRandom(tp, cid)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ attrKey, err = maybeFillRandom(tp, attrKey)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ attrVal, err = maybeFillRandom(tp, attrVal)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", cid)
+ r.SetUserValue("attr_key", attrKey)
+ r.SetUserValue("attr_val", attrVal)
+
+ hc.Handler().HeadByAttribute(r)
+
+ return fuzzSuccessExitCode
+}
+
+func FuzzHeadByAttribute(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ DoFuzzHeadByAttribute(data)
+ })
+}
+
+func InitFuzzDownloadZipped() {
+
+}
+
+func DoFuzzDownloadZipped(input []byte) int {
+ // FUZZER INIT
+ if len(input) < 100 {
+ return fuzzFailExitCode
+ }
+
+ tp, err := go_fuzz_utils.NewTypeProvider(input)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ ctx, hc, cnrID, _, _, _, _, err := upload(tp)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ cid := cnrID.EncodeToString()
+ cid, err = maybeFillRandom(tp, cid)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ prefix := ""
+ prefix, err = maybeFillRandom(tp, prefix)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+ r.SetUserValue("cid", cid)
+ r.SetUserValue("prefix", prefix)
+
+ hc.Handler().DownloadZipped(r)
+
+ return fuzzSuccessExitCode
+}
+
+func FuzzDownloadZipped(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ DoFuzzDownloadZipped(data)
+ })
+}
+
+func InitFuzzStoreBearerTokenAppCtx() {
+
+}
+
+func DoFuzzStoreBearerTokenAppCtx(input []byte) int {
+ // FUZZER INIT
+ if len(input) < 100 {
+ return fuzzFailExitCode
+ }
+
+ tp, err := go_fuzz_utils.NewTypeProvider(input)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ prefix := ""
+ prefix, err = maybeFillRandom(tp, prefix)
+ if err != nil {
+ return fuzzFailExitCode
+ }
+
+ ctx := context.Background()
+ ctx = middleware.SetNamespace(ctx, "")
+
+ r := new(fasthttp.RequestCtx)
+ utils.SetContextToRequest(ctx, r)
+
+ strings, err := prepareStrings(tp, 3)
+
+ rand, err := prepareBools(tp, 2)
+
+ if rand[0] == true {
+ r.Request.Header.Set(fasthttp.HeaderAuthorization, "Bearer"+strings[0])
+ } else if rand[1] == true {
+ r.Request.Header.SetCookie(fasthttp.HeaderAuthorization, "Bearer"+strings[1])
+ } else {
+ r.Request.Header.Set(fasthttp.HeaderAuthorization, "Bearer"+strings[0])
+ r.Request.Header.SetCookie(fasthttp.HeaderAuthorization, "Bearer"+strings[1])
+ }
+
+ tokens.StoreBearerTokenAppCtx(ctx, r)
+
+ return fuzzSuccessExitCode
+}
+
+func FuzzStoreBearerTokenAppCtx(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ DoFuzzStoreBearerTokenAppCtx(data)
+ })
+}
From 77ffde58e94ccc0ca9a7f20485c142b4d2769ff7 Mon Sep 17 00:00:00 2001
From: Pavel Pogodaev
Date: Fri, 30 Aug 2024 13:02:49 +0300
Subject: [PATCH 079/161] [#123] Add SECURITY.md
Signed-off-by: Pavel Pogodaev
---
SECURITY.md | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 SECURITY.md
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..46fe535
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,26 @@
+# Security Policy
+
+
+## How To Report a Vulnerability
+
+If you think you have found a vulnerability in this repository, please report it to us through coordinated disclosure.
+
+**Please do not report security vulnerabilities through public issues, discussions, or change requests.**
+
+Instead, you can report it using one of the following ways:
+
+* Contact the [TrueCloudLab Security Team](mailto:security@frostfs.info) via email
+
+Please include as much of the information listed below as you can to help us better understand and resolve the issue:
+
+* The type of issue (e.g., buffer overflow, or cross-site scripting)
+* Affected version(s)
+* Impact of the issue, including how an attacker might exploit the issue
+* Step-by-step instructions to reproduce the issue
+* The location of the affected source code (tag/branch/commit or direct URL)
+* Full paths of source file(s) related to the manifestation of the issue
+* Any special configuration required to reproduce the issue
+* Any log files that are related to this issue (if possible)
+* Proof-of-concept or exploit code (if possible)
+
+This information will help us triage your report more quickly.
From 843708a558c5b9f308c8eafeaa09171a45500202 Mon Sep 17 00:00:00 2001
From: Pavel Pogodaev
Date: Mon, 26 Aug 2024 18:47:33 +0300
Subject: [PATCH 080/161] [#134] Support percent-encoding
Signed-off-by: Pavel Pogodaev
---
CHANGELOG.md | 1 +
internal/handler/handler.go | 8 +++++++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b322c96..a489027 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
This document outlines major changes between releases.
## [Unreleased]
+- Support percent-encoding for GET queries (#134)
### Changed
- Update go version to 1.22 (#132)
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 0bbcdb9..4de9d9a 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -215,6 +215,12 @@ func (h *Handler) byObjectName(req *fasthttp.RequestCtx, f func(context.Context,
log = h.log.With(zap.String("bucketname", bucketname), zap.String("key", key))
)
+ unescapedKey, err := url.QueryUnescape(key)
+ if err != nil {
+ logAndSendBucketError(req, log, err)
+ return
+ }
+
ctx := utils.GetContextFromRequest(req)
bktInfo, err := h.getBucketInfo(ctx, bucketname, log)
@@ -223,7 +229,7 @@ func (h *Handler) byObjectName(req *fasthttp.RequestCtx, f func(context.Context,
return
}
- foundOid, err := h.tree.GetLatestVersion(ctx, &bktInfo.CID, key)
+ foundOid, err := h.tree.GetLatestVersion(ctx, &bktInfo.CID, unescapedKey)
if err != nil {
if errors.Is(err, tree.ErrNodeAccessDenied) {
response.Error(req, "Access Denied", fasthttp.StatusForbidden)
From 7e80f0cce6fba901414964347e0ccf7755ff8553 Mon Sep 17 00:00:00 2001
From: Aleksey Savaitan
Date: Tue, 10 Sep 2024 10:09:51 +0300
Subject: [PATCH 081/161] [#139] Add root ca cert for telemetry configuration
Signed-off-by: Aleksey Savaitan
---
cmd/http-gw/app.go | 17 ++++++
cmd/http-gw/settings.go | 7 ++-
config/config.env | 1 +
config/config.yaml | 1 +
docs/gate-configuration.md | 12 ++--
go.mod | 47 +++++++--------
go.sum | 119 ++++++++++++++++---------------------
7 files changed, 102 insertions(+), 102 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 561598f..4c49ee4 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -3,6 +3,7 @@ package main
import (
"bytes"
"context"
+ "crypto/x509"
"errors"
"fmt"
"net/http"
@@ -737,6 +738,22 @@ func (a *app) initTracing(ctx context.Context) {
InstanceID: instanceID,
Version: Version,
}
+
+ if trustedCa := a.cfg.GetString(cfgTracingTrustedCa); trustedCa != "" {
+ caBytes, err := os.ReadFile(trustedCa)
+ if err != nil {
+ a.log.Warn(logs.FailedToInitializeTracing, zap.Error(err))
+ return
+ }
+ certPool := x509.NewCertPool()
+ ok := certPool.AppendCertsFromPEM(caBytes)
+ if !ok {
+ a.log.Warn(logs.FailedToInitializeTracing, zap.String("error", "can't fill cert pool by ca cert"))
+ return
+ }
+ cfg.ServerCaCertPool = certPool
+ }
+
updated, err := tracing.Setup(ctx, cfg)
if err != nil {
a.log.Warn(logs.FailedToInitializeTracing, zap.Error(err))
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 0d97dcb..3fe0023 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -75,9 +75,10 @@ const (
cfgPprofAddress = "pprof.address"
// Tracing ...
- cfgTracingEnabled = "tracing.enabled"
- cfgTracingExporter = "tracing.exporter"
- cfgTracingEndpoint = "tracing.endpoint"
+ cfgTracingEnabled = "tracing.enabled"
+ cfgTracingExporter = "tracing.exporter"
+ cfgTracingEndpoint = "tracing.endpoint"
+ cfgTracingTrustedCa = "tracing.trusted_ca"
// Pool config.
cfgConTimeout = "connect_timeout"
diff --git a/config/config.env b/config/config.env
index 05b83b3..b7347d7 100644
--- a/config/config.env
+++ b/config/config.env
@@ -99,6 +99,7 @@ HTTP_GW_ZIP_COMPRESSION=false
HTTP_GW_TRACING_ENABLED=true
HTTP_GW_TRACING_ENDPOINT="localhost:4317"
HTTP_GW_TRACING_EXPORTER="otlp_grpc"
+HTTP_GW_TRACING_TRUSTED_CA=""
HTTP_GW_RUNTIME_SOFT_MEMORY_LIMIT=1073741824
diff --git a/config/config.yaml b/config/config.yaml
index 7f8077b..ef57612 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -13,6 +13,7 @@ tracing:
enabled: true
exporter: "otlp_grpc"
endpoint: "localhost:4317"
+ trusted_ca: ""
logger:
level: debug # Log level.
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 8e3daad..e93e6cf 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -256,13 +256,15 @@ tracing:
enabled: true
exporter: "otlp_grpc"
endpoint: "localhost:4317"
+ trusted_ca: "/etc/ssl/telemetry-trusted-ca.pem"
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|------------|----------|---------------|------------------|---------------------------------------------------------------|
-| `enabled` | `bool` | yes | `false` | Flag to enable the tracing. |
-| `exporter` | `string` | yes | | Trace collector type (`stdout` or `otlp_grpc` are supported). |
-| `endpoint` | `string` | yes | | Address of collector endpoint for OTLP exporters. |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|--------------|----------|---------------|---------------|---------------------------------------------------------------------------------------------------------------------------------|
+| `enabled` | `bool` | yes | `false` | Flag to enable the tracing. |
+| `exporter` | `string` | yes | | Trace collector type (`stdout` or `otlp_grpc` are supported). |
+| `endpoint` | `string` | yes | | Address of collector endpoint for OTLP exporters. |
+| `trusted_ca` | `string` | yes | | Path to certificate of a certification authority in pem format, that issued the TLS certificate of the telemetry remote server. |
# `runtime` section
Contains runtime parameters.
diff --git a/go.mod b/go.mod
index accedfb..3519a14 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.22
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164
- git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
+ git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
@@ -19,12 +19,12 @@ require (
github.com/testcontainers/testcontainers-go v0.13.0
github.com/trailofbits/go-fuzz-utils v0.0.0-20230413173806-58c38daa3cb4
github.com/valyala/fasthttp v1.34.0
- go.opentelemetry.io/otel v1.16.0
- go.opentelemetry.io/otel/trace v1.16.0
+ go.opentelemetry.io/otel v1.28.0
+ go.opentelemetry.io/otel/trace v1.28.0
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
- golang.org/x/net v0.23.0
- google.golang.org/grpc v1.62.0
+ golang.org/x/net v0.26.0
+ google.golang.org/grpc v1.64.0
)
require (
@@ -39,7 +39,7 @@ require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v4 v4.2.1 // indirect
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/cgroups v1.0.3 // indirect
github.com/containerd/containerd v1.6.2 // indirect
@@ -51,16 +51,15 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
- github.com/go-logr/logr v1.2.4 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
- github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/klauspost/compress v1.16.4 // indirect
@@ -95,24 +94,22 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.opencensus.io v0.24.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 // indirect
- go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 // indirect
- go.opentelemetry.io/otel/metric v1.16.0 // indirect
- go.opentelemetry.io/otel/sdk v1.16.0 // indirect
- go.opentelemetry.io/proto/otlp v0.19.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect
+ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect
+ go.opentelemetry.io/otel/metric v1.28.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.28.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/crypto v0.21.0 // indirect
- golang.org/x/sync v0.6.0 // indirect
- golang.org/x/sys v0.18.0 // indirect
- golang.org/x/term v0.18.0 // indirect
- golang.org/x/text v0.14.0 // indirect
+ golang.org/x/crypto v0.24.0 // indirect
+ golang.org/x/sync v0.7.0 // indirect
+ golang.org/x/sys v0.22.0 // indirect
+ golang.org/x/term v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.3.0 // indirect
- google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
- google.golang.org/protobuf v1.33.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
+ google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 07eccfa..bac691a 100644
--- a/go.sum
+++ b/go.sum
@@ -43,8 +43,8 @@ git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
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-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
-git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
+git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573 h1:6qCcm1oqFbmf9C5AauXzrL5OPGnTbI9HoB/jAtD9274=
+git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573/go.mod h1:kbwB4v2o6RyOfCo9kEFeUDZIX3LKhmS0yXPrtvzkQ1g=
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36 h1:MV/vKJWLQT34RRbXYvkNKFYGNjL5bRNuCQMXkbC7fLI=
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36/go.mod h1:vluJ/+yQMcq8ZIZZSA7Te+JKClr0lgtRErjICvb8wto=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
@@ -137,8 +137,8 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
-github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -160,11 +160,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
@@ -336,7 +332,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fasthttp/router v1.4.1 h1:3xPUO+hy/HAkgGDSd5sX5w18cyGDIFbC7vip8KwPDk8=
@@ -366,8 +361,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
-github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
@@ -402,9 +397,6 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
-github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
-github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -436,8 +428,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -504,9 +494,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
@@ -739,8 +728,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
-github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -874,25 +863,23 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
-go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
-go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0=
-go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0/go.mod h1:vLarbg68dH2Wa77g71zmKQqlQ8+8Rq3GRG31uc0WcWI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 h1:+XWJd3jf75RXJq29mxbuXhCXFDG3S3R4vBUeSI2P7tE=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0/go.mod h1:hqgzBPTf4yONMFgdZvL/bK42R/iinTyVQtiWihs3SZc=
-go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
-go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
-go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
-go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
-go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
-go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
+go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
+go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y=
+go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
+go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
+go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
+go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
+go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
+go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
-go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
+go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
+go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
@@ -921,8 +908,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -959,8 +946,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
-golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1012,8 +999,8 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1023,7 +1010,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1035,8 +1021,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1127,23 +1113,22 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1213,8 +1198,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
-golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1290,13 +1275,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
-google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
-google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A=
-google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1320,9 +1302,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
-google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1336,8 +1317,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
From a4233b006c6ce227078c05938c1f5a235933b7ab Mon Sep 17 00:00:00 2001
From: Nikita Zinkevich
Date: Mon, 23 Sep 2024 09:09:32 +0300
Subject: [PATCH 082/161] [#144] Update frostfs-sdk-go
Signed-off-by: Nikita Zinkevich
---
go.mod | 11 +++++++----
go.sum | 22 ++++++++++++++--------
internal/frostfs/frostfs.go | 5 ++++-
metrics/metrics.go | 2 --
4 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/go.mod b/go.mod
index 3519a14..afa704b 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,9 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.22
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
github.com/fasthttp/router v1.4.1
@@ -24,7 +24,7 @@ require (
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/net v0.26.0
- google.golang.org/grpc v1.64.0
+ google.golang.org/grpc v1.66.2
)
require (
@@ -36,11 +36,12 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Microsoft/hcsshim v0.9.2 // indirect
+ github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
- github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/cgroups v1.0.3 // indirect
github.com/containerd/containerd v1.6.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
@@ -62,8 +63,10 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.16.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/sys/mount v0.3.2 // indirect
github.com/moby/sys/mountinfo v0.6.1 // indirect
diff --git a/go.sum b/go.sum
index bac691a..bc433eb 100644
--- a/go.sum
+++ b/go.sum
@@ -37,16 +37,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164 h1:XxvwQKJT/f16qS3df5PBQPRYKkhy0/A7zH6644QpKD0=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240716113920-f517e3949164/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e h1:740ABnOBYx4o6jxULHdSSnVW2fYIO35ohg+Uz59sxd0=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e/go.mod h1:F5GS7hRb62PUy5sTYDC4ajVdeffoAfjHSSHTKUJEaYU=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e h1:kcBqZBiFIUBATUqEuvVigtkJJWQ2Gug/eYXn967o3M4=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
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-observability v0.0.0-20240909114314-666d326cc573 h1:6qCcm1oqFbmf9C5AauXzrL5OPGnTbI9HoB/jAtD9274=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573/go.mod h1:kbwB4v2o6RyOfCo9kEFeUDZIX3LKhmS0yXPrtvzkQ1g=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36 h1:MV/vKJWLQT34RRbXYvkNKFYGNjL5bRNuCQMXkbC7fLI=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240718141740-ce8270568d36/go.mod h1:vluJ/+yQMcq8ZIZZSA7Te+JKClr0lgtRErjICvb8wto=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98 h1:ijUci3thz0EwWkuRJDocW5D1RkVAJlt9xNG4CYepC90=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98/go.mod h1:GeNpo12HcEW4J412sH5yf8xFYapxlrt5fcYzRwg0Ino=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
@@ -101,6 +101,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
+github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
+github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -142,8 +144,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
@@ -523,6 +525,8 @@ github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -564,6 +568,8 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -1302,8 +1308,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
-google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
+google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/internal/frostfs/frostfs.go b/internal/frostfs/frostfs.go
index 2610564..6cf162a 100644
--- a/internal/frostfs/frostfs.go
+++ b/internal/frostfs/frostfs.go
@@ -60,7 +60,10 @@ func (x *FrostFS) CreateObject(ctx context.Context, prm handler.PrmObjectCreate)
}
idObj, err := x.pool.PutObject(ctx, prmPut)
- return idObj, handleObjectError("save object via connection pool", err)
+ if err != nil {
+ return oid.ID{}, handleObjectError("save object via connection pool", err)
+ }
+ return idObj.ObjectID, nil
}
// wraps io.ReadCloser and transforms Read errors related to access violation
diff --git a/metrics/metrics.go b/metrics/metrics.go
index bfb66ee..b516477 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -191,8 +191,6 @@ func (m *poolMetricsCollector) updateRequestsDuration(node pool.NodeStatistic) {
m.requestDuration.WithLabelValues(node.Address(), methodGetContainer).Set(float64(node.AverageGetContainer().Milliseconds()))
m.requestDuration.WithLabelValues(node.Address(), methodListContainer).Set(float64(node.AverageListContainer().Milliseconds()))
m.requestDuration.WithLabelValues(node.Address(), methodDeleteContainer).Set(float64(node.AverageDeleteContainer().Milliseconds()))
- m.requestDuration.WithLabelValues(node.Address(), methodGetContainerEacl).Set(float64(node.AverageGetContainerEACL().Milliseconds()))
- m.requestDuration.WithLabelValues(node.Address(), methodSetContainerEacl).Set(float64(node.AverageSetContainerEACL().Milliseconds()))
m.requestDuration.WithLabelValues(node.Address(), methodEndpointInfo).Set(float64(node.AverageEndpointInfo().Milliseconds()))
m.requestDuration.WithLabelValues(node.Address(), methodNetworkInfo).Set(float64(node.AverageNetworkInfo().Milliseconds()))
m.requestDuration.WithLabelValues(node.Address(), methodPutObject).Set(float64(node.AveragePutObject().Milliseconds()))
From c8473498aeebf65e855badd995118893cbf5f6ba Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Wed, 25 Sep 2024 14:02:32 +0300
Subject: [PATCH 083/161] [#146] Fix of sighup traicing docs
Signed-off-by: Roman Loginov
---
docs/gate-configuration.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index e93e6cf..e0bb336 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -261,7 +261,7 @@ tracing:
| Parameter | Type | SIGHUP reload | Default value | Description |
|--------------|----------|---------------|---------------|---------------------------------------------------------------------------------------------------------------------------------|
-| `enabled` | `bool` | yes | `false` | Flag to enable the tracing. |
+| `enabled` | `bool` | no | `false` | Flag to enable the tracing. |
| `exporter` | `string` | yes | | Trace collector type (`stdout` or `otlp_grpc` are supported). |
| `endpoint` | `string` | yes | | Address of collector endpoint for OTLP exporters. |
| `trusted_ca` | `string` | yes | | Path to certificate of a certification authority in pem format, that issued the TLS certificate of the telemetry remote server. |
From 77eb4745814640ff34a6b787712a427c1ff93519 Mon Sep 17 00:00:00 2001
From: Pavel Pogodaev
Date: Thu, 26 Sep 2024 17:48:19 +0300
Subject: [PATCH 084/161] [#147] Add sampling configuration
Signed-off-by: Pavel Pogodaev
---
cmd/http-gw/settings.go | 73 +++++++++++++++++++++++++++-----------
config/config.env | 6 +++-
config/config.yaml | 5 +++
docs/gate-configuration.md | 17 ++++++---
4 files changed, 75 insertions(+), 26 deletions(-)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 3fe0023..d9bbc53 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -41,6 +41,8 @@ const (
defaultConnectTimeout = 10 * time.Second
defaultStreamTimeout = 10 * time.Second
+ defaultLoggerSamplerInterval = 1 * time.Second
+
defaultShutdownTimeout = 15 * time.Second
defaultPoolErrorThreshold uint32 = 100
@@ -91,6 +93,11 @@ const (
cfgLoggerLevel = "logger.level"
cfgLoggerDestination = "logger.destination"
+ cfgLoggerSamplingEnabled = "logger.sampling.enabled"
+ cfgLoggerSamplingInitial = "logger.sampling.initial"
+ cfgLoggerSamplingThereafter = "logger.sampling.thereafter"
+ cfgLoggerSamplingInterval = "logger.sampling.interval"
+
// Wallet.
cfgWalletPassphrase = "wallet.passphrase"
cfgWalletPath = "wallet.path"
@@ -188,6 +195,10 @@ func settings() *viper.Viper {
// logger:
v.SetDefault(cfgLoggerLevel, "debug")
v.SetDefault(cfgLoggerDestination, "stdout")
+ v.SetDefault(cfgLoggerSamplingEnabled, false)
+ v.SetDefault(cfgLoggerSamplingThereafter, 100)
+ v.SetDefault(cfgLoggerSamplingInitial, 100)
+ v.SetDefault(cfgLoggerSamplingInterval, defaultLoggerSamplerInterval)
// pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
@@ -386,9 +397,9 @@ func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
switch dest {
case destinationStdout:
- return newStdoutLogger(lvl)
+ return newStdoutLogger(v, lvl)
case destinationJournald:
- return newJournaldLogger(lvl)
+ return newJournaldLogger(v, lvl)
default:
panic(fmt.Sprintf("wrong destination for logger: %s", dest))
}
@@ -405,39 +416,59 @@ func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
// Logger records a stack trace for all messages at or above fatal level.
//
// See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace.
-func newStdoutLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
- c := zap.NewProductionConfig()
- c.Level = zap.NewAtomicLevelAt(lvl)
- c.Encoding = "console"
- c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+func newStdoutLogger(v *viper.Viper, lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
+ stdout := zapcore.AddSync(os.Stderr)
+ level := zap.NewAtomicLevelAt(lvl)
- l, err := c.Build(
- zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)),
- )
- if err != nil {
- panic(fmt.Sprintf("build zap logger instance: %v", err))
- }
+ consoleOutCore := zapcore.NewCore(newLogEncoder(), stdout, level)
+ consoleOutCore = samplingEnabling(v, consoleOutCore)
- return l, c.Level
+ l := zap.New(consoleOutCore, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
+ return l, level
}
-func newJournaldLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
- c := zap.NewProductionConfig()
- c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
- c.Level = zap.NewAtomicLevelAt(lvl)
+func newJournaldLogger(v *viper.Viper, lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
+ level := zap.NewAtomicLevelAt(lvl)
- encoder := zapjournald.NewPartialEncoder(zapcore.NewConsoleEncoder(c.EncoderConfig), zapjournald.SyslogFields)
+ encoder := zapjournald.NewPartialEncoder(newLogEncoder(), zapjournald.SyslogFields)
- core := zapjournald.NewCore(c.Level, encoder, &journald.Journal{}, zapjournald.SyslogFields)
+ core := zapjournald.NewCore(level, encoder, &journald.Journal{}, zapjournald.SyslogFields)
coreWithContext := core.With([]zapcore.Field{
zapjournald.SyslogFacility(zapjournald.LogDaemon),
zapjournald.SyslogIdentifier(),
zapjournald.SyslogPid(),
})
+ coreWithContext = samplingEnabling(v, coreWithContext)
+
l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
- return l, c.Level
+ return l, level
+}
+
+func newLogEncoder() zapcore.Encoder {
+ c := zap.NewProductionEncoderConfig()
+ c.EncodeTime = zapcore.ISO8601TimeEncoder
+
+ return zapcore.NewConsoleEncoder(c)
+}
+
+func samplingEnabling(v *viper.Viper, core zapcore.Core) zapcore.Core {
+ // Zap samples by logging the first cgfLoggerSamplingInitial entries with a given level
+ // and message within the specified time interval.
+ // In the above config, only the first cgfLoggerSamplingInitial log entries with the same level and message
+ // are recorded in cfgLoggerSamplingInterval interval. Every other log entry will be dropped within the interval since
+ // cfgLoggerSamplingThereafter is specified here.
+ if v.GetBool(cfgLoggerSamplingEnabled) {
+ core = zapcore.NewSamplerWithOptions(
+ core,
+ v.GetDuration(cfgLoggerSamplingInterval),
+ v.GetInt(cfgLoggerSamplingInitial),
+ v.GetInt(cfgLoggerSamplingThereafter),
+ )
+ }
+
+ return core
}
func getLogLevel(v *viper.Viper) (zapcore.Level, error) {
diff --git a/config/config.env b/config/config.env
index b7347d7..e1d5e7d 100644
--- a/config/config.env
+++ b/config/config.env
@@ -14,8 +14,12 @@ HTTP_GW_PPROF_ADDRESS=localhost:8083
HTTP_GW_PROMETHEUS_ENABLED=true
HTTP_GW_PROMETHEUS_ADDRESS=localhost:8084
-# Log level.
+# Logger.
HTTP_GW_LOGGER_LEVEL=debug
+HTTP_GW_LOGGER_SAMPLING_ENABLED=false
+HTTP_GW_LOGGER_SAMPLING_INITIAL=100
+HTTP_GW_LOGGER_SAMPLING_THEREAFTER=100
+HTTP_GW_LOGGER_SAMPLING_INTERVAL=1s
HTTP_GW_SERVER_0_ADDRESS=0.0.0.0:443
HTTP_GW_SERVER_0_TLS_ENABLED=false
diff --git a/config/config.yaml b/config/config.yaml
index ef57612..1b87fe9 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -18,6 +18,11 @@ tracing:
logger:
level: debug # Log level.
destination: stdout
+ sampling:
+ enabled: false
+ initial: 100
+ thereafter: 100
+ interval: 1s
server:
- address: 0.0.0.0:8080
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index e0bb336..fb5ad2f 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -164,12 +164,21 @@ server:
logger:
level: debug
destination: stdout
+ sampling:
+ enabled: false
+ initial: 100
+ thereafter: 100
+ interval: 1s
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|---------------|----------|---------------|---------------|----------------------------------------------------------------------------------------------------|
-| `level` | `string` | yes | `debug` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. |
-| `destination` | `string` | no | `stdout` | Destination for logger: `stdout` or `journald` |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|-----------------------|------------|---------------|---------------|----------------------------------------------------------------------------------------------------|
+| `level` | `string` | yes | `debug` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. |
+| `destination` | `string` | no | `stdout` | Destination for logger: `stdout` or `journald` |
+| `sampling.enabled` | `bool` | no | false | Sampling enabling flag. |
+| `sampling.initial` | `int` | no | '100' | Sampling count of first log entries. |
+| `sampling.thereafter` | `int` | no | '100' | Sampling count of entries after an `interval`. |
+| `sampling.interval` | `duration` | no | '1s' | Sampling interval of messaging similar entries. |
# `web` section
From 8fe8f2dcc279548c6a8299fac5e3a3097ab9b20d Mon Sep 17 00:00:00 2001
From: Nikita Zinkevich
Date: Thu, 26 Sep 2024 17:32:27 +0300
Subject: [PATCH 085/161] [#137] Add index page support
Signed-off-by: Nikita Zinkevich
---
cmd/http-gw/app.go | 54 ++++++
cmd/http-gw/settings.go | 6 +
config/config.yaml | 7 +-
docs/api.md | 13 +-
docs/gate-configuration.md | 38 ++--
go.mod | 2 +-
internal/api/tree.go | 1 +
internal/frostfs/services/pool_wrapper.go | 84 ++++++++-
internal/handler/browse.go | 157 +++++++++++++++++
internal/handler/handler.go | 59 +++++--
internal/handler/handler_test.go | 16 ++
internal/handler/reader.go | 7 +
internal/handler/upload.go | 34 ++--
internal/handler/utils.go | 21 +++
internal/logs/logs.go | 2 +
internal/templates/index.gotmpl | 90 ++++++++++
internal/templates/template.go | 6 +
tokens/bearer-token.go | 4 +-
tree/tree.go | 204 +++++++++++++++++++---
tree/tree_test.go | 13 +-
20 files changed, 738 insertions(+), 80 deletions(-)
create mode 100644 internal/handler/browse.go
create mode 100644 internal/templates/index.gotmpl
create mode 100644 internal/templates/template.go
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 4c49ee4..8d5930d 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -6,6 +6,7 @@ import (
"crypto/x509"
"errors"
"fmt"
+ "io"
"net/http"
"os"
"os/signal"
@@ -23,6 +24,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/templates"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
@@ -91,6 +93,8 @@ type (
defaultTimestamp bool
zipCompression bool
clientCut bool
+ returnIndexPage bool
+ indexPageTemplate string
bufferMaxSizeForPut uint64
namespaceHeader string
defaultNamespaces []string
@@ -155,6 +159,7 @@ func newApp(ctx context.Context, opt ...Option) App {
a.initResolver()
a.initMetrics()
a.initTracing(ctx)
+ a.loadIndexPageTemplate()
return a
}
@@ -177,12 +182,59 @@ func (s *appSettings) ZipCompression() bool {
return s.zipCompression
}
+func (s *appSettings) IndexPageEnabled() bool {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.returnIndexPage
+}
+
+func (s *appSettings) IndexPageTemplate() string {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ if s.indexPageTemplate == "" {
+ return templates.DefaultIndexTemplate
+ }
+ return s.indexPageTemplate
+}
+
func (s *appSettings) setZipCompression(val bool) {
s.mu.Lock()
s.zipCompression = val
s.mu.Unlock()
}
+func (s *appSettings) setReturnIndexPage(val bool) {
+ s.mu.Lock()
+ s.returnIndexPage = val
+ s.mu.Unlock()
+}
+
+func (s *appSettings) setIndexTemplate(val string) {
+ s.mu.Lock()
+ s.indexPageTemplate = val
+ s.mu.Unlock()
+}
+
+func (a *app) loadIndexPageTemplate() {
+ if !a.settings.IndexPageEnabled() {
+ return
+ }
+ reader, err := os.Open(a.cfg.GetString(cfgIndexPageTemplatePath))
+ if err != nil {
+ a.settings.setIndexTemplate("")
+ a.log.Warn(logs.FailedToReadIndexPageTemplate, zap.Error(err))
+ return
+ }
+ tmpl, err := io.ReadAll(reader)
+ if err != nil {
+ a.settings.setIndexTemplate("")
+ a.log.Warn(logs.FailedToReadIndexPageTemplate, zap.Error(err))
+ return
+ }
+ a.settings.setIndexTemplate(string(tmpl))
+ a.log.Info(logs.SetCustomIndexPageTemplate)
+}
+
func (s *appSettings) ClientCut() bool {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -491,6 +543,7 @@ func (a *app) configReload(ctx context.Context) {
a.metrics.SetEnabled(a.cfg.GetBool(cfgPrometheusEnabled))
a.initTracing(ctx)
+ a.loadIndexPageTemplate()
a.setHealthStatus()
a.log.Info(logs.SIGHUPConfigReloadCompleted)
@@ -499,6 +552,7 @@ func (a *app) configReload(ctx context.Context) {
func (a *app) updateSettings() {
a.settings.setDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
a.settings.setZipCompression(a.cfg.GetBool(cfgZipCompression))
+ a.settings.setReturnIndexPage(a.cfg.GetBool(cfgIndexPageEnabled))
a.settings.setClientCut(a.cfg.GetBool(cfgClientCut))
a.settings.setBufferMaxSizeForPut(a.cfg.GetUint64(cfgBufferMaxSizeForPut))
a.settings.setNamespaceHeader(a.cfg.GetString(cfgResolveNamespaceHeader))
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index d9bbc53..eab5b6b 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -62,6 +62,9 @@ const (
cfgReconnectInterval = "reconnect_interval"
+ cfgIndexPageEnabled = "index_page.enabled"
+ cfgIndexPageTemplatePath = "index_page.template_path"
+
// Web.
cfgWebReadBufferSize = "web.read_buffer_size"
cfgWebWriteBufferSize = "web.write_buffer_size"
@@ -203,6 +206,9 @@ func settings() *viper.Viper {
// pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
+ v.SetDefault(cfgIndexPageEnabled, false)
+ v.SetDefault(cfgIndexPageTemplatePath, "")
+
// frostfs:
v.SetDefault(cfgBufferMaxSizeForPut, defaultBufferMaxSizeForPut)
diff --git a/config/config.yaml b/config/config.yaml
index 1b87fe9..61aa70b 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -107,6 +107,11 @@ request_timeout: 5s # Timeout to check node health during rebalance.
rebalance_timer: 30s # Interval to check nodes health.
pool_error_threshold: 100 # The number of errors on connection after which node is considered as unhealthy.
+# Enable index page to see objects list for specified container and prefix
+index_page:
+ enabled: false
+ template_path: internal/handler/templates/index.gotmpl
+
zip:
compression: false # Enable zip compression to download files by common prefix.
@@ -132,4 +137,4 @@ cache:
resolve_bucket:
namespace_header: X-Frostfs-Namespace
- default_namespaces: [ "", "root" ]
\ No newline at end of file
+ default_namespaces: [ "", "root" ]
diff --git a/docs/api.md b/docs/api.md
index 78df766..f7eb3a4 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -95,12 +95,12 @@ The `filename` field from the multipart form will be set as `FileName` attribute
## Get object
-Route: `/get/{cid}/{oid}?[download=true]`
+Route: `/get/{cid}/{oid}?[download=false]`
| Route parameter | Type | Description |
|-----------------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `cid` | Single | Base58 encoded container ID or container name from NNS. |
-| `oid` | Single | Base58 encoded object ID. |
+| `cid` | Single | Base58 encoded `container ID` or `container name` from NNS or `bucket name`. |
+| `oid` | Single | Base58 encoded `object ID`. Also could be `S3 object name` if `cid` is specified as bucket name. |
| `download` | Query | Set the `Content-Disposition` header as `attachment` in response.
This make the browser to download object as file instead of showing it on the page. |
### Methods
@@ -141,6 +141,13 @@ Get an object (payload and attributes) by an address.
| 400 | Some error occurred during object downloading. |
| 404 | Container or object not found. |
+###### Body
+
+Returns object data. If request performed from browser, either displays raw data or downloads it as
+attachment if `download` query parameter is set to `true`.
+If `index_page.enabled` is set to `true`, returns HTML with index-page if no object with specified
+S3-name was found.
+
#### HEAD
Get an object attributes by an address.
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index fb5ad2f..e8d1f4b 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -57,6 +57,7 @@ $ cat http.log
| `frostfs` | [Frostfs configuration](#frostfs-section) |
| `cache` | [Cache configuration](#cache-section) |
| `resolve_bucket` | [Bucket name resolving configuration](#resolve_bucket-section) |
+| `index_page` | [Index page configuration](#index_page-section) |
# General section
@@ -75,16 +76,16 @@ pool_error_threshold: 100
reconnect_interval: 1m
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|------------------------|------------|---------------|----------------|------------------------------------------------------------------------------------|
-| `rpc_endpoint` | `string` | yes | | The address of the RPC host to which the gateway connects to resolve bucket names. |
-| `resolve_order` | `[]string` | yes | `[nns, dns]` | Order of bucket name resolvers to use. |
-| `connect_timeout` | `duration` | | `10s` | Timeout to connect to a node. |
-| `stream_timeout` | `duration` | | `10s` | Timeout for individual operations in streaming RPC. |
-| `request_timeout` | `duration` | | `15s` | Timeout to check node health during rebalance. |
-| `rebalance_timer` | `duration` | | `60s` | Interval to check node health. |
-| `pool_error_threshold` | `uint32` | | `100` | The number of errors on connection after which node is considered as unhealthy. |
-| `reconnect_interval` | `duration` | no | `1m` | Listeners reconnection interval. |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|------------------------|------------|---------------|---------------|-------------------------------------------------------------------------------------------------|
+| `rpc_endpoint` | `string` | yes | | The address of the RPC host to which the gateway connects to resolve bucket names. |
+| `resolve_order` | `[]string` | yes | `[nns, dns]` | Order of bucket name resolvers to use. |
+| `connect_timeout` | `duration` | | `10s` | Timeout to connect to a node. |
+| `stream_timeout` | `duration` | | `10s` | Timeout for individual operations in streaming RPC. |
+| `request_timeout` | `duration` | | `15s` | Timeout to check node health during rebalance. |
+| `rebalance_timer` | `duration` | | `60s` | Interval to check node health. |
+| `pool_error_threshold` | `uint32` | | `100` | The number of errors on connection after which node is considered as unhealthy. |
+| `reconnect_interval` | `duration` | no | `1m` | Listeners reconnection interval. |
# `wallet` section
@@ -346,4 +347,19 @@ resolve_bucket:
| Parameter | Type | SIGHUP reload | Default value | Description |
|----------------------|------------|---------------|-----------------------|--------------------------------------------------------------------------------------------------------------------------|
| `namespace_header` | `string` | yes | `X-Frostfs-Namespace` | Header to determine zone to resolve bucket name. |
-| `default_namespaces` | `[]string` | yes | ["","root"] | Namespaces that should be handled as default. |
\ No newline at end of file
+| `default_namespaces` | `[]string` | yes | ["","root"] | Namespaces that should be handled as default. |
+
+# `index_page` section
+
+Parameters for index HTML-page output with S3-bucket or S3-subdir content for `Get object` request
+
+```yaml
+index_page:
+ enabled: false
+ template_path: ""
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|-----------------|----------|---------------|---------------|---------------------------------------------------------------------------------|
+| `enabled` | `bool` | yes | `false` | Flag to enable index_page return if no object with specified S3-name was found. |
+| `template_path` | `string` | yes | `""` | Path to .gotmpl file with html template for index_page. |
diff --git a/go.mod b/go.mod
index afa704b..d1a3788 100644
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
+ github.com/docker/go-units v0.4.0
github.com/fasthttp/router v1.4.1
github.com/nspcc-dev/neo-go v0.106.2
github.com/prometheus/client_golang v1.19.0
@@ -50,7 +51,6 @@ require (
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.14+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
- github.com/docker/go-units v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
diff --git a/internal/api/tree.go b/internal/api/tree.go
index 4d16cc7..5b1d608 100644
--- a/internal/api/tree.go
+++ b/internal/api/tree.go
@@ -8,6 +8,7 @@ import (
type NodeVersion struct {
BaseNodeVersion
DeleteMarker bool
+ IsPrefixNode bool
}
// BaseNodeVersion is minimal node info from tree service.
diff --git a/internal/frostfs/services/pool_wrapper.go b/internal/frostfs/services/pool_wrapper.go
index f7b0a26..fa70f15 100644
--- a/internal/frostfs/services/pool_wrapper.go
+++ b/internal/frostfs/services/pool_wrapper.go
@@ -4,7 +4,9 @@ import (
"context"
"errors"
"fmt"
+ "io"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
treepool "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
@@ -15,16 +17,16 @@ type GetNodeByPathResponseInfoWrapper struct {
response *grpcService.GetNodeByPathResponse_Info
}
-func (n GetNodeByPathResponseInfoWrapper) GetNodeID() uint64 {
- return n.response.GetNodeId()
+func (n GetNodeByPathResponseInfoWrapper) GetNodeID() []uint64 {
+ return []uint64{n.response.GetNodeId()}
}
-func (n GetNodeByPathResponseInfoWrapper) GetParentID() uint64 {
- return n.response.GetParentId()
+func (n GetNodeByPathResponseInfoWrapper) GetParentID() []uint64 {
+ return []uint64{n.response.GetParentId()}
}
-func (n GetNodeByPathResponseInfoWrapper) GetTimestamp() uint64 {
- return n.response.GetTimestamp()
+func (n GetNodeByPathResponseInfoWrapper) GetTimestamp() []uint64 {
+ return []uint64{n.response.GetTimestamp()}
}
func (n GetNodeByPathResponseInfoWrapper) GetMeta() []tree.Meta {
@@ -89,3 +91,73 @@ func handleError(err error) error {
return err
}
+
+func (w *PoolWrapper) GetSubTree(ctx context.Context, bktInfo *data.BucketInfo, treeID string, rootID []uint64, depth uint32, sort bool) ([]tree.NodeResponse, error) {
+ order := treepool.NoneOrder
+ if sort {
+ order = treepool.AscendingOrder
+ }
+ poolPrm := treepool.GetSubTreeParams{
+ CID: bktInfo.CID,
+ TreeID: treeID,
+ RootID: rootID,
+ Depth: depth,
+ BearerToken: getBearer(ctx),
+ Order: order,
+ }
+ if len(rootID) == 1 && rootID[0] == 0 {
+ // storage node interprets 'nil' value as []uint64{0}
+ // gate wants to send 'nil' value instead of []uint64{0}, because
+ // it provides compatibility with previous tree service api where
+ // single uint64(0) value is dropped from signature
+ poolPrm.RootID = nil
+ }
+
+ subTreeReader, err := w.p.GetSubTree(ctx, poolPrm)
+ if err != nil {
+ return nil, handleError(err)
+ }
+
+ var subtree []tree.NodeResponse
+
+ node, err := subTreeReader.Next()
+ for err == nil {
+ subtree = append(subtree, GetSubTreeResponseBodyWrapper{node})
+ node, err = subTreeReader.Next()
+ }
+ if err != io.EOF {
+ return nil, handleError(err)
+ }
+
+ return subtree, nil
+}
+
+type GetSubTreeResponseBodyWrapper struct {
+ response *grpcService.GetSubTreeResponse_Body
+}
+
+func (n GetSubTreeResponseBodyWrapper) GetNodeID() []uint64 {
+ return n.response.GetNodeId()
+}
+
+func (n GetSubTreeResponseBodyWrapper) GetParentID() []uint64 {
+ resp := n.response.GetParentId()
+ if resp == nil {
+ // storage sends nil that should be interpreted as []uint64{0}
+ // due to protobuf compatibility, see 'GetSubTree' function
+ return []uint64{0}
+ }
+ return resp
+}
+
+func (n GetSubTreeResponseBodyWrapper) GetTimestamp() []uint64 {
+ return n.response.GetTimestamp()
+}
+
+func (n GetSubTreeResponseBodyWrapper) GetMeta() []tree.Meta {
+ res := make([]tree.Meta, len(n.response.Meta))
+ for i, value := range n.response.Meta {
+ res[i] = value
+ }
+ return res
+}
diff --git a/internal/handler/browse.go b/internal/handler/browse.go
new file mode 100644
index 0000000..e84fb04
--- /dev/null
+++ b/internal/handler/browse.go
@@ -0,0 +1,157 @@
+package handler
+
+import (
+ "html/template"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ "github.com/docker/go-units"
+ "github.com/valyala/fasthttp"
+ "go.uber.org/zap"
+)
+
+const (
+ dateFormat = "02-01-2006 15:04"
+ attrOID = "OID"
+ attrCreated = "Created"
+ attrFileName = "FileName"
+ attrSize = "Size"
+)
+
+type (
+ BrowsePageData struct {
+ BucketName,
+ Prefix string
+ Objects []ResponseObject
+ }
+ ResponseObject struct {
+ OID string
+ Created string
+ FileName string
+ Size string
+ IsDir bool
+ }
+)
+
+func parseTimestamp(tstamp string) (time.Time, error) {
+ millis, err := strconv.ParseInt(tstamp, 10, 64)
+ if err != nil {
+ return time.Time{}, err
+ }
+
+ return time.UnixMilli(millis), nil
+}
+
+func NewResponseObject(nodes map[string]string) ResponseObject {
+ return ResponseObject{
+ OID: nodes[attrOID],
+ Created: nodes[attrCreated],
+ FileName: nodes[attrFileName],
+ Size: nodes[attrSize],
+ IsDir: nodes[attrOID] == "",
+ }
+}
+
+func formatTimestamp(strdate string) string {
+ date, err := parseTimestamp(strdate)
+ if err != nil || date.IsZero() {
+ return ""
+ }
+
+ return date.Format(dateFormat)
+}
+
+func formatSize(strsize string) string {
+ size, err := strconv.ParseFloat(strsize, 64)
+ if err != nil {
+ return "0B"
+ }
+ return units.HumanSize(size)
+}
+
+func parentDir(prefix string) string {
+ index := strings.LastIndex(prefix, "/")
+ if index == -1 {
+ return prefix
+ }
+ return prefix[index:]
+}
+
+func trimPrefix(encPrefix string) string {
+ prefix, err := url.PathUnescape(encPrefix)
+ if err != nil {
+ return ""
+ }
+ slashIndex := strings.LastIndex(prefix, "/")
+ if slashIndex == -1 {
+ return ""
+ }
+ return prefix[:slashIndex]
+}
+
+func urlencode(prefix, filename string) string {
+ var res strings.Builder
+ path := filename
+ if prefix != "" {
+ path = strings.Join([]string{prefix, filename}, "/")
+ }
+ prefixParts := strings.Split(path, "/")
+ for _, prefixPart := range prefixParts {
+ prefixPart = "/" + url.PathEscape(prefixPart)
+ if prefixPart == "/." || prefixPart == "/.." {
+ prefixPart = url.PathEscape(prefixPart)
+ }
+ res.WriteString(prefixPart)
+ }
+
+ return res.String()
+}
+
+func (h *Handler) browseObjects(c *fasthttp.RequestCtx, bucketInfo *data.BucketInfo, prefix string) {
+ log := h.log.With(zap.String("bucket", bucketInfo.Name))
+ ctx := utils.GetContextFromRequest(c)
+ nodes, err := h.listObjects(ctx, bucketInfo, prefix)
+ if err != nil {
+ logAndSendBucketError(c, log, err)
+ return
+ }
+
+ respObjects := make([]ResponseObject, len(nodes))
+
+ for i, node := range nodes {
+ respObjects[i] = NewResponseObject(node)
+ }
+
+ sort.Slice(respObjects, func(i, j int) bool {
+ if respObjects[i].IsDir == respObjects[j].IsDir {
+ return respObjects[i].FileName < respObjects[j].FileName
+ }
+ return respObjects[i].IsDir
+ })
+ indexTemplate := h.config.IndexPageTemplate()
+
+ tmpl, err := template.New("index").Funcs(template.FuncMap{
+ "formatTimestamp": formatTimestamp,
+ "formatSize": formatSize,
+ "trimPrefix": trimPrefix,
+ "urlencode": urlencode,
+ "parentDir": parentDir,
+ }).Parse(indexTemplate)
+ if err != nil {
+ logAndSendBucketError(c, log, err)
+ return
+ }
+ if err = tmpl.Execute(c, &BrowsePageData{
+ BucketName: bucketInfo.Name,
+ Prefix: prefix,
+ Objects: respObjects,
+ }); err != nil {
+ logAndSendBucketError(c, log, err)
+ return
+ }
+}
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 4de9d9a..8b07af0 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -30,6 +30,8 @@ type Config interface {
DefaultTimestamp() bool
ZipCompression() bool
ClientCut() bool
+ IndexPageEnabled() bool
+ IndexPageTemplate() string
BufferMaxSizeForPut() uint64
NamespaceHeader() string
}
@@ -208,41 +210,50 @@ func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, requ
// byObjectName is a wrapper for function (e.g. request.headObject, request.receiveFile) that
// prepares request and object address to it.
-func (h *Handler) byObjectName(req *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
+func (h *Handler) byObjectName(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
var (
- bucketname = req.UserValue("cid").(string)
- key = req.UserValue("oid").(string)
+ bucketname = c.UserValue("cid").(string)
+ key = c.UserValue("oid").(string)
log = h.log.With(zap.String("bucketname", bucketname), zap.String("key", key))
+ download = c.QueryArgs().GetBool("download")
)
unescapedKey, err := url.QueryUnescape(key)
if err != nil {
- logAndSendBucketError(req, log, err)
+ logAndSendBucketError(c, log, err)
return
}
- ctx := utils.GetContextFromRequest(req)
+ ctx := utils.GetContextFromRequest(c)
bktInfo, err := h.getBucketInfo(ctx, bucketname, log)
if err != nil {
- logAndSendBucketError(req, log, err)
+ logAndSendBucketError(c, log, err)
return
}
foundOid, err := h.tree.GetLatestVersion(ctx, &bktInfo.CID, unescapedKey)
+ if h.config.IndexPageEnabled() && !download && string(c.Method()) != fasthttp.MethodHead {
+ if isDir(unescapedKey) || isContainerRoot(unescapedKey) {
+ if code := checkErrorType(err); code == fasthttp.StatusNotFound || code == fasthttp.StatusOK {
+ c.SetStatusCode(code)
+ h.browseObjects(c, bktInfo, unescapedKey)
+ return
+ }
+ }
+ }
if err != nil {
if errors.Is(err, tree.ErrNodeAccessDenied) {
- response.Error(req, "Access Denied", fasthttp.StatusForbidden)
- return
+ response.Error(c, "Access Denied", fasthttp.StatusForbidden)
+ } else {
+ response.Error(c, "object wasn't found", fasthttp.StatusNotFound)
+ log.Error(logs.GetLatestObjectVersion, zap.Error(err))
}
- log.Error(logs.GetLatestObjectVersion, zap.Error(err))
-
- response.Error(req, "object wasn't found", fasthttp.StatusNotFound)
return
}
if foundOid.DeleteMarker {
log.Error(logs.ObjectWasDeleted)
- response.Error(req, "object deleted", fasthttp.StatusNotFound)
+ response.Error(c, "object deleted", fasthttp.StatusNotFound)
return
}
@@ -250,7 +261,7 @@ func (h *Handler) byObjectName(req *fasthttp.RequestCtx, f func(context.Context,
addr.SetContainer(bktInfo.CID)
addr.SetObject(foundOid.OID)
- f(ctx, *h.newRequest(req, log), addr)
+ f(ctx, *h.newRequest(c, log), addr)
}
// byAttribute is a wrapper similar to byAddress.
@@ -379,3 +390,25 @@ func (h *Handler) readContainer(ctx context.Context, cnrID cid.ID) (*data.Bucket
return bktInfo, err
}
+
+func (h *Handler) listObjects(ctx context.Context, bucketInfo *data.BucketInfo, prefix string) ([]map[string]string, error) {
+ nodes, _, err := h.tree.GetSubTreeByPrefix(ctx, bucketInfo, prefix, true)
+ if err != nil {
+ return nil, err
+ }
+
+ var objects = make([]map[string]string, 0, len(nodes))
+ for _, node := range nodes {
+ meta := node.GetMeta()
+ if meta == nil {
+ continue
+ }
+ var obj = make(map[string]string, len(meta))
+ for _, m := range meta {
+ obj[m.GetKey()] = string(m.GetValue())
+ }
+ objects = append(objects, obj)
+ }
+
+ return objects, nil
+}
diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go
index ed67f88..4fe9153 100644
--- a/internal/handler/handler_test.go
+++ b/internal/handler/handler_test.go
@@ -12,6 +12,7 @@ import (
"time"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
@@ -37,6 +38,10 @@ func (t *treeClientMock) GetNodes(context.Context, *tree.GetNodesParams) ([]tree
return nil, nil
}
+func (t *treeClientMock) GetSubTree(context.Context, *data.BucketInfo, string, []uint64, uint32, bool) ([]tree.NodeResponse, error) {
+ return nil, nil
+}
+
type configMock struct {
}
@@ -48,6 +53,17 @@ func (c *configMock) ZipCompression() bool {
return false
}
+func (c *configMock) IndexPageEnabled() bool {
+ return false
+}
+
+func (c *configMock) IndexPageTemplatePath() string {
+ return ""
+}
+func (c *configMock) IndexPageTemplate() string {
+ return ""
+}
+
func (c *configMock) ClientCut() bool {
return false
}
diff --git a/internal/handler/reader.go b/internal/handler/reader.go
index 65d258b..d901f25 100644
--- a/internal/handler/reader.go
+++ b/internal/handler/reader.go
@@ -53,6 +53,7 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
dis = "inline"
start = time.Now()
filename string
+ filepath string
)
prm := PrmObjectGet{
@@ -104,6 +105,8 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
time.Unix(value, 0).UTC().Format(http.TimeFormat))
case object.AttributeContentType:
contentType = val
+ case object.AttributeFilePath:
+ filepath = val
}
}
@@ -135,6 +138,10 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
}
req.SetContentType(contentType)
+ if filename == "" {
+ filename = filepath
+ }
+
req.Response.Header.Set(fasthttp.HeaderContentDisposition, dis+"; filename="+path.Base(filename))
req.Response.SetBodyStream(rObj.Payload, int(payloadSize))
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index cea2250..6c0e117 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -43,22 +43,22 @@ func (pr *putResponse) encode(w io.Writer) error {
}
// Upload handles multipart upload request.
-func (h *Handler) Upload(req *fasthttp.RequestCtx) {
+func (h *Handler) Upload(c *fasthttp.RequestCtx) {
var (
file MultipartFile
idObj oid.ID
addr oid.Address
- scid, _ = req.UserValue("cid").(string)
+ scid, _ = c.UserValue("cid").(string)
log = h.log.With(zap.String("cid", scid))
- bodyStream = req.RequestBodyStream()
+ bodyStream = c.RequestBodyStream()
drainBuf = make([]byte, drainBufSize)
)
- ctx := utils.GetContextFromRequest(req)
+ ctx := utils.GetContextFromRequest(c)
bktInfo, err := h.getBucketInfo(ctx, scid, log)
if err != nil {
- logAndSendBucketError(req, log, err)
+ logAndSendBucketError(c, log, err)
return
}
@@ -75,21 +75,21 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
zap.Error(err),
)
}()
- boundary := string(req.Request.Header.MultipartFormBoundary())
+ boundary := string(c.Request.Header.MultipartFormBoundary())
if file, err = fetchMultipartFile(h.log, bodyStream, boundary); err != nil {
log.Error(logs.CouldNotReceiveMultipartForm, zap.Error(err))
- response.Error(req, "could not receive multipart/form: "+err.Error(), fasthttp.StatusBadRequest)
+ response.Error(c, "could not receive multipart/form: "+err.Error(), fasthttp.StatusBadRequest)
return
}
- filtered, err := filterHeaders(h.log, &req.Request.Header)
+ filtered, err := filterHeaders(h.log, &c.Request.Header)
if err != nil {
log.Error(logs.CouldNotProcessHeaders, zap.Error(err))
- response.Error(req, err.Error(), fasthttp.StatusBadRequest)
+ response.Error(c, err.Error(), fasthttp.StatusBadRequest)
return
}
now := time.Now()
- if rawHeader := req.Request.Header.Peek(fasthttp.HeaderDate); rawHeader != nil {
+ if rawHeader := c.Request.Header.Peek(fasthttp.HeaderDate); rawHeader != nil {
if parsed, err := time.Parse(http.TimeFormat, string(rawHeader)); err != nil {
log.Warn(logs.CouldNotParseClientTime, zap.String("Date header", string(rawHeader)), zap.Error(err))
} else {
@@ -97,9 +97,9 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
}
}
- if err = utils.PrepareExpirationHeader(req, h.frostfs, filtered, now); err != nil {
+ if err = utils.PrepareExpirationHeader(c, h.frostfs, filtered, now); err != nil {
log.Error(logs.CouldNotPrepareExpirationHeader, zap.Error(err))
- response.Error(req, "could not prepare expiration header: "+err.Error(), fasthttp.StatusBadRequest)
+ response.Error(c, "could not prepare expiration header: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -143,7 +143,7 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
}
if idObj, err = h.frostfs.CreateObject(ctx, prm); err != nil {
- h.handlePutFrostFSErr(req, err)
+ h.handlePutFrostFSErr(c, err)
return
}
@@ -151,9 +151,9 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
addr.SetContainer(bktInfo.CID)
// Try to return the response, otherwise, if something went wrong, throw an error.
- if err = newPutResponse(addr).encode(req); err != nil {
+ if err = newPutResponse(addr).encode(c); err != nil {
log.Error(logs.CouldNotEncodeResponse, zap.Error(err))
- response.Error(req, "could not encode response", fasthttp.StatusBadRequest)
+ response.Error(c, "could not encode response", fasthttp.StatusBadRequest)
return
}
@@ -170,8 +170,8 @@ func (h *Handler) Upload(req *fasthttp.RequestCtx) {
}
}
// Report status code and content type.
- req.Response.SetStatusCode(fasthttp.StatusOK)
- req.Response.Header.SetContentType(jsonHeader)
+ c.Response.SetStatusCode(fasthttp.StatusOK)
+ c.Response.Header.SetContentType(jsonHeader)
}
func (h *Handler) handlePutFrostFSErr(r *fasthttp.RequestCtx, err error) {
diff --git a/internal/handler/utils.go b/internal/handler/utils.go
index a5a53ed..7fa1158 100644
--- a/internal/handler/utils.go
+++ b/internal/handler/utils.go
@@ -2,12 +2,14 @@ package handler
import (
"context"
+ "errors"
"strings"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
"github.com/valyala/fasthttp"
@@ -38,6 +40,25 @@ func bearerToken(ctx context.Context) *bearer.Token {
return nil
}
+func isDir(name string) bool {
+ return strings.HasSuffix(name, "/")
+}
+
+func isContainerRoot(key string) bool {
+ return key == ""
+}
+
+func checkErrorType(err error) int {
+ switch {
+ case err == nil:
+ return fasthttp.StatusOK
+ case errors.Is(err, tree.ErrNodeAccessDenied):
+ return fasthttp.StatusForbidden
+ default:
+ return fasthttp.StatusNotFound
+ }
+}
+
func isValidToken(s string) bool {
for _, c := range s {
if c <= ' ' || c > 127 {
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index 0ab5dbf..96bdaa5 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -31,6 +31,8 @@ const (
CouldNotStoreFileInFrostfs = "could not store file in frostfs" // Error in ../../uploader/upload.go
AddAttributeToResultObject = "add attribute to result object" // Debug in ../../uploader/filter.go
FailedToCreateResolver = "failed to create resolver" // Fatal in ../../app.go
+ FailedToReadIndexPageTemplate = "failed to read index page template, set default" // Warn in ../../app.go
+ SetCustomIndexPageTemplate = "set custom index page template" // Info in ../../app.go
ContainerResolverWillBeDisabledBecauseOfResolversResolverOrderIsEmpty = "container resolver will be disabled because of resolvers 'resolver_order' is empty" // Info in ../../app.go
MetricsAreDisabled = "metrics are disabled" // Warn in ../../app.go
NoWalletPathSpecifiedCreatingEphemeralKeyAutomaticallyForThisRun = "no wallet path specified, creating ephemeral key automatically for this run" // Info in ../../app.go
diff --git a/internal/templates/index.gotmpl b/internal/templates/index.gotmpl
new file mode 100644
index 0000000..ea66a62
--- /dev/null
+++ b/internal/templates/index.gotmpl
@@ -0,0 +1,90 @@
+{{$bucketName := .BucketName}}
+{{ $prefix := trimPrefix .Prefix }}
+
+
+
+
+ Index of s3://{{$bucketName}}/{{if $prefix}}/{{$prefix}}/{{end}}
+
+
+
+Index of s3://{{$bucketName}}/{{if $prefix}}{{$prefix}}/{{end}}
+
+
+
+ Filename |
+ Size |
+ Created |
+ Download |
+
+
+
+ {{ $trimmedPrefix := trimPrefix $prefix }}
+ {{if $trimmedPrefix }}
+
+
+ ⮐..
+ |
+ |
+ |
+ |
+
+ {{else}}
+
+
+ ⮐..
+ |
+ |
+ |
+ |
+
+ {{end}}
+ {{range .Objects}}
+
+
+ {{if .IsDir}}
+ 🗀
+
+ {{.FileName}}/
+
+ {{else}}
+ 🗎
+
+ {{.FileName}}
+
+ {{end}}
+ |
+ {{if not .IsDir}}{{ formatSize .Size }}{{end}} |
+ {{if not .IsDir}}{{ formatTimestamp .Created }}{{end}} |
+
+ {{ if not .IsDir }}
+
+ Link
+
+ {{ end }}
+ |
+
+ {{end}}
+
+
+
+
diff --git a/internal/templates/template.go b/internal/templates/template.go
new file mode 100644
index 0000000..b9885e6
--- /dev/null
+++ b/internal/templates/template.go
@@ -0,0 +1,6 @@
+package templates
+
+import _ "embed"
+
+//go:embed index.gotmpl
+var DefaultIndexTemplate string
diff --git a/tokens/bearer-token.go b/tokens/bearer-token.go
index b01860d..880a100 100644
--- a/tokens/bearer-token.go
+++ b/tokens/bearer-token.go
@@ -52,8 +52,8 @@ func BearerTokenFromCookie(h *fasthttp.RequestHeader) []byte {
// StoreBearerTokenAppCtx extracts a bearer token from the header or cookie and stores
// it in the application context.
-func StoreBearerTokenAppCtx(ctx context.Context, req *fasthttp.RequestCtx) (context.Context, error) {
- tkn, err := fetchBearerToken(req)
+func StoreBearerTokenAppCtx(ctx context.Context, c *fasthttp.RequestCtx) (context.Context, error) {
+ tkn, err := fetchBearerToken(c)
if err != nil {
return nil, err
}
diff --git a/tree/tree.go b/tree/tree.go
index a9135eb..162f41f 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -2,11 +2,13 @@ package tree
import (
"context"
+ "errors"
"fmt"
"strings"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api/layer"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
@@ -20,6 +22,7 @@ type (
// Each method must return ErrNodeNotFound or ErrNodeAccessDenied if relevant.
ServiceClient interface {
GetNodes(ctx context.Context, p *GetNodesParams) ([]NodeResponse, error)
+ GetSubTree(ctx context.Context, bktInfo *data.BucketInfo, treeID string, rootID []uint64, depth uint32, sort bool) ([]NodeResponse, error)
}
treeNode struct {
@@ -29,6 +32,7 @@ type (
GetNodesParams struct {
CnrID cid.ID
+ BktInfo *data.BucketInfo
TreeID string
Path []string
Meta []string
@@ -54,6 +58,7 @@ const (
// keys for delete marker nodes.
isDeleteMarkerKV = "IsDeleteMarker"
+ sizeKV = "Size"
// versionTree -- ID of a tree with object versions.
versionTree = "version"
@@ -73,26 +78,28 @@ type Meta interface {
type NodeResponse interface {
GetMeta() []Meta
- GetTimestamp() uint64
+ GetTimestamp() []uint64
+ GetNodeID() []uint64
+ GetParentID() []uint64
}
func newTreeNode(nodeInfo NodeResponse) (*treeNode, error) {
- treeNode := &treeNode{
+ tNode := &treeNode{
Meta: make(map[string]string, len(nodeInfo.GetMeta())),
}
for _, kv := range nodeInfo.GetMeta() {
switch kv.GetKey() {
case oidKV:
- if err := treeNode.ObjID.DecodeString(string(kv.GetValue())); err != nil {
+ if err := tNode.ObjID.DecodeString(string(kv.GetValue())); err != nil {
return nil, err
}
default:
- treeNode.Meta[kv.GetKey()] = string(kv.GetValue())
+ tNode.Meta[kv.GetKey()] = string(kv.GetValue())
}
}
- return treeNode, nil
+ return tNode, nil
}
func (n *treeNode) Get(key string) (string, bool) {
@@ -106,29 +113,44 @@ func (n *treeNode) FileName() (string, bool) {
}
func newNodeVersion(node NodeResponse) (*api.NodeVersion, error) {
- treeNode, err := newTreeNode(node)
+ tNode, err := newTreeNode(node)
if err != nil {
return nil, fmt.Errorf("invalid tree node: %w", err)
}
- return newNodeVersionFromTreeNode(treeNode), nil
+ return newNodeVersionFromTreeNode(tNode), nil
}
func newNodeVersionFromTreeNode(treeNode *treeNode) *api.NodeVersion {
_, isDeleteMarker := treeNode.Get(isDeleteMarkerKV)
-
+ size, _ := treeNode.Get(sizeKV)
version := &api.NodeVersion{
BaseNodeVersion: api.BaseNodeVersion{
OID: treeNode.ObjID,
},
DeleteMarker: isDeleteMarker,
+ IsPrefixNode: size == "",
}
return version
}
func (c *Tree) GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName string) (*api.NodeVersion, error) {
- meta := []string{oidKV, isDeleteMarkerKV}
+ nodes, err := c.GetVersions(ctx, cnrID, objectName)
+ if err != nil {
+ return nil, err
+ }
+
+ latestNode, err := getLatestVersionNode(nodes)
+ if err != nil {
+ return nil, err
+ }
+
+ return newNodeVersion(latestNode)
+}
+
+func (c *Tree) GetVersions(ctx context.Context, cnrID *cid.ID, objectName string) ([]NodeResponse, error) {
+ meta := []string{oidKV, isDeleteMarkerKV, sizeKV}
path := pathFromName(objectName)
p := &GetNodesParams{
@@ -139,30 +161,24 @@ func (c *Tree) GetLatestVersion(ctx context.Context, cnrID *cid.ID, objectName s
LatestOnly: false,
AllAttrs: false,
}
- nodes, err := c.service.GetNodes(ctx, p)
- if err != nil {
- return nil, err
- }
- latestNode, err := getLatestNode(nodes)
- if err != nil {
- return nil, err
- }
-
- return newNodeVersion(latestNode)
+ return c.service.GetNodes(ctx, p)
}
-func getLatestNode(nodes []NodeResponse) (NodeResponse, error) {
+func getLatestVersionNode(nodes []NodeResponse) (NodeResponse, error) {
var (
maxCreationTime uint64
targetIndexNode = -1
)
for i, node := range nodes {
- currentCreationTime := node.GetTimestamp()
- if checkExistOID(node.GetMeta()) && currentCreationTime > maxCreationTime {
- maxCreationTime = currentCreationTime
+ if !checkExistOID(node.GetMeta()) {
+ continue
+ }
+
+ if currentCreationTime := getMaxTimestamp(node); currentCreationTime > maxCreationTime {
targetIndexNode = i
+ maxCreationTime = currentCreationTime
}
}
@@ -187,3 +203,145 @@ func checkExistOID(meta []Meta) bool {
func pathFromName(objectName string) []string {
return strings.Split(objectName, separator)
}
+
+func (c *Tree) GetSubTreeByPrefix(ctx context.Context, bktInfo *data.BucketInfo, prefix string, latestOnly bool) ([]NodeResponse, string, error) {
+ rootID, tailPrefix, err := c.determinePrefixNode(ctx, bktInfo, versionTree, prefix)
+ if err != nil {
+ return nil, "", err
+ }
+ subTree, err := c.service.GetSubTree(ctx, bktInfo, versionTree, rootID, 2, false)
+ if err != nil {
+ if errors.Is(err, layer.ErrNodeNotFound) {
+ return nil, "", nil
+ }
+ return nil, "", err
+ }
+
+ nodesMap := make(map[string][]NodeResponse, len(subTree))
+ for _, node := range subTree {
+ if MultiID(rootID).Equal(node.GetNodeID()) {
+ continue
+ }
+
+ fileName := GetFilename(node)
+ if !strings.HasPrefix(fileName, tailPrefix) {
+ continue
+ }
+
+ nodes := nodesMap[fileName]
+
+ // Add all nodes if flag latestOnly is false.
+ // Add all intermediate nodes
+ // and only latest leaf (object) nodes. To do this store and replace last leaf (object) node in nodes[0]
+ if len(nodes) == 0 {
+ nodes = []NodeResponse{node}
+ } else if !latestOnly || isIntermediate(node) {
+ nodes = append(nodes, node)
+ } else if isIntermediate(nodes[0]) {
+ nodes = append([]NodeResponse{node}, nodes...)
+ } else if getMaxTimestamp(node) > getMaxTimestamp(nodes[0]) {
+ nodes[0] = node
+ }
+
+ nodesMap[fileName] = nodes
+ }
+
+ result := make([]NodeResponse, 0, len(subTree))
+ for _, nodes := range nodesMap {
+ result = append(result, nodes...)
+ }
+
+ return result, strings.TrimSuffix(prefix, tailPrefix), nil
+}
+
+func (c *Tree) determinePrefixNode(ctx context.Context, bktInfo *data.BucketInfo, treeID, prefix string) ([]uint64, string, error) {
+ rootID := []uint64{0}
+ path := strings.Split(prefix, separator)
+ tailPrefix := path[len(path)-1]
+
+ if len(path) > 1 {
+ var err error
+ rootID, err = c.getPrefixNodeID(ctx, bktInfo, treeID, path[:len(path)-1])
+ if err != nil {
+ return nil, "", err
+ }
+ }
+
+ return rootID, tailPrefix, nil
+}
+
+func (c *Tree) getPrefixNodeID(ctx context.Context, bktInfo *data.BucketInfo, treeID string, prefixPath []string) ([]uint64, error) {
+ p := &GetNodesParams{
+ CnrID: bktInfo.CID,
+ BktInfo: bktInfo,
+ TreeID: treeID,
+ Path: prefixPath,
+ LatestOnly: false,
+ AllAttrs: true,
+ }
+ nodes, err := c.service.GetNodes(ctx, p)
+ if err != nil {
+ return nil, err
+ }
+
+ var intermediateNodes []uint64
+ for _, node := range nodes {
+ if isIntermediate(node) {
+ intermediateNodes = append(intermediateNodes, node.GetNodeID()...)
+ }
+ }
+
+ if len(intermediateNodes) == 0 {
+ return nil, layer.ErrNodeNotFound
+ }
+
+ return intermediateNodes, nil
+}
+
+func GetFilename(node NodeResponse) string {
+ for _, kv := range node.GetMeta() {
+ if kv.GetKey() == FileNameKey {
+ return string(kv.GetValue())
+ }
+ }
+
+ return ""
+}
+
+func isIntermediate(node NodeResponse) bool {
+ if len(node.GetMeta()) != 1 {
+ return false
+ }
+
+ return node.GetMeta()[0].GetKey() == FileNameKey
+}
+
+func getMaxTimestamp(node NodeResponse) uint64 {
+ var maxTimestamp uint64
+
+ for _, timestamp := range node.GetTimestamp() {
+ if timestamp > maxTimestamp {
+ maxTimestamp = timestamp
+ }
+ }
+
+ return maxTimestamp
+}
+
+type MultiID []uint64
+
+func (m MultiID) Equal(id MultiID) bool {
+ seen := make(map[uint64]struct{}, len(m))
+
+ for i := range m {
+ seen[m[i]] = struct{}{}
+ }
+
+ for i := range id {
+ if _, ok := seen[id[i]]; !ok {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/tree/tree_test.go b/tree/tree_test.go
index 7cd2314..69ac5f6 100644
--- a/tree/tree_test.go
+++ b/tree/tree_test.go
@@ -24,8 +24,8 @@ type nodeResponse struct {
timestamp uint64
}
-func (n nodeResponse) GetTimestamp() uint64 {
- return n.timestamp
+func (n nodeResponse) GetTimestamp() []uint64 {
+ return []uint64{n.timestamp}
}
func (n nodeResponse) GetMeta() []Meta {
@@ -36,6 +36,13 @@ func (n nodeResponse) GetMeta() []Meta {
return res
}
+func (n nodeResponse) GetNodeID() []uint64 {
+ return nil
+}
+func (n nodeResponse) GetParentID() []uint64 {
+ return nil
+}
+
func TestGetLatestNode(t *testing.T) {
for _, tc := range []struct {
name string
@@ -130,7 +137,7 @@ func TestGetLatestNode(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
- actualNode, err := getLatestNode(tc.nodes)
+ actualNode, err := getLatestVersionNode(tc.nodes)
if tc.error {
require.Error(t, err)
return
From 495f7455355c11d3f4262ea61044a22bd45c301a Mon Sep 17 00:00:00 2001
From: Nikita Zinkevich
Date: Wed, 18 Sep 2024 07:35:26 +0300
Subject: [PATCH 086/161] [#142] Fix multipart-objects download
Signed-off-by: Nikita Zinkevich
---
cmd/http-gw/app.go | 5 +-
internal/handler/frostfs_mock.go | 11 +-
internal/handler/handler.go | 19 +-
internal/handler/multipart.go | 31 +++
internal/handler/reader.go | 98 ++++---
internal/handler/utils.go | 9 +
internal/{ => service}/frostfs/frostfs.go | 4 +-
.../service/frostfs/multi_object_reader.go | 241 ++++++++++++++++++
.../frostfs/multi_object_reader_test.go | 137 ++++++++++
.../frostfs}/pool_wrapper.go | 2 +-
tree/tree_test.go | 22 +-
11 files changed, 517 insertions(+), 62 deletions(-)
rename internal/{ => service}/frostfs/frostfs.go (97%)
create mode 100644 internal/service/frostfs/multi_object_reader.go
create mode 100644 internal/service/frostfs/multi_object_reader_test.go
rename internal/{frostfs/services => service/frostfs}/pool_wrapper.go (99%)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 8d5930d..f8300ec 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -19,11 +19,10 @@ import (
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs"
- "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/service/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/templates"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
@@ -453,7 +452,7 @@ func (a *app) setHealthStatus() {
}
func (a *app) Serve() {
- handler := handler.New(a.AppParams(), a.settings, tree.NewTree(services.NewPoolWrapper(a.treePool)))
+ handler := handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool)))
// Configure router.
a.configureRouter(handler)
diff --git a/internal/handler/frostfs_mock.go b/internal/handler/frostfs_mock.go
index 9f4378a..b60915e 100644
--- a/internal/handler/frostfs_mock.go
+++ b/internal/handler/frostfs_mock.go
@@ -229,6 +229,10 @@ func (t *TestFrostFS) SearchObjects(_ context.Context, prm PrmObjectSearch) (Res
return &resObjectSearchMock{res: res}, nil
}
+func (t *TestFrostFS) InitMultiObjectReader(context.Context, PrmInitMultiObjectReader) (io.Reader, error) {
+ return nil, nil
+}
+
func isMatched(attributes []object.Attribute, filter object.SearchFilter) bool {
for _, attr := range attributes {
if attr.Key() == filter.Header() {
@@ -269,10 +273,3 @@ func (t *TestFrostFS) isAllowed(cnrID cid.ID, userID user.ID, op acl.Op, objID o
}
return false
}
-
-func newAddress(cnr cid.ID, obj oid.ID) oid.Address {
- var addr oid.Address
- addr.SetContainer(cnr)
- addr.SetObject(obj)
- return addr
-}
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 8b07af0..c680706 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -119,6 +119,14 @@ type PrmObjectSearch struct {
Filters object.SearchFilters
}
+type PrmInitMultiObjectReader struct {
+ // payload range
+ Off, Ln uint64
+
+ Addr oid.Address
+ Bearer *bearer.Token
+}
+
type ResObjectSearch interface {
Read(buf []oid.ID) (int, error)
Iterate(f func(oid.ID) bool) error
@@ -140,6 +148,8 @@ type FrostFS interface {
RangeObject(context.Context, PrmObjectRange) (io.ReadCloser, error)
CreateObject(context.Context, PrmObjectCreate) (oid.ID, error)
SearchObjects(context.Context, PrmObjectSearch) (ResObjectSearch, error)
+ InitMultiObjectReader(ctx context.Context, p PrmInitMultiObjectReader) (io.Reader, error)
+
utils.EpochInfoFetcher
}
@@ -201,9 +211,7 @@ func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, requ
return
}
- var addr oid.Address
- addr.SetContainer(bktInfo.CID)
- addr.SetObject(*objID)
+ addr := newAddress(bktInfo.CID, *objID)
f(ctx, *h.newRequest(c, log), addr)
}
@@ -256,10 +264,7 @@ func (h *Handler) byObjectName(c *fasthttp.RequestCtx, f func(context.Context, r
response.Error(c, "object deleted", fasthttp.StatusNotFound)
return
}
-
- var addr oid.Address
- addr.SetContainer(bktInfo.CID)
- addr.SetObject(foundOid.OID)
+ addr := newAddress(bktInfo.CID, foundOid.OID)
f(ctx, *h.newRequest(c, log), addr)
}
diff --git a/internal/handler/multipart.go b/internal/handler/multipart.go
index de9242f..213286c 100644
--- a/internal/handler/multipart.go
+++ b/internal/handler/multipart.go
@@ -1,13 +1,17 @@
package handler
import (
+ "errors"
"io"
+ "strconv"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/multipart"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"go.uber.org/zap"
)
+const attributeMultipartObjectSize = "S3-Multipart-Object-Size"
+
// MultipartFile provides standard ReadCloser interface and also allows one to
// get file name, it's used for multipart uploads.
type MultipartFile interface {
@@ -45,3 +49,30 @@ func fetchMultipartFile(l *zap.Logger, r io.Reader, boundary string) (MultipartF
return part, nil
}
}
+
+// getPayload returns initial payload if object is not multipart else composes new reader with parts data.
+func (h *Handler) getPayload(p getMultiobjectBodyParams) (io.ReadCloser, uint64, error) {
+ cid, ok := p.obj.Header.ContainerID()
+ if !ok {
+ return nil, 0, errors.New("no container id set")
+ }
+ oid, ok := p.obj.Header.ID()
+ if !ok {
+ return nil, 0, errors.New("no object id set")
+ }
+ size, err := strconv.ParseUint(p.strSize, 10, 64)
+ if err != nil {
+ return nil, 0, err
+ }
+ ctx := p.req.RequestCtx
+ params := PrmInitMultiObjectReader{
+ Addr: newAddress(cid, oid),
+ Bearer: bearerToken(ctx),
+ }
+ payload, err := h.frostfs.InitMultiObjectReader(ctx, params)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return io.NopCloser(payload), size, nil
+}
diff --git a/internal/handler/reader.go b/internal/handler/reader.go
index d901f25..50121c9 100644
--- a/internal/handler/reader.go
+++ b/internal/handler/reader.go
@@ -47,20 +47,26 @@ func readContentType(maxSize uint64, rInit func(uint64) (io.Reader, error)) (str
return http.DetectContentType(buf), buf, err // to not lose io.EOF
}
-func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oid.Address) {
+type getMultiobjectBodyParams struct {
+ obj *Object
+ req request
+ strSize string
+}
+
+func (h *Handler) receiveFile(ctx context.Context, req request, objAddress oid.Address) {
var (
- err error
- dis = "inline"
- start = time.Now()
- filename string
- filepath string
+ shouldDownload = req.QueryArgs().GetBool("download")
+ start = time.Now()
+ filename string
+ filepath string
+ contentType string
)
prm := PrmObjectGet{
PrmAuth: PrmAuth{
BearerToken: bearerToken(ctx),
},
- Address: objectAddress,
+ Address: objAddress,
}
rObj, err := h.frostfs.GetObject(ctx, prm)
@@ -70,15 +76,9 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
}
// we can't close reader in this function, so how to do it?
-
- if req.Request.URI().QueryArgs().GetBool("download") {
- dis = "attachment"
- }
-
+ req.setIDs(rObj.Header)
+ payload := rObj.Payload
payloadSize := rObj.Header.PayloadSize()
-
- req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
- var contentType string
for _, attr := range rObj.Header.Attributes() {
key := attr.Key()
val := attr.Value()
@@ -93,31 +93,41 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
case object.AttributeFileName:
filename = val
case object.AttributeTimestamp:
- value, err := strconv.ParseInt(val, 10, 64)
- if err != nil {
- req.log.Info(logs.CouldntParseCreationDate,
- zap.String("key", key),
+ if err = req.setTimestamp(val); err != nil {
+ req.log.Error(logs.CouldntParseCreationDate,
zap.String("val", val),
zap.Error(err))
- continue
}
- req.Response.Header.Set(fasthttp.HeaderLastModified,
- time.Unix(value, 0).UTC().Format(http.TimeFormat))
case object.AttributeContentType:
contentType = val
case object.AttributeFilePath:
filepath = val
+ case attributeMultipartObjectSize:
+ payload, payloadSize, err = h.getPayload(getMultiobjectBodyParams{
+ obj: rObj,
+ req: req,
+ strSize: val,
+ })
+ if err != nil {
+ req.handleFrostFSErr(err, start)
+ return
+ }
}
}
+ if filename == "" {
+ filename = filepath
+ }
- idsToResponse(&req.Response, &rObj.Header)
+ req.setDisposition(shouldDownload, filename)
+
+ req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
if len(contentType) == 0 {
// determine the Content-Type from the payload head
var payloadHead []byte
contentType, payloadHead, err = readContentType(payloadSize, func(uint64) (io.Reader, error) {
- return rObj.Payload, nil
+ return payload, nil
})
if err != nil && err != io.EOF {
req.log.Error(logs.CouldNotDetectContentTypeFromPayload, zap.Error(err))
@@ -129,20 +139,46 @@ func (h *Handler) receiveFile(ctx context.Context, req request, objectAddress oi
var headReader io.Reader = bytes.NewReader(payloadHead)
if err != io.EOF { // otherwise, we've already read full payload
- headReader = io.MultiReader(headReader, rObj.Payload)
+ headReader = io.MultiReader(headReader, payload)
}
// note: we could do with io.Reader, but SetBodyStream below closes body stream
// if it implements io.Closer and that's useful for us.
- rObj.Payload = readCloser{headReader, rObj.Payload}
+ payload = readCloser{headReader, payload}
}
req.SetContentType(contentType)
+ req.Response.SetBodyStream(payload, int(payloadSize))
+}
- if filename == "" {
- filename = filepath
+func (r *request) setIDs(obj object.Object) {
+ objID, _ := obj.ID()
+ cnrID, _ := obj.ContainerID()
+ r.Response.Header.Set(hdrObjectID, objID.String())
+ r.Response.Header.Set(hdrOwnerID, obj.OwnerID().String())
+ r.Response.Header.Set(hdrContainerID, cnrID.String())
+}
+
+func (r *request) setDisposition(shouldDownload bool, filename string) {
+ const (
+ inlineDisposition = "inline"
+ attachmentDisposition = "attachment"
+ )
+
+ dis := inlineDisposition
+ if shouldDownload {
+ dis = attachmentDisposition
}
- req.Response.Header.Set(fasthttp.HeaderContentDisposition, dis+"; filename="+path.Base(filename))
-
- req.Response.SetBodyStream(rObj.Payload, int(payloadSize))
+ r.Response.Header.Set(fasthttp.HeaderContentDisposition, dis+"; filename="+path.Base(filename))
+}
+
+func (r *request) setTimestamp(timestamp string) error {
+ value, err := strconv.ParseInt(timestamp, 10, 64)
+ if err != nil {
+ return err
+ }
+ r.Response.Header.Set(fasthttp.HeaderLastModified,
+ time.Unix(value, 0).UTC().Format(http.TimeFormat))
+
+ return nil
}
diff --git a/internal/handler/utils.go b/internal/handler/utils.go
index 7fa1158..a944b67 100644
--- a/internal/handler/utils.go
+++ b/internal/handler/utils.go
@@ -12,6 +12,8 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -90,3 +92,10 @@ func logAndSendBucketError(c *fasthttp.RequestCtx, log *zap.Logger, err error) {
}
response.Error(c, "could not get bucket: "+err.Error(), fasthttp.StatusBadRequest)
}
+
+func newAddress(cnr cid.ID, obj oid.ID) oid.Address {
+ var addr oid.Address
+ addr.SetContainer(cnr)
+ addr.SetObject(obj)
+ return addr
+}
diff --git a/internal/frostfs/frostfs.go b/internal/service/frostfs/frostfs.go
similarity index 97%
rename from internal/frostfs/frostfs.go
rename to internal/service/frostfs/frostfs.go
index 6cf162a..c7e56a4 100644
--- a/internal/frostfs/frostfs.go
+++ b/internal/service/frostfs/frostfs.go
@@ -33,9 +33,9 @@ func NewFrostFS(p *pool.Pool) *FrostFS {
}
// Container implements frostfs.FrostFS interface method.
-func (x *FrostFS) Container(ctx context.Context, layerPrm handler.PrmContainer) (*container.Container, error) {
+func (x *FrostFS) Container(ctx context.Context, containerPrm handler.PrmContainer) (*container.Container, error) {
prm := pool.PrmContainerGet{
- ContainerID: layerPrm.ContainerID,
+ ContainerID: containerPrm.ContainerID,
}
res, err := x.pool.GetContainer(ctx, prm)
diff --git a/internal/service/frostfs/multi_object_reader.go b/internal/service/frostfs/multi_object_reader.go
new file mode 100644
index 0000000..93f1f60
--- /dev/null
+++ b/internal/service/frostfs/multi_object_reader.go
@@ -0,0 +1,241 @@
+package frostfs
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+)
+
+// PartInfo is upload information about part.
+type PartInfo struct {
+ Key string `json:"key"`
+ UploadID string `json:"uploadId"`
+ Number int `json:"number"`
+ OID oid.ID `json:"oid"`
+ Size uint64 `json:"size"`
+ ETag string `json:"etag"`
+ MD5 string `json:"md5"`
+ Created time.Time `json:"created"`
+}
+
+type GetFrostFSParams struct {
+ // payload range
+ Off, Ln uint64
+ Addr oid.Address
+}
+
+type PartObj struct {
+ OID oid.ID
+ Size uint64
+}
+
+type readerInitiator interface {
+ InitFrostFSObjectPayloadReader(ctx context.Context, p GetFrostFSParams) (io.ReadCloser, error)
+}
+
+// MultiObjectReader implements io.Reader of payloads of the object list stored in the FrostFS network.
+type MultiObjectReader struct {
+ ctx context.Context
+
+ layer readerInitiator
+
+ startPartOffset uint64
+ endPartLength uint64
+
+ prm GetFrostFSParams
+
+ curIndex int
+ curReader io.ReadCloser
+
+ parts []PartObj
+}
+
+type MultiObjectReaderConfig struct {
+ Initiator readerInitiator
+
+ // the offset of complete object and total size to read
+ Off, Ln uint64
+
+ Addr oid.Address
+ Parts []PartObj
+}
+
+var (
+ errOffsetIsOutOfRange = errors.New("offset is out of payload range")
+ errLengthIsOutOfRange = errors.New("length is out of payload range")
+ errEmptyPartsList = errors.New("empty parts list")
+ errorZeroRangeLength = errors.New("zero range length")
+)
+
+func (x *FrostFS) InitMultiObjectReader(ctx context.Context, p handler.PrmInitMultiObjectReader) (io.Reader, error) {
+ combinedObj, err := x.GetObject(ctx, handler.PrmObjectGet{
+ PrmAuth: handler.PrmAuth{BearerToken: p.Bearer},
+ Address: p.Addr,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("get combined object '%s': %w", p.Addr.Object().EncodeToString(), err)
+ }
+
+ var parts []*PartInfo
+ if err = json.NewDecoder(combinedObj.Payload).Decode(&parts); err != nil {
+ return nil, fmt.Errorf("unmarshal combined object parts: %w", err)
+ }
+
+ objParts := make([]PartObj, len(parts))
+ for i, part := range parts {
+ objParts[i] = PartObj{
+ OID: part.OID,
+ Size: part.Size,
+ }
+ }
+
+ return NewMultiObjectReader(ctx, MultiObjectReaderConfig{
+ Initiator: x,
+ Off: p.Off,
+ Ln: p.Ln,
+ Parts: objParts,
+ Addr: p.Addr,
+ })
+}
+
+func NewMultiObjectReader(ctx context.Context, cfg MultiObjectReaderConfig) (*MultiObjectReader, error) {
+ if len(cfg.Parts) == 0 {
+ return nil, errEmptyPartsList
+ }
+
+ r := &MultiObjectReader{
+ ctx: ctx,
+ layer: cfg.Initiator,
+ prm: GetFrostFSParams{
+ Addr: cfg.Addr,
+ },
+ parts: cfg.Parts,
+ }
+
+ if cfg.Off+cfg.Ln == 0 {
+ return r, nil
+ }
+
+ if cfg.Off > 0 && cfg.Ln == 0 {
+ return nil, errorZeroRangeLength
+ }
+
+ startPartIndex, startPartOffset := findStartPart(cfg)
+ if startPartIndex == -1 {
+ return nil, errOffsetIsOutOfRange
+ }
+ r.startPartOffset = startPartOffset
+
+ endPartIndex, endPartLength := findEndPart(cfg)
+ if endPartIndex == -1 {
+ return nil, errLengthIsOutOfRange
+ }
+ r.endPartLength = endPartLength
+
+ r.parts = cfg.Parts[startPartIndex : endPartIndex+1]
+
+ return r, nil
+}
+
+func findStartPart(cfg MultiObjectReaderConfig) (index int, offset uint64) {
+ position := cfg.Off
+ for i, part := range cfg.Parts {
+ // Strict inequality when searching for start position to avoid reading zero length part.
+ if position < part.Size {
+ return i, position
+ }
+ position -= part.Size
+ }
+
+ return -1, 0
+}
+
+func findEndPart(cfg MultiObjectReaderConfig) (index int, length uint64) {
+ position := cfg.Off + cfg.Ln
+ for i, part := range cfg.Parts {
+ // Non-strict inequality when searching for end position to avoid out of payload range error.
+ if position <= part.Size {
+ return i, position
+ }
+ position -= part.Size
+ }
+
+ return -1, 0
+}
+
+func (x *MultiObjectReader) Read(p []byte) (n int, err error) {
+ if x.curReader != nil {
+ n, err = x.curReader.Read(p)
+ if err != nil {
+ if closeErr := x.curReader.Close(); closeErr != nil {
+ return n, fmt.Errorf("%w (close err: %v)", err, closeErr)
+ }
+ }
+ if !errors.Is(err, io.EOF) {
+ return n, err
+ }
+
+ x.curIndex++
+ }
+
+ if x.curIndex == len(x.parts) {
+ return n, io.EOF
+ }
+
+ x.prm.Addr.SetObject(x.parts[x.curIndex].OID)
+
+ if x.curIndex == 0 {
+ x.prm.Off = x.startPartOffset
+ x.prm.Ln = x.parts[x.curIndex].Size - x.startPartOffset
+ }
+
+ if x.curIndex == len(x.parts)-1 {
+ x.prm.Ln = x.endPartLength - x.prm.Off
+ }
+
+ x.curReader, err = x.layer.InitFrostFSObjectPayloadReader(x.ctx, x.prm)
+ if err != nil {
+ return n, fmt.Errorf("init payload reader for the next part: %w", err)
+ }
+
+ x.prm.Off = 0
+ x.prm.Ln = 0
+
+ next, err := x.Read(p[n:])
+
+ return n + next, err
+}
+
+// InitFrostFSObjectPayloadReader initializes payload reader of the FrostFS object.
+// Zero range corresponds to full payload (panics if only offset is set).
+func (x *FrostFS) InitFrostFSObjectPayloadReader(ctx context.Context, p GetFrostFSParams) (io.ReadCloser, error) {
+ var prmAuth handler.PrmAuth
+
+ if p.Off+p.Ln != 0 {
+ prm := handler.PrmObjectRange{
+ PrmAuth: prmAuth,
+ PayloadRange: [2]uint64{p.Off, p.Ln},
+ Address: p.Addr,
+ }
+
+ return x.RangeObject(ctx, prm)
+ }
+
+ prm := handler.PrmObjectGet{
+ PrmAuth: prmAuth,
+ Address: p.Addr,
+ }
+
+ res, err := x.GetObject(ctx, prm)
+ if err != nil {
+ return nil, err
+ }
+
+ return res.Payload, nil
+}
diff --git a/internal/service/frostfs/multi_object_reader_test.go b/internal/service/frostfs/multi_object_reader_test.go
new file mode 100644
index 0000000..4127cdc
--- /dev/null
+++ b/internal/service/frostfs/multi_object_reader_test.go
@@ -0,0 +1,137 @@
+package frostfs
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "testing"
+
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
+ oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
+ "github.com/stretchr/testify/require"
+)
+
+type readerInitiatorMock struct {
+ parts map[oid.ID][]byte
+}
+
+func (r *readerInitiatorMock) InitFrostFSObjectPayloadReader(_ context.Context, p GetFrostFSParams) (io.ReadCloser, error) {
+ partPayload, ok := r.parts[p.Addr.Object()]
+ if !ok {
+ return nil, errors.New("part not found")
+ }
+
+ if p.Off+p.Ln == 0 {
+ return io.NopCloser(bytes.NewReader(partPayload)), nil
+ }
+
+ if p.Off > uint64(len(partPayload)-1) {
+ return nil, fmt.Errorf("invalid offset: %d/%d", p.Off, len(partPayload))
+ }
+
+ if p.Off+p.Ln > uint64(len(partPayload)) {
+ return nil, fmt.Errorf("invalid range: %d-%d/%d", p.Off, p.Off+p.Ln, len(partPayload))
+ }
+
+ return io.NopCloser(bytes.NewReader(partPayload[p.Off : p.Off+p.Ln])), nil
+}
+
+func prepareDataReader() ([]byte, []PartObj, *readerInitiatorMock) {
+ mockInitReader := &readerInitiatorMock{
+ parts: map[oid.ID][]byte{
+ oidtest.ID(): []byte("first part 1"),
+ oidtest.ID(): []byte("second part 2"),
+ oidtest.ID(): []byte("third part 3"),
+ },
+ }
+
+ var fullPayload []byte
+ parts := make([]PartObj, 0, len(mockInitReader.parts))
+ for id, payload := range mockInitReader.parts {
+ parts = append(parts, PartObj{OID: id, Size: uint64(len(payload))})
+ fullPayload = append(fullPayload, payload...)
+ }
+
+ return fullPayload, parts, mockInitReader
+}
+
+func TestMultiReader(t *testing.T) {
+ ctx := context.Background()
+
+ fullPayload, parts, mockInitReader := prepareDataReader()
+
+ for _, tc := range []struct {
+ name string
+ off uint64
+ ln uint64
+ err error
+ }{
+ {
+ name: "simple read all",
+ },
+ {
+ name: "simple read with length",
+ ln: uint64(len(fullPayload)),
+ },
+ {
+ name: "middle of parts",
+ off: parts[0].Size + 2,
+ ln: 4,
+ },
+ {
+ name: "first and second",
+ off: parts[0].Size - 4,
+ ln: 8,
+ },
+ {
+ name: "first and third",
+ off: parts[0].Size - 4,
+ ln: parts[1].Size + 8,
+ },
+ {
+ name: "second part",
+ off: parts[0].Size,
+ ln: parts[1].Size,
+ },
+ {
+ name: "second and third",
+ off: parts[0].Size,
+ ln: parts[1].Size + parts[2].Size,
+ },
+ {
+ name: "offset out of range",
+ off: uint64(len(fullPayload) + 1),
+ ln: 1,
+ err: errOffsetIsOutOfRange,
+ },
+ {
+ name: "zero length",
+ off: parts[1].Size + 1,
+ ln: 0,
+ err: errorZeroRangeLength,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ multiReader, err := NewMultiObjectReader(ctx, MultiObjectReaderConfig{
+ Initiator: mockInitReader,
+ Parts: parts,
+ Off: tc.off,
+ Ln: tc.ln,
+ })
+ require.ErrorIs(t, err, tc.err)
+
+ if tc.err == nil {
+ off := tc.off
+ ln := tc.ln
+ if off+ln == 0 {
+ ln = uint64(len(fullPayload))
+ }
+ data, err := io.ReadAll(multiReader)
+ require.NoError(t, err)
+ require.Equal(t, fullPayload[off:off+ln], data)
+ }
+ })
+ }
+}
diff --git a/internal/frostfs/services/pool_wrapper.go b/internal/service/frostfs/pool_wrapper.go
similarity index 99%
rename from internal/frostfs/services/pool_wrapper.go
rename to internal/service/frostfs/pool_wrapper.go
index fa70f15..b978d73 100644
--- a/internal/frostfs/services/pool_wrapper.go
+++ b/internal/service/frostfs/pool_wrapper.go
@@ -1,4 +1,4 @@
-package services
+package frostfs
import (
"context"
diff --git a/tree/tree_test.go b/tree/tree_test.go
index 69ac5f6..62f9914 100644
--- a/tree/tree_test.go
+++ b/tree/tree_test.go
@@ -21,11 +21,11 @@ func (m nodeMeta) GetValue() []byte {
type nodeResponse struct {
meta []nodeMeta
- timestamp uint64
+ timestamp []uint64
}
func (n nodeResponse) GetTimestamp() []uint64 {
- return []uint64{n.timestamp}
+ return n.timestamp
}
func (n nodeResponse) GetMeta() []Meta {
@@ -59,7 +59,7 @@ func TestGetLatestNode(t *testing.T) {
name: "one node of the object version",
nodes: []NodeResponse{
nodeResponse{
- timestamp: 1,
+ timestamp: []uint64{1},
meta: []nodeMeta{
{
key: oidKV,
@@ -74,11 +74,11 @@ func TestGetLatestNode(t *testing.T) {
name: "one node of the object version and one node of the secondary object",
nodes: []NodeResponse{
nodeResponse{
- timestamp: 3,
+ timestamp: []uint64{3},
meta: []nodeMeta{},
},
nodeResponse{
- timestamp: 1,
+ timestamp: []uint64{1},
meta: []nodeMeta{
{
key: oidKV,
@@ -93,11 +93,11 @@ func TestGetLatestNode(t *testing.T) {
name: "all nodes represent a secondary object",
nodes: []NodeResponse{
nodeResponse{
- timestamp: 3,
+ timestamp: []uint64{3},
meta: []nodeMeta{},
},
nodeResponse{
- timestamp: 5,
+ timestamp: []uint64{5},
meta: []nodeMeta{},
},
},
@@ -107,7 +107,7 @@ func TestGetLatestNode(t *testing.T) {
name: "several nodes of different types and with different timestamp",
nodes: []NodeResponse{
nodeResponse{
- timestamp: 1,
+ timestamp: []uint64{1},
meta: []nodeMeta{
{
key: oidKV,
@@ -116,11 +116,11 @@ func TestGetLatestNode(t *testing.T) {
},
},
nodeResponse{
- timestamp: 3,
+ timestamp: []uint64{3},
meta: []nodeMeta{},
},
nodeResponse{
- timestamp: 4,
+ timestamp: []uint64{4},
meta: []nodeMeta{
{
key: oidKV,
@@ -129,7 +129,7 @@ func TestGetLatestNode(t *testing.T) {
},
},
nodeResponse{
- timestamp: 6,
+ timestamp: []uint64{6},
meta: []nodeMeta{},
},
},
From fc86ab3511a153b30b7ff45b15ff5865405786ca Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Tue, 8 Oct 2024 12:08:39 +0300
Subject: [PATCH 087/161] [#148] Add trace_id to logs
Signed-off-by: Roman Loginov
---
CHANGELOG.md | 3 +++
cmd/http-gw/app.go | 41 ++++++++++++++++++++++++------------
internal/handler/browse.go | 4 +++-
internal/handler/download.go | 9 ++++----
internal/handler/handler.go | 35 +++++++++++++++---------------
internal/handler/upload.go | 28 +++++++++++++-----------
utils/util.go | 32 ++++++++++++++++++++++++++++
7 files changed, 104 insertions(+), 48 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a489027..46e9c23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,10 @@
This document outlines major changes between releases.
## [Unreleased]
+
+### Added
- Support percent-encoding for GET queries (#134)
+- Add `trace_id` to logs (#148)
### Changed
- Update go version to 1.22 (#132)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index f8300ec..b26df89 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -42,6 +42,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/spf13/viper"
"github.com/valyala/fasthttp"
+ "go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"golang.org/x/exp/slices"
)
@@ -589,15 +590,15 @@ func (a *app) configureRouter(handler *handler.Handler) {
response.Error(r, "Method Not Allowed", fasthttp.StatusMethodNotAllowed)
}
- r.POST("/upload/{cid}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.Upload))))))
+ r.POST("/upload/{cid}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.Upload))))))
a.log.Info(logs.AddedPathUploadCid)
- r.GET("/get/{cid}/{oid:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAddressOrBucketName))))))
- r.HEAD("/get/{cid}/{oid:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAddressOrBucketName))))))
+ r.GET("/get/{cid}/{oid:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.DownloadByAddressOrBucketName))))))
+ r.HEAD("/get/{cid}/{oid:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.HeadByAddressOrBucketName))))))
a.log.Info(logs.AddedPathGetCidOid)
- r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadByAttribute))))))
- r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.HeadByAttribute))))))
+ r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.DownloadByAttribute))))))
+ r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.HeadByAttribute))))))
a.log.Info(logs.AddedPathGetByAttributeCidAttrKeyAttrVal)
- r.GET("/zip/{cid}/{prefix:*}", a.logger(a.canonicalizer(a.tokenizer(a.tracer(a.reqNamespace(handler.DownloadZipped))))))
+ r.GET("/zip/{cid}/{prefix:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.DownloadZipped))))))
a.log.Info(logs.AddedPathZipCidPrefix)
a.webServer.Handler = r.Handler
@@ -605,11 +606,24 @@ func (a *app) configureRouter(handler *handler.Handler) {
func (a *app) logger(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
- a.log.Info(logs.Request, zap.String("remote", req.RemoteAddr().String()),
+ requiredFields := []zap.Field{zap.Uint64("id", req.ID())}
+ reqCtx := utils.GetContextFromRequest(req)
+ if traceID := trace.SpanFromContext(reqCtx).SpanContext().TraceID(); traceID.IsValid() {
+ requiredFields = append(requiredFields, zap.String("trace_id", traceID.String()))
+ }
+ log := a.log.With(requiredFields...)
+
+ reqCtx = utils.SetReqLog(reqCtx, log)
+ utils.SetContextToRequest(reqCtx, req)
+
+ fields := []zap.Field{
+ zap.String("remote", req.RemoteAddr().String()),
zap.ByteString("method", req.Method()),
zap.ByteString("path", req.Path()),
zap.ByteString("query", req.QueryArgs().QueryString()),
- zap.Uint64("id", req.ID()))
+ }
+
+ log.Info(logs.Request, fields...)
h(req)
}
}
@@ -648,9 +662,12 @@ func (a *app) canonicalizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
func (a *app) tokenizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
- appCtx, err := tokens.StoreBearerTokenAppCtx(a.ctx, req)
+ reqCtx := utils.GetContextFromRequest(req)
+ appCtx, err := tokens.StoreBearerTokenAppCtx(reqCtx, req)
if err != nil {
- a.log.Error(logs.CouldNotFetchAndStoreBearerToken, zap.Uint64("id", req.ID()), zap.Error(err))
+ log := utils.GetReqLogOrDefault(reqCtx, a.log)
+
+ log.Error(logs.CouldNotFetchAndStoreBearerToken, zap.Error(err))
response.Error(req, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -661,9 +678,7 @@ func (a *app) tokenizer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
func (a *app) tracer(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
- appCtx := utils.GetContextFromRequest(req)
-
- appCtx, span := utils.StartHTTPServerSpan(appCtx, req, "REQUEST")
+ appCtx, span := utils.StartHTTPServerSpan(a.ctx, req, "REQUEST")
defer func() {
utils.SetHTTPTraceInfo(appCtx, span, req)
span.End()
diff --git a/internal/handler/browse.go b/internal/handler/browse.go
index e84fb04..c89d8c5 100644
--- a/internal/handler/browse.go
+++ b/internal/handler/browse.go
@@ -113,8 +113,10 @@ func urlencode(prefix, filename string) string {
}
func (h *Handler) browseObjects(c *fasthttp.RequestCtx, bucketInfo *data.BucketInfo, prefix string) {
- log := h.log.With(zap.String("bucket", bucketInfo.Name))
ctx := utils.GetContextFromRequest(c)
+ reqLog := utils.GetReqLogOrDefault(ctx, h.log)
+ log := reqLog.With(zap.String("bucket", bucketInfo.Name))
+
nodes, err := h.listObjects(ctx, bucketInfo, prefix)
if err != nil {
logAndSendBucketError(c, log, err)
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 88109a6..19380d4 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -84,16 +84,17 @@ func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
scid, _ := c.UserValue("cid").(string)
prefix, _ := c.UserValue("prefix").(string)
+ ctx := utils.GetContextFromRequest(c)
+ log := utils.GetReqLogOrDefault(ctx, h.log)
+
prefix, err := url.QueryUnescape(prefix)
if err != nil {
- h.log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("prefix", prefix), zap.Uint64("id", c.ID()), zap.Error(err))
+ log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("prefix", prefix), zap.Error(err))
response.Error(c, "could not unescape prefix: "+err.Error(), fasthttp.StatusBadRequest)
return
}
- log := h.log.With(zap.String("cid", scid), zap.String("prefix", prefix), zap.Uint64("id", c.ID()))
-
- ctx := utils.GetContextFromRequest(c)
+ log = log.With(zap.String("cid", scid), zap.String("prefix", prefix))
bktInfo, err := h.getBucketInfo(ctx, scid, log)
if err != nil {
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index c680706..62d0897 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -190,13 +190,12 @@ func New(params *AppParams, config Config, tree *tree.Tree) *Handler {
// byAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that
// prepares request and object address to it.
func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
- var (
- idCnr, _ = c.UserValue("cid").(string)
- idObj, _ = c.UserValue("oid").(string)
- log = h.log.With(zap.String("cid", idCnr), zap.String("oid", idObj))
- )
+ idCnr, _ := c.UserValue("cid").(string)
+ idObj, _ := c.UserValue("oid").(string)
ctx := utils.GetContextFromRequest(c)
+ reqLog := utils.GetReqLogOrDefault(ctx, h.log)
+ log := reqLog.With(zap.String("cid", idCnr), zap.String("oid", idObj))
bktInfo, err := h.getBucketInfo(ctx, idCnr, log)
if err != nil {
@@ -219,12 +218,13 @@ func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, requ
// byObjectName is a wrapper for function (e.g. request.headObject, request.receiveFile) that
// prepares request and object address to it.
func (h *Handler) byObjectName(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
- var (
- bucketname = c.UserValue("cid").(string)
- key = c.UserValue("oid").(string)
- log = h.log.With(zap.String("bucketname", bucketname), zap.String("key", key))
- download = c.QueryArgs().GetBool("download")
- )
+ bucketname := c.UserValue("cid").(string)
+ key := c.UserValue("oid").(string)
+ download := c.QueryArgs().GetBool("download")
+
+ ctx := utils.GetContextFromRequest(c)
+ reqLog := utils.GetReqLogOrDefault(ctx, h.log)
+ log := reqLog.With(zap.String("bucketname", bucketname), zap.String("key", key))
unescapedKey, err := url.QueryUnescape(key)
if err != nil {
@@ -232,8 +232,6 @@ func (h *Handler) byObjectName(c *fasthttp.RequestCtx, f func(context.Context, r
return
}
- ctx := utils.GetContextFromRequest(c)
-
bktInfo, err := h.getBucketInfo(ctx, bucketname, log)
if err != nil {
logAndSendBucketError(c, log, err)
@@ -275,23 +273,24 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, re
key, _ := c.UserValue("attr_key").(string)
val, _ := c.UserValue("attr_val").(string)
+ ctx := utils.GetContextFromRequest(c)
+ log := utils.GetReqLogOrDefault(ctx, h.log)
+
key, err := url.QueryUnescape(key)
if err != nil {
- h.log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_key", key), zap.Uint64("id", c.ID()), zap.Error(err))
+ log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_key", key), zap.Error(err))
response.Error(c, "could not unescape attr_key: "+err.Error(), fasthttp.StatusBadRequest)
return
}
val, err = url.QueryUnescape(val)
if err != nil {
- h.log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_val", val), zap.Uint64("id", c.ID()), zap.Error(err))
+ log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("attr_val", val), zap.Error(err))
response.Error(c, "could not unescape attr_val: "+err.Error(), fasthttp.StatusBadRequest)
return
}
- log := h.log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val))
-
- ctx := utils.GetContextFromRequest(c)
+ log = log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val))
bktInfo, err := h.getBucketInfo(ctx, scid, log)
if err != nil {
diff --git a/internal/handler/upload.go b/internal/handler/upload.go
index 6c0e117..867025d 100644
--- a/internal/handler/upload.go
+++ b/internal/handler/upload.go
@@ -45,16 +45,18 @@ func (pr *putResponse) encode(w io.Writer) error {
// Upload handles multipart upload request.
func (h *Handler) Upload(c *fasthttp.RequestCtx) {
var (
- file MultipartFile
- idObj oid.ID
- addr oid.Address
- scid, _ = c.UserValue("cid").(string)
- log = h.log.With(zap.String("cid", scid))
- bodyStream = c.RequestBodyStream()
- drainBuf = make([]byte, drainBufSize)
+ file MultipartFile
+ idObj oid.ID
+ addr oid.Address
)
+ scid, _ := c.UserValue("cid").(string)
+ bodyStream := c.RequestBodyStream()
+ drainBuf := make([]byte, drainBufSize)
+
ctx := utils.GetContextFromRequest(c)
+ reqLog := utils.GetReqLogOrDefault(ctx, h.log)
+ log := reqLog.With(zap.String("cid", scid))
bktInfo, err := h.getBucketInfo(ctx, scid, log)
if err != nil {
@@ -75,13 +77,15 @@ func (h *Handler) Upload(c *fasthttp.RequestCtx) {
zap.Error(err),
)
}()
+
boundary := string(c.Request.Header.MultipartFormBoundary())
- if file, err = fetchMultipartFile(h.log, bodyStream, boundary); err != nil {
+ if file, err = fetchMultipartFile(log, bodyStream, boundary); err != nil {
log.Error(logs.CouldNotReceiveMultipartForm, zap.Error(err))
response.Error(c, "could not receive multipart/form: "+err.Error(), fasthttp.StatusBadRequest)
return
}
- filtered, err := filterHeaders(h.log, &c.Request.Header)
+
+ filtered, err := filterHeaders(log, &c.Request.Header)
if err != nil {
log.Error(logs.CouldNotProcessHeaders, zap.Error(err))
response.Error(c, err.Error(), fasthttp.StatusBadRequest)
@@ -143,7 +147,7 @@ func (h *Handler) Upload(c *fasthttp.RequestCtx) {
}
if idObj, err = h.frostfs.CreateObject(ctx, prm); err != nil {
- h.handlePutFrostFSErr(c, err)
+ h.handlePutFrostFSErr(c, err, log)
return
}
@@ -174,11 +178,11 @@ func (h *Handler) Upload(c *fasthttp.RequestCtx) {
c.Response.Header.SetContentType(jsonHeader)
}
-func (h *Handler) handlePutFrostFSErr(r *fasthttp.RequestCtx, err error) {
+func (h *Handler) handlePutFrostFSErr(r *fasthttp.RequestCtx, err error, log *zap.Logger) {
statusCode, msg, additionalFields := response.FormErrorResponse("could not store file in frostfs", err)
logFields := append([]zap.Field{zap.Error(err)}, additionalFields...)
- h.log.Error(logs.CouldNotStoreFileInFrostfs, logFields...)
+ log.Error(logs.CouldNotStoreFileInFrostfs, logFields...)
response.Error(r, msg, statusCode)
}
diff --git a/utils/util.go b/utils/util.go
index d513817..b7f5e39 100644
--- a/utils/util.go
+++ b/utils/util.go
@@ -4,6 +4,7 @@ import (
"context"
"github.com/valyala/fasthttp"
+ "go.uber.org/zap"
)
// SetContextToRequest adds new context to fasthttp request.
@@ -15,3 +16,34 @@ func SetContextToRequest(ctx context.Context, c *fasthttp.RequestCtx) {
func GetContextFromRequest(c *fasthttp.RequestCtx) context.Context {
return c.UserValue("context").(context.Context)
}
+
+type ctxReqLoggerKeyType struct{}
+
+// SetReqLog sets child zap.Logger in the context.
+func SetReqLog(ctx context.Context, log *zap.Logger) context.Context {
+ if ctx == nil {
+ return nil
+ }
+ return context.WithValue(ctx, ctxReqLoggerKeyType{}, log)
+}
+
+// GetReqLog returns log if set.
+// If zap.Logger isn't set returns nil.
+func GetReqLog(ctx context.Context) *zap.Logger {
+ if ctx == nil {
+ return nil
+ } else if r, ok := ctx.Value(ctxReqLoggerKeyType{}).(*zap.Logger); ok {
+ return r
+ }
+ return nil
+}
+
+// GetReqLogOrDefault returns log from context, if it exists.
+// If the log is missing from the context, the default logger is returned.
+func GetReqLogOrDefault(ctx context.Context, defaultLog *zap.Logger) *zap.Logger {
+ log := GetReqLog(ctx)
+ if log == nil {
+ log = defaultLog
+ }
+ return log
+}
From 70846fdaecf6ee2867507ac8e75b82ac61cc35db Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Mon, 21 Oct 2024 11:39:14 +0300
Subject: [PATCH 088/161] [#157] Support the continuous use of interceptors
We can always add interceptors to the grpc
connection to the storage, since the actual
use will be controlled by the configuration
from the frostfs-observability library.
Signed-off-by: Roman Loginov
---
cmd/http-gw/settings.go | 16 +++++-----------
docs/gate-configuration.md | 2 +-
2 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index eab5b6b..476d658 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -589,18 +589,12 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
prmTree.SetMaxRequestAttempts(cfg.GetInt(cfgTreePoolMaxAttempts))
- var apiGRPCDialOpts []grpc.DialOption
- var treeGRPCDialOpts []grpc.DialOption
- if cfg.GetBool(cfgTracingEnabled) {
- interceptors := []grpc.DialOption{
- grpc.WithUnaryInterceptor(grpctracing.NewUnaryClientInteceptor()),
- grpc.WithStreamInterceptor(grpctracing.NewStreamClientInterceptor()),
- }
- treeGRPCDialOpts = append(treeGRPCDialOpts, interceptors...)
- apiGRPCDialOpts = append(apiGRPCDialOpts, interceptors...)
+ interceptors := []grpc.DialOption{
+ grpc.WithUnaryInterceptor(grpctracing.NewUnaryClientInteceptor()),
+ grpc.WithStreamInterceptor(grpctracing.NewStreamClientInterceptor()),
}
- prm.SetGRPCDialOptions(apiGRPCDialOpts...)
- prmTree.SetGRPCDialOptions(treeGRPCDialOpts...)
+ prm.SetGRPCDialOptions(interceptors...)
+ prmTree.SetGRPCDialOptions(interceptors...)
p, err := pool.NewPool(prm)
if err != nil {
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index e8d1f4b..b484f9d 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -271,7 +271,7 @@ tracing:
| Parameter | Type | SIGHUP reload | Default value | Description |
|--------------|----------|---------------|---------------|---------------------------------------------------------------------------------------------------------------------------------|
-| `enabled` | `bool` | no | `false` | Flag to enable the tracing. |
+| `enabled` | `bool` | yes | `false` | Flag to enable the tracing. |
| `exporter` | `string` | yes | | Trace collector type (`stdout` or `otlp_grpc` are supported). |
| `endpoint` | `string` | yes | | Address of collector endpoint for OTLP exporters. |
| `trusted_ca` | `string` | yes | | Path to certificate of a certification authority in pem format, that issued the TLS certificate of the telemetry remote server. |
From 8dc527296515bd255ab8d6920b21a1e2133eea98 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Thu, 24 Oct 2024 16:17:28 +0300
Subject: [PATCH 089/161] [#158] Rework app settings
Update settings by sighup using one lock/unlock operation
Signed-off-by: Denis Kirillov
---
cmd/http-gw/app.go | 150 ++++++++++++----------------------------
cmd/http-gw/settings.go | 32 +++++++++
2 files changed, 76 insertions(+), 106 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index b26df89..ead4a29 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -6,13 +6,11 @@ import (
"crypto/x509"
"errors"
"fmt"
- "io"
"net/http"
"os"
"os/signal"
"runtime/debug"
"strconv"
- "strings"
"sync"
"syscall"
"time"
@@ -159,23 +157,47 @@ func newApp(ctx context.Context, opt ...Option) App {
a.initResolver()
a.initMetrics()
a.initTracing(ctx)
- a.loadIndexPageTemplate()
return a
}
+func (a *app) initAppSettings() {
+ a.settings = &appSettings{
+ reconnectInterval: fetchReconnectInterval(a.cfg),
+ }
+ a.settings.update(a.cfg, a.log)
+}
+
+func (s *appSettings) update(v *viper.Viper, l *zap.Logger) {
+ defaultTimestamp := v.GetBool(cfgUploaderHeaderEnableDefaultTimestamp)
+ zipCompression := v.GetBool(cfgZipCompression)
+ returnIndexPage := v.GetBool(cfgIndexPageEnabled)
+ clientCut := v.GetBool(cfgClientCut)
+ bufferMaxSizeForPut := v.GetUint64(cfgBufferMaxSizeForPut)
+ namespaceHeader := v.GetString(cfgResolveNamespaceHeader)
+ defaultNamespaces := fetchDefaultNamespaces(v)
+ indexPage, indexEnabled := fetchIndexPageTemplate(v, l)
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.defaultTimestamp = defaultTimestamp
+ s.zipCompression = zipCompression
+ s.returnIndexPage = returnIndexPage
+ s.clientCut = clientCut
+ s.bufferMaxSizeForPut = bufferMaxSizeForPut
+ s.namespaceHeader = namespaceHeader
+ s.defaultNamespaces = defaultNamespaces
+ s.returnIndexPage = indexEnabled
+ s.indexPageTemplate = indexPage
+}
+
func (s *appSettings) DefaultTimestamp() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.defaultTimestamp
}
-func (s *appSettings) setDefaultTimestamp(val bool) {
- s.mu.Lock()
- s.defaultTimestamp = val
- s.mu.Unlock()
-}
-
func (s *appSettings) ZipCompression() bool {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -197,73 +219,33 @@ func (s *appSettings) IndexPageTemplate() string {
return s.indexPageTemplate
}
-func (s *appSettings) setZipCompression(val bool) {
- s.mu.Lock()
- s.zipCompression = val
- s.mu.Unlock()
-}
-
-func (s *appSettings) setReturnIndexPage(val bool) {
- s.mu.Lock()
- s.returnIndexPage = val
- s.mu.Unlock()
-}
-
-func (s *appSettings) setIndexTemplate(val string) {
- s.mu.Lock()
- s.indexPageTemplate = val
- s.mu.Unlock()
-}
-
-func (a *app) loadIndexPageTemplate() {
- if !a.settings.IndexPageEnabled() {
- return
- }
- reader, err := os.Open(a.cfg.GetString(cfgIndexPageTemplatePath))
- if err != nil {
- a.settings.setIndexTemplate("")
- a.log.Warn(logs.FailedToReadIndexPageTemplate, zap.Error(err))
- return
- }
- tmpl, err := io.ReadAll(reader)
- if err != nil {
- a.settings.setIndexTemplate("")
- a.log.Warn(logs.FailedToReadIndexPageTemplate, zap.Error(err))
- return
- }
- a.settings.setIndexTemplate(string(tmpl))
- a.log.Info(logs.SetCustomIndexPageTemplate)
-}
-
func (s *appSettings) ClientCut() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.clientCut
}
-func (s *appSettings) setClientCut(val bool) {
- s.mu.Lock()
- s.clientCut = val
- s.mu.Unlock()
-}
-
func (s *appSettings) BufferMaxSizeForPut() uint64 {
s.mu.RLock()
defer s.mu.RUnlock()
return s.bufferMaxSizeForPut
}
-func (s *appSettings) setBufferMaxSizeForPut(val uint64) {
- s.mu.Lock()
- s.bufferMaxSizeForPut = val
- s.mu.Unlock()
+func (s *appSettings) NamespaceHeader() string {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.namespaceHeader
}
-func (a *app) initAppSettings() {
- a.settings = &appSettings{
- reconnectInterval: fetchReconnectInterval(a.cfg),
+func (s *appSettings) FormContainerZone(ns string) (zone string, isDefault bool) {
+ s.mu.RLock()
+ namespaces := s.defaultNamespaces
+ s.mu.RUnlock()
+ if slices.Contains(namespaces, ns) {
+ return v2container.SysAttributeZoneDefault, true
}
- a.updateSettings()
+
+ return ns + ".ns", false
}
func (a *app) initResolver() {
@@ -539,26 +521,15 @@ func (a *app) configReload(ctx context.Context) {
a.stopServices()
a.startServices()
- a.updateSettings()
+ a.settings.update(a.cfg, a.log)
a.metrics.SetEnabled(a.cfg.GetBool(cfgPrometheusEnabled))
a.initTracing(ctx)
- a.loadIndexPageTemplate()
a.setHealthStatus()
a.log.Info(logs.SIGHUPConfigReloadCompleted)
}
-func (a *app) updateSettings() {
- a.settings.setDefaultTimestamp(a.cfg.GetBool(cfgUploaderHeaderEnableDefaultTimestamp))
- a.settings.setZipCompression(a.cfg.GetBool(cfgZipCompression))
- a.settings.setReturnIndexPage(a.cfg.GetBool(cfgIndexPageEnabled))
- a.settings.setClientCut(a.cfg.GetBool(cfgClientCut))
- a.settings.setBufferMaxSizeForPut(a.cfg.GetUint64(cfgBufferMaxSizeForPut))
- a.settings.setNamespaceHeader(a.cfg.GetString(cfgResolveNamespaceHeader))
- a.settings.setDefaultNamespaces(a.cfg.GetStringSlice(cfgResolveDefaultNamespaces))
-}
-
func (a *app) startServices() {
pprofConfig := metrics.Config{Enabled: a.cfg.GetBool(cfgPprofEnabled), Address: a.cfg.GetString(cfgPprofAddress)}
pprofService := metrics.NewPprofService(a.log, pprofConfig)
@@ -847,39 +818,6 @@ func (a *app) setRuntimeParameters() {
}
}
-func (s *appSettings) NamespaceHeader() string {
- s.mu.RLock()
- defer s.mu.RUnlock()
- return s.namespaceHeader
-}
-
-func (s *appSettings) setNamespaceHeader(nsHeader string) {
- s.mu.Lock()
- s.namespaceHeader = nsHeader
- s.mu.Unlock()
-}
-
-func (s *appSettings) FormContainerZone(ns string) (zone string, isDefault bool) {
- s.mu.RLock()
- namespaces := s.defaultNamespaces
- s.mu.RUnlock()
- if slices.Contains(namespaces, ns) {
- return v2container.SysAttributeZoneDefault, true
- }
-
- return ns + ".ns", false
-}
-
-func (s *appSettings) setDefaultNamespaces(namespaces []string) {
- for i := range namespaces { // to be set namespaces in env variable as `HTTP_GW_RESOLVE_BUCKET_DEFAULT_NAMESPACES="" "root"`
- namespaces[i] = strings.Trim(namespaces[i], "\"")
- }
-
- s.mu.Lock()
- s.defaultNamespaces = namespaces
- s.mu.Unlock()
-}
-
func (a *app) scheduleReconnect(ctx context.Context, srv *fasthttp.Server) {
go func() {
t := time.NewTicker(a.settings.reconnectInterval)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 476d658..e777f67 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/hex"
"fmt"
+ "io"
"math"
"os"
"path"
@@ -505,6 +506,37 @@ func fetchReconnectInterval(cfg *viper.Viper) time.Duration {
return reconnect
}
+func fetchIndexPageTemplate(v *viper.Viper, l *zap.Logger) (string, bool) {
+ if !v.GetBool(cfgIndexPageEnabled) {
+ return "", false
+ }
+
+ reader, err := os.Open(v.GetString(cfgIndexPageTemplatePath))
+ if err != nil {
+ l.Warn(logs.FailedToReadIndexPageTemplate, zap.Error(err))
+ return "", true
+ }
+
+ tmpl, err := io.ReadAll(reader)
+ if err != nil {
+ l.Warn(logs.FailedToReadIndexPageTemplate, zap.Error(err))
+ return "", true
+ }
+
+ l.Info(logs.SetCustomIndexPageTemplate)
+ return string(tmpl), true
+}
+
+func fetchDefaultNamespaces(v *viper.Viper) []string {
+ namespaces := v.GetStringSlice(cfgResolveDefaultNamespaces)
+
+ for i := range namespaces { // to be set namespaces in env variable as `HTTP_GW_RESOLVE_BUCKET_DEFAULT_NAMESPACES="" "root"`
+ namespaces[i] = strings.Trim(namespaces[i], "\"")
+ }
+
+ return namespaces
+}
+
func fetchServers(v *viper.Viper, log *zap.Logger) []ServerInfo {
var servers []ServerInfo
seen := make(map[string]struct{})
From 901b8ff95b6ca8eb96880dc663b86014d8915d56 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Thu, 24 Oct 2024 16:18:24 +0300
Subject: [PATCH 090/161] [#158] Fix integration test compilation error
Signed-off-by: Denis Kirillov
---
cmd/http-gw/integration_test.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cmd/http-gw/integration_test.go b/cmd/http-gw/integration_test.go
index e888ed6..79a2da5 100644
--- a/cmd/http-gw/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -102,7 +102,7 @@ func runServer(pathToWallet string) (App, context.CancelFunc) {
v.Set(cfgWalletPath, pathToWallet)
v.Set(cfgWalletPassphrase, "")
- l, lvl := newStdoutLogger(zapcore.DebugLevel)
+ l, lvl := newStdoutLogger(v, zapcore.DebugLevel)
application := newApp(cancelCtx, WithConfig(v), WithLogger(l, lvl))
go application.Serve()
@@ -525,7 +525,7 @@ func putObject(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID
id, err := clientPool.PutObject(ctx, prm)
require.NoError(t, err)
- return id
+ return id.ObjectID
}
func makeBearerToken(t *testing.T, key *keys.PrivateKey, ownerID user.ID, version string) string {
From 46c63edd67af9f1676283db5d89e6b72f7bd32d4 Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Thu, 24 Oct 2024 17:27:32 +0300
Subject: [PATCH 091/161] [#158] Support cors
Signed-off-by: Denis Kirillov
---
CHANGELOG.md | 1 +
cmd/http-gw/app.go | 158 +++++++++++++++++++++++++++++++++----
cmd/http-gw/settings.go | 19 +++++
config/config.env | 7 ++
config/config.yaml | 8 ++
docs/gate-configuration.md | 24 ++++++
6 files changed, 201 insertions(+), 16 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46e9c23..0990b51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ This document outlines major changes between releases.
### Added
- Support percent-encoding for GET queries (#134)
- Add `trace_id` to logs (#148)
+- Add `cors` config params (#158)
### Changed
- Update go version to 1.22 (#132)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index ead4a29..fa93a0e 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -11,6 +11,7 @@ import (
"os/signal"
"runtime/debug"
"strconv"
+ "strings"
"sync"
"syscall"
"time"
@@ -87,15 +88,30 @@ type (
appSettings struct {
reconnectInterval time.Duration
- mu sync.RWMutex
- defaultTimestamp bool
- zipCompression bool
- clientCut bool
- returnIndexPage bool
- indexPageTemplate string
- bufferMaxSizeForPut uint64
- namespaceHeader string
- defaultNamespaces []string
+ mu sync.RWMutex
+ defaultTimestamp bool
+ zipCompression bool
+ clientCut bool
+ returnIndexPage bool
+ indexPageTemplate string
+ bufferMaxSizeForPut uint64
+ namespaceHeader string
+ defaultNamespaces []string
+ corsAllowOrigin string
+ corsAllowMethods []string
+ corsAllowHeaders []string
+ corsExposeHeaders []string
+ corsAllowCredentials bool
+ corsMaxAge int
+ }
+
+ CORS struct {
+ AllowOrigin string
+ AllowMethods []string
+ AllowHeaders []string
+ ExposeHeaders []string
+ AllowCredentials bool
+ MaxAge int
}
)
@@ -177,6 +193,12 @@ func (s *appSettings) update(v *viper.Viper, l *zap.Logger) {
namespaceHeader := v.GetString(cfgResolveNamespaceHeader)
defaultNamespaces := fetchDefaultNamespaces(v)
indexPage, indexEnabled := fetchIndexPageTemplate(v, l)
+ corsAllowOrigin := v.GetString(cfgCORSAllowOrigin)
+ corsAllowMethods := v.GetStringSlice(cfgCORSAllowMethods)
+ corsAllowHeaders := v.GetStringSlice(cfgCORSAllowHeaders)
+ corsExposeHeaders := v.GetStringSlice(cfgCORSExposeHeaders)
+ corsAllowCredentials := v.GetBool(cfgCORSAllowCredentials)
+ corsMaxAge := fetchCORSMaxAge(v)
s.mu.Lock()
defer s.mu.Unlock()
@@ -190,6 +212,12 @@ func (s *appSettings) update(v *viper.Viper, l *zap.Logger) {
s.defaultNamespaces = defaultNamespaces
s.returnIndexPage = indexEnabled
s.indexPageTemplate = indexPage
+ s.corsAllowOrigin = corsAllowOrigin
+ s.corsAllowMethods = corsAllowMethods
+ s.corsAllowHeaders = corsAllowHeaders
+ s.corsExposeHeaders = corsExposeHeaders
+ s.corsAllowCredentials = corsAllowCredentials
+ s.corsMaxAge = corsMaxAge
}
func (s *appSettings) DefaultTimestamp() bool {
@@ -219,6 +247,29 @@ func (s *appSettings) IndexPageTemplate() string {
return s.indexPageTemplate
}
+func (s *appSettings) CORS() CORS {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ allowMethods := make([]string, len(s.corsAllowMethods))
+ copy(allowMethods, s.corsAllowMethods)
+
+ allowHeaders := make([]string, len(s.corsAllowHeaders))
+ copy(allowHeaders, s.corsAllowHeaders)
+
+ exposeHeaders := make([]string, len(s.corsExposeHeaders))
+ copy(exposeHeaders, s.corsExposeHeaders)
+
+ return CORS{
+ AllowOrigin: s.corsAllowOrigin,
+ AllowMethods: allowMethods,
+ AllowHeaders: allowHeaders,
+ ExposeHeaders: exposeHeaders,
+ AllowCredentials: s.corsAllowCredentials,
+ MaxAge: s.corsMaxAge,
+ }
+}
+
func (s *appSettings) ClientCut() bool {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -550,7 +601,6 @@ func (a *app) stopServices() {
svc.ShutDown(ctx)
}
}
-
func (a *app) configureRouter(handler *handler.Handler) {
r := router.New()
r.RedirectTrailingSlash = true
@@ -561,20 +611,96 @@ func (a *app) configureRouter(handler *handler.Handler) {
response.Error(r, "Method Not Allowed", fasthttp.StatusMethodNotAllowed)
}
- r.POST("/upload/{cid}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.Upload))))))
+ r.POST("/upload/{cid}", a.addMiddlewares(handler.Upload))
+ r.OPTIONS("/upload/{cid}", a.addPreflight())
a.log.Info(logs.AddedPathUploadCid)
- r.GET("/get/{cid}/{oid:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.DownloadByAddressOrBucketName))))))
- r.HEAD("/get/{cid}/{oid:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.HeadByAddressOrBucketName))))))
+ r.GET("/get/{cid}/{oid:*}", a.addMiddlewares(handler.DownloadByAddressOrBucketName))
+ r.HEAD("/get/{cid}/{oid:*}", a.addMiddlewares(handler.HeadByAddressOrBucketName))
+ r.OPTIONS("/get/{cid}/{oid:*}", a.addPreflight())
a.log.Info(logs.AddedPathGetCidOid)
- r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.DownloadByAttribute))))))
- r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.HeadByAttribute))))))
+ r.GET("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.addMiddlewares(handler.DownloadByAttribute))
+ r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.addMiddlewares(handler.HeadByAttribute))
+ r.OPTIONS("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.addPreflight())
a.log.Info(logs.AddedPathGetByAttributeCidAttrKeyAttrVal)
- r.GET("/zip/{cid}/{prefix:*}", a.tracer(a.logger(a.canonicalizer(a.tokenizer(a.reqNamespace(handler.DownloadZipped))))))
+ r.GET("/zip/{cid}/{prefix:*}", a.addMiddlewares(handler.DownloadZipped))
+ r.OPTIONS("/zip/{cid}/{prefix:*}", a.addPreflight())
a.log.Info(logs.AddedPathZipCidPrefix)
a.webServer.Handler = r.Handler
}
+func (a *app) addMiddlewares(h fasthttp.RequestHandler) fasthttp.RequestHandler {
+ list := []func(fasthttp.RequestHandler) fasthttp.RequestHandler{
+ a.tracer,
+ a.logger,
+ a.canonicalizer,
+ a.tokenizer,
+ a.reqNamespace,
+ a.cors,
+ }
+
+ for i := len(list) - 1; i >= 0; i-- {
+ h = list[i](h)
+ }
+
+ return h
+}
+
+func (a *app) addPreflight() fasthttp.RequestHandler {
+ list := []func(fasthttp.RequestHandler) fasthttp.RequestHandler{
+ a.tracer,
+ a.logger,
+ a.reqNamespace,
+ }
+
+ h := a.preflightHandler
+ for i := len(list) - 1; i >= 0; i-- {
+ h = list[i](h)
+ }
+
+ return h
+}
+
+func (a *app) preflightHandler(c *fasthttp.RequestCtx) {
+ cors := a.settings.CORS()
+ setCORSHeaders(c, cors)
+}
+
+func (a *app) cors(h fasthttp.RequestHandler) fasthttp.RequestHandler {
+ return func(c *fasthttp.RequestCtx) {
+ h(c)
+ code := c.Response.StatusCode()
+ if code >= fasthttp.StatusOK && code < fasthttp.StatusMultipleChoices {
+ cors := a.settings.CORS()
+ setCORSHeaders(c, cors)
+ }
+ }
+}
+
+func setCORSHeaders(c *fasthttp.RequestCtx, cors CORS) {
+ c.Response.Header.Set(fasthttp.HeaderAccessControlMaxAge, strconv.Itoa(cors.MaxAge))
+
+ if len(cors.AllowOrigin) != 0 {
+ c.Response.Header.Set(fasthttp.HeaderAccessControlAllowOrigin, cors.AllowOrigin)
+ }
+
+ if len(cors.AllowMethods) != 0 {
+ c.Response.Header.Set(fasthttp.HeaderAccessControlAllowMethods, strings.Join(cors.AllowMethods, ","))
+ }
+
+ if len(cors.AllowHeaders) != 0 {
+ c.Response.Header.Set(fasthttp.HeaderAccessControlAllowHeaders, strings.Join(cors.AllowHeaders, ","))
+ }
+
+ if len(cors.ExposeHeaders) != 0 {
+ c.Response.Header.Set(fasthttp.HeaderAccessControlExposeHeaders, strings.Join(cors.ExposeHeaders, ","))
+ }
+
+ if cors.AllowCredentials {
+ c.Response.Header.Set(fasthttp.HeaderAccessControlAllowCredentials, "true")
+ }
+}
+
func (a *app) logger(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(req *fasthttp.RequestCtx) {
requiredFields := []zap.Field{zap.Uint64("id", req.ID())}
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index e777f67..5fb628d 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -56,6 +56,8 @@ const (
defaultReconnectInterval = time.Minute
+ defaultCORSMaxAge = 600 // seconds
+
cfgServer = "server"
cfgTLSEnabled = "tls.enabled"
cfgTLSCertFile = "tls.cert_file"
@@ -141,6 +143,14 @@ const (
cfgResolveNamespaceHeader = "resolve_bucket.namespace_header"
cfgResolveDefaultNamespaces = "resolve_bucket.default_namespaces"
+ // CORS.
+ cfgCORSAllowOrigin = "cors.allow_origin"
+ cfgCORSAllowMethods = "cors.allow_methods"
+ cfgCORSAllowHeaders = "cors.allow_headers"
+ cfgCORSExposeHeaders = "cors.expose_headers"
+ cfgCORSAllowCredentials = "cors.allow_credentials"
+ cfgCORSMaxAge = "cors.max_age"
+
// Command line args.
cmdHelp = "help"
cmdVersion = "version"
@@ -537,6 +547,15 @@ func fetchDefaultNamespaces(v *viper.Viper) []string {
return namespaces
}
+func fetchCORSMaxAge(v *viper.Viper) int {
+ maxAge := v.GetInt(cfgCORSMaxAge)
+ if maxAge <= 0 {
+ maxAge = defaultCORSMaxAge
+ }
+
+ return maxAge
+}
+
func fetchServers(v *viper.Viper, log *zap.Logger) []ServerInfo {
var servers []ServerInfo
seen := make(map[string]struct{})
diff --git a/config/config.env b/config/config.env
index e1d5e7d..d2f4a56 100644
--- a/config/config.env
+++ b/config/config.env
@@ -126,3 +126,10 @@ HTTP_GW_RESOLVE_BUCKET_DEFAULT_NAMESPACES="" "root"
# Max attempt to make successful tree request.
# default value is 0 that means the number of attempts equals to number of nodes in pool.
HTTP_GW_FROSTFS_TREE_POOL_MAX_ATTEMPTS=0
+
+HTTP_GW_CORS_ALLOW_ORIGIN="*"
+HTTP_GW_CORS_ALLOW_METHODS="GET" "POST"
+HTTP_GW_CORS_ALLOW_HEADERS="*"
+HTTP_GW_CORS_EXPOSE_HEADERS="*"
+HTTP_GW_CORS_ALLOW_CREDENTIALS=false
+HTTP_GW_CORS_MAX_AGE=600
diff --git a/config/config.yaml b/config/config.yaml
index 61aa70b..dd985ad 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -138,3 +138,11 @@ cache:
resolve_bucket:
namespace_header: X-Frostfs-Namespace
default_namespaces: [ "", "root" ]
+
+cors:
+ allow_origin: ""
+ allow_methods: []
+ allow_headers: []
+ expose_headers: []
+ allow_credentials: false
+ max_age: 600
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index b484f9d..7a3eba7 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -363,3 +363,27 @@ index_page:
|-----------------|----------|---------------|---------------|---------------------------------------------------------------------------------|
| `enabled` | `bool` | yes | `false` | Flag to enable index_page return if no object with specified S3-name was found. |
| `template_path` | `string` | yes | `""` | Path to .gotmpl file with html template for index_page. |
+
+# `cors` section
+
+Parameters for CORS (used in OPTIONS requests and responses in all handlers).
+If values are not set, headers will not be included to response.
+
+```yaml
+cors:
+ allow_origin: "*"
+ allow_methods: ["GET", "HEAD"]
+ allow_headers: ["Authorization"]
+ expose_headers: ["*"]
+ allow_credentials: false
+ max_age: 600
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|---------------------|------------|---------------|---------------|--------------------------------------------------------|
+| `allow_origin` | `string` | yes | | Values for `Access-Control-Allow-Origin` headers. |
+| `allow_methods` | `[]string` | yes | | Values for `Access-Control-Allow-Methods` headers. |
+| `allow_headers` | `[]string` | yes | | Values for `Access-Control-Allow-Headers` headers. |
+| `expose_headers` | `[]string` | yes | | Values for `Access-Control-Expose-Headers` headers. |
+| `allow_credentials` | `bool` | yes | `false` | Values for `Access-Control-Allow-Credentials` headers. |
+| `max_age` | `int` | yes | `600` | Values for `Access-Control-Max-Age ` headers. |
From 69b7761bd6e03cad3f70bffe592716ac8bb52489 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 29 Oct 2024 17:45:42 +0300
Subject: [PATCH 092/161] [#160] Add internal/net package with multinet dialer
source
Signed-off-by: Alex Vanin
---
go.mod | 3 +-
go.sum | 2 +
internal/logs/logs.go | 2 +
internal/net/config.go | 68 ++++++++++++++++++++++++++++++++++
internal/net/dial_target.go | 54 +++++++++++++++++++++++++++
internal/net/dialer.go | 36 ++++++++++++++++++
internal/net/dialer_source.go | 69 +++++++++++++++++++++++++++++++++++
internal/net/event_handler.go | 28 ++++++++++++++
8 files changed, 261 insertions(+), 1 deletion(-)
create mode 100644 internal/net/config.go
create mode 100644 internal/net/dial_target.go
create mode 100644 internal/net/dialer.go
create mode 100644 internal/net/dialer_source.go
create mode 100644 internal/net/event_handler.go
diff --git a/go.mod b/go.mod
index d1a3788..efb79eb 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98
+ git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
github.com/docker/go-units v0.4.0
@@ -25,6 +26,7 @@ require (
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/net v0.26.0
+ golang.org/x/sys v0.22.0
google.golang.org/grpc v1.66.2
)
@@ -106,7 +108,6 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
- golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.3.0 // indirect
diff --git a/go.sum b/go.sum
index bc433eb..ce02ee6 100644
--- a/go.sum
+++ b/go.sum
@@ -49,6 +49,8 @@ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98/go.mod h1:GeNpo12HcEW4J412sH5yf8xFYapxlrt5fcYzRwg0Ino=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
+git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/960fWeyn2AFHwQUwDsWB3sbP6lTEnFnMzLMM6tx6N8=
+git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index 96bdaa5..7b7ddc1 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -79,4 +79,6 @@ const (
ServerReconnectedSuccessfully = "server reconnected successfully"
ServerReconnectFailed = "failed to reconnect server"
WarnDuplicateAddress = "duplicate address"
+ MultinetDialSuccess = "multinet dial successful"
+ MultinetDialFail = "multinet dial failed"
)
diff --git a/internal/net/config.go b/internal/net/config.go
new file mode 100644
index 0000000..b40e003
--- /dev/null
+++ b/internal/net/config.go
@@ -0,0 +1,68 @@
+package net
+
+import (
+ "errors"
+ "fmt"
+ "net/netip"
+ "slices"
+ "time"
+
+ "git.frostfs.info/TrueCloudLab/multinet"
+)
+
+var errEmptySourceIPList = errors.New("empty source IP list")
+
+type Subnet struct {
+ Prefix string
+ SourceIPs []string
+}
+
+type Config struct {
+ Enabled bool
+ Subnets []Subnet
+ Balancer string
+ Restrict bool
+ FallbackDelay time.Duration
+ EventHandler multinet.EventHandler
+}
+
+func (c Config) toMultinetConfig() (multinet.Config, error) {
+ var subnets []multinet.Subnet
+ for _, s := range c.Subnets {
+ var ms multinet.Subnet
+ p, err := netip.ParsePrefix(s.Prefix)
+ if err != nil {
+ return multinet.Config{}, fmt.Errorf("parse IP prefix '%s': %w", s.Prefix, err)
+ }
+ ms.Prefix = p
+ for _, ip := range s.SourceIPs {
+ addr, err := netip.ParseAddr(ip)
+ if err != nil {
+ return multinet.Config{}, fmt.Errorf("parse IP address '%s': %w", ip, err)
+ }
+ ms.SourceIPs = append(ms.SourceIPs, addr)
+ }
+ if len(ms.SourceIPs) == 0 {
+ return multinet.Config{}, errEmptySourceIPList
+ }
+ subnets = append(subnets, ms)
+ }
+ return multinet.Config{
+ Subnets: subnets,
+ Balancer: multinet.BalancerType(c.Balancer),
+ Restrict: c.Restrict,
+ FallbackDelay: c.FallbackDelay,
+ Dialer: newDefaultDialer(),
+ EventHandler: c.EventHandler,
+ }, nil
+}
+
+func (c Config) equals(other Config) bool {
+ return c.Enabled == other.Enabled &&
+ slices.EqualFunc(c.Subnets, other.Subnets, func(lhs, rhs Subnet) bool {
+ return lhs.Prefix == rhs.Prefix && slices.Equal(lhs.SourceIPs, rhs.SourceIPs)
+ }) &&
+ c.Balancer == other.Balancer &&
+ c.Restrict == other.Restrict &&
+ c.FallbackDelay == other.FallbackDelay
+}
diff --git a/internal/net/dial_target.go b/internal/net/dial_target.go
new file mode 100644
index 0000000..6265f18
--- /dev/null
+++ b/internal/net/dial_target.go
@@ -0,0 +1,54 @@
+// NOTE: code is taken from https://github.com/grpc/grpc-go/blob/v1.68.x/internal/transport/http_util.go
+
+/*
+ *
+ * Copyright 2014 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package net
+
+import (
+ "net/url"
+ "strings"
+)
+
+// parseDialTarget returns the network and address to pass to dialer.
+func parseDialTarget(target string) (string, string) {
+ net := "tcp"
+ m1 := strings.Index(target, ":")
+ m2 := strings.Index(target, ":/")
+ // handle unix:addr which will fail with url.Parse
+ if m1 >= 0 && m2 < 0 {
+ if n := target[0:m1]; n == "unix" {
+ return n, target[m1+1:]
+ }
+ }
+ if m2 >= 0 {
+ t, err := url.Parse(target)
+ if err != nil {
+ return net, target
+ }
+ scheme := t.Scheme
+ addr := t.Path
+ if scheme == "unix" {
+ if addr == "" {
+ addr = t.Host
+ }
+ return scheme, addr
+ }
+ }
+ return net, target
+}
diff --git a/internal/net/dialer.go b/internal/net/dialer.go
new file mode 100644
index 0000000..8441dd5
--- /dev/null
+++ b/internal/net/dialer.go
@@ -0,0 +1,36 @@
+package net
+
+import (
+ "net"
+ "syscall"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+func newDefaultDialer() net.Dialer {
+ // From `grpc.WithContextDialer` comment:
+ //
+ // Note: All supported releases of Go (as of December 2023) override the OS
+ // defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive
+ // with OS defaults for keepalive time and interval, use a net.Dialer that sets
+ // the KeepAlive field to a negative value, and sets the SO_KEEPALIVE socket
+ // option to true from the Control field. For a concrete example of how to do
+ // this, see internal.NetDialerWithTCPKeepalive().
+ //
+ // https://github.com/grpc/grpc-go/blob/830135e6c5a351abf75f0c9cfdf978e5df8daeba/dialoptions.go#L432
+ //
+ // From `internal.NetDialerWithTCPKeepalive` comment:
+ //
+ // TODO: Once https://github.com/golang/go/issues/62254 lands, and the
+ // appropriate Go version becomes less than our least supported Go version, we
+ // should look into using the new API to make things more straightforward.
+ return net.Dialer{
+ KeepAlive: time.Duration(-1),
+ Control: func(_, _ string, c syscall.RawConn) error {
+ return c.Control(func(fd uintptr) {
+ _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1)
+ })
+ },
+ }
+}
diff --git a/internal/net/dialer_source.go b/internal/net/dialer_source.go
new file mode 100644
index 0000000..e6a142a
--- /dev/null
+++ b/internal/net/dialer_source.go
@@ -0,0 +1,69 @@
+package net
+
+import (
+ "context"
+ "net"
+ "sync"
+
+ "git.frostfs.info/TrueCloudLab/multinet"
+)
+
+type DialerSource struct {
+ guard sync.RWMutex
+
+ c Config
+
+ md multinet.Dialer
+}
+
+func NewDialerSource(c Config) (*DialerSource, error) {
+ result := &DialerSource{}
+ if err := result.build(c); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func (s *DialerSource) build(c Config) error {
+ if c.Enabled {
+ mc, err := c.toMultinetConfig()
+ if err != nil {
+ return err
+ }
+ md, err := multinet.NewDialer(mc)
+ if err != nil {
+ return err
+ }
+ s.md = md
+ s.c = c
+ return nil
+ }
+ s.md = nil
+ s.c = c
+ return nil
+}
+
+// GrpcContextDialer returns grpc.WithContextDialer func.
+// Returns nil if multinet disabled.
+func (s *DialerSource) GrpcContextDialer() func(context.Context, string) (net.Conn, error) {
+ s.guard.RLock()
+ defer s.guard.RUnlock()
+
+ if s.c.Enabled {
+ return func(ctx context.Context, address string) (net.Conn, error) {
+ network, address := parseDialTarget(address)
+ return s.md.DialContext(ctx, network, address)
+ }
+ }
+ return nil
+}
+
+func (s *DialerSource) Update(c Config) error {
+ s.guard.Lock()
+ defer s.guard.Unlock()
+
+ if s.c.equals(c) {
+ return nil
+ }
+ return s.build(c)
+}
diff --git a/internal/net/event_handler.go b/internal/net/event_handler.go
new file mode 100644
index 0000000..9520c01
--- /dev/null
+++ b/internal/net/event_handler.go
@@ -0,0 +1,28 @@
+package net
+
+import (
+ "net"
+
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ "go.uber.org/zap"
+)
+
+type LogEventHandler struct {
+ logger *zap.Logger
+}
+
+func (l LogEventHandler) DialPerformed(sourceIP net.Addr, _, address string, err error) {
+ sourceIPString := "undefined"
+ if sourceIP != nil {
+ sourceIPString = sourceIP.Network() + "://" + sourceIP.String()
+ }
+ if err == nil {
+ l.logger.Debug(logs.MultinetDialSuccess, zap.String("source", sourceIPString), zap.String("destination", address))
+ } else {
+ l.logger.Debug(logs.MultinetDialFail, zap.String("source", sourceIPString), zap.String("destination", address), zap.Error(err))
+ }
+}
+
+func NewLogEventHandler(logger *zap.Logger) LogEventHandler {
+ return LogEventHandler{logger: logger}
+}
From 8bc64ce5e997b169b9c146b3ee291b86c3fdab62 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 29 Oct 2024 14:41:49 +0300
Subject: [PATCH 093/161] [#160] Use source dialer for gRPC connection to
storage
Signed-off-by: Alex Vanin
---
cmd/http-gw/app.go | 12 +++++++++--
cmd/http-gw/settings.go | 47 ++++++++++++++++++++++++++++++++++++++++-
internal/logs/logs.go | 2 ++
3 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index fa93a0e..84379d4 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -21,6 +21,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ internalnet "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/net"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/service/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/templates"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
@@ -87,6 +88,7 @@ type (
// appSettings stores reloading parameters, so it has to provide getters and setters which use RWMutex.
appSettings struct {
reconnectInterval time.Duration
+ dialerSource *internalnet.DialerSource
mu sync.RWMutex
defaultTimestamp bool
@@ -148,6 +150,8 @@ func newApp(ctx context.Context, opt ...Option) App {
opt[i](a)
}
+ a.initAppSettings()
+
// -- setup FastHTTP server --
a.webServer.Name = "frost-http-gw"
a.webServer.ReadBufferSize = a.cfg.GetInt(cfgWebReadBufferSize)
@@ -161,7 +165,7 @@ func newApp(ctx context.Context, opt ...Option) App {
a.webServer.DisablePreParseMultipartForm = true
a.webServer.StreamRequestBody = a.cfg.GetBool(cfgWebStreamRequestBody)
// -- -- -- -- -- -- -- -- -- -- -- -- -- --
- a.pool, a.treePool, a.key = getPools(ctx, a.log, a.cfg)
+ a.pool, a.treePool, a.key = getPools(ctx, a.log, a.cfg, a.settings.dialerSource)
var owner user.ID
user.IDFromKey(&owner, a.key.PrivateKey.PublicKey)
@@ -169,7 +173,6 @@ func newApp(ctx context.Context, opt ...Option) App {
a.setRuntimeParameters()
- a.initAppSettings()
a.initResolver()
a.initMetrics()
a.initTracing(ctx)
@@ -180,6 +183,7 @@ func newApp(ctx context.Context, opt ...Option) App {
func (a *app) initAppSettings() {
a.settings = &appSettings{
reconnectInterval: fetchReconnectInterval(a.cfg),
+ dialerSource: getDialerSource(a.log, a.cfg),
}
a.settings.update(a.cfg, a.log)
}
@@ -559,6 +563,10 @@ func (a *app) configReload(ctx context.Context) {
a.logLevel.SetLevel(lvl)
}
+ if err := a.settings.dialerSource.Update(fetchMultinetConfig(a.cfg, a.log)); err != nil {
+ a.log.Warn(logs.MultinetConfigWontBeUpdated, zap.Error(err))
+ }
+
if err := a.resolver.UpdateResolvers(a.getResolverConfig()); err != nil {
a.log.Warn(logs.FailedToUpdateResolvers, zap.Error(err))
}
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 5fb628d..f464fbc 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -16,6 +16,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
+ internalnet "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/net"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
@@ -58,6 +59,8 @@ const (
defaultCORSMaxAge = 600 // seconds
+ defaultMultinetFallbackDelay = 300 * time.Millisecond
+
cfgServer = "server"
cfgTLSEnabled = "tls.enabled"
cfgTLSCertFile = "tls.cert_file"
@@ -151,6 +154,13 @@ const (
cfgCORSAllowCredentials = "cors.allow_credentials"
cfgCORSMaxAge = "cors.max_age"
+ // Multinet.
+ cfgMultinetEnabled = "multinet.enabled"
+ cfgMultinetBalancer = "multinet.balancer"
+ cfgMultinetRestrict = "multinet.restrict"
+ cfgMultinetFallbackDelay = "multinet.fallback_delay"
+ cfgMultinetSubnets = "multinet.subnets"
+
// Command line args.
cmdHelp = "help"
cmdVersion = "version"
@@ -245,6 +255,9 @@ func settings() *viper.Viper {
v.SetDefault(cfgResolveNamespaceHeader, defaultNamespaceHeader)
v.SetDefault(cfgResolveDefaultNamespaces, []string{"", "root"})
+ // multinet
+ v.SetDefault(cfgMultinetFallbackDelay, defaultMultinetFallbackDelay)
+
// Binding flags
if err := v.BindPFlag(cfgPprofEnabled, flags.Lookup(cmdPprof)); err != nil {
panic(err)
@@ -584,7 +597,7 @@ func fetchServers(v *viper.Viper, log *zap.Logger) []ServerInfo {
return servers
}
-func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.Pool, *treepool.Pool, *keys.PrivateKey) {
+func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper, dialSource *internalnet.DialerSource) (*pool.Pool, *treepool.Pool, *keys.PrivateKey) {
key, err := getFrostFSKey(cfg, logger)
if err != nil {
logger.Fatal(logs.CouldNotLoadFrostFSPrivateKey, zap.Error(err))
@@ -643,6 +656,7 @@ func getPools(ctx context.Context, logger *zap.Logger, cfg *viper.Viper) (*pool.
interceptors := []grpc.DialOption{
grpc.WithUnaryInterceptor(grpctracing.NewUnaryClientInteceptor()),
grpc.WithStreamInterceptor(grpctracing.NewStreamClientInterceptor()),
+ grpc.WithContextDialer(dialSource.GrpcContextDialer()),
}
prm.SetGRPCDialOptions(interceptors...)
prmTree.SetGRPCDialOptions(interceptors...)
@@ -745,3 +759,34 @@ func fetchCacheSize(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue
return defaultValue
}
+
+func getDialerSource(logger *zap.Logger, cfg *viper.Viper) *internalnet.DialerSource {
+ source, err := internalnet.NewDialerSource(fetchMultinetConfig(cfg, logger))
+ if err != nil {
+ logger.Fatal(logs.FailedToLoadMultinetConfig, zap.Error(err))
+ }
+ return source
+}
+
+func fetchMultinetConfig(v *viper.Viper, l *zap.Logger) (cfg internalnet.Config) {
+ cfg.Enabled = v.GetBool(cfgMultinetEnabled)
+ cfg.Balancer = v.GetString(cfgMultinetBalancer)
+ cfg.Restrict = v.GetBool(cfgMultinetRestrict)
+ cfg.FallbackDelay = v.GetDuration(cfgMultinetFallbackDelay)
+ cfg.Subnets = make([]internalnet.Subnet, 0, 5)
+ cfg.EventHandler = internalnet.NewLogEventHandler(l)
+
+ for i := 0; ; i++ {
+ key := cfgMultinetSubnets + "." + strconv.Itoa(i) + "."
+ subnet := internalnet.Subnet{}
+
+ subnet.Prefix = v.GetString(key + "mask")
+ if subnet.Prefix == "" {
+ break
+ }
+ subnet.SourceIPs = v.GetStringSlice(key + "source_ips")
+ cfg.Subnets = append(cfg.Subnets, subnet)
+ }
+
+ return
+}
diff --git a/internal/logs/logs.go b/internal/logs/logs.go
index 7b7ddc1..409f87d 100644
--- a/internal/logs/logs.go
+++ b/internal/logs/logs.go
@@ -81,4 +81,6 @@ const (
WarnDuplicateAddress = "duplicate address"
MultinetDialSuccess = "multinet dial successful"
MultinetDialFail = "multinet dial failed"
+ FailedToLoadMultinetConfig = "failed to load multinet config"
+ MultinetConfigWontBeUpdated = "multinet config won't be updated"
)
From 821f8c2248040415cb3984e39afd38ff9acebdd5 Mon Sep 17 00:00:00 2001
From: Alex Vanin
Date: Tue, 29 Oct 2024 14:44:09 +0300
Subject: [PATCH 094/161] [#160] Add documentation for multinet settings
Signed-off-by: Alex Vanin
---
config/config.env | 13 +++++++++++++
config/config.yaml | 17 ++++++++++++++++
docs/gate-configuration.md | 40 ++++++++++++++++++++++++++++++++++++++
3 files changed, 70 insertions(+)
diff --git a/config/config.env b/config/config.env
index d2f4a56..4fd8132 100644
--- a/config/config.env
+++ b/config/config.env
@@ -133,3 +133,16 @@ HTTP_GW_CORS_ALLOW_HEADERS="*"
HTTP_GW_CORS_EXPOSE_HEADERS="*"
HTTP_GW_CORS_ALLOW_CREDENTIALS=false
HTTP_GW_CORS_MAX_AGE=600
+
+# Multinet properties
+# Enable multinet support
+HTTP_GW_MULTINET_ENABLED=false
+# Strategy to pick source IP address
+HTTP_GW_MULTINET_BALANCER=roundrobin
+# Restrict requests with unknown destination subnet
+HTTP_GW_MULTINET_RESTRICT=false
+# Delay between ipv6 to ipv4 fallback switch
+HTTP_GW_MULTINET_FALLBACK_DELAY=300ms
+# List of subnets and IP addresses to use as source for those subnets
+HTTP_GW_MULTINET_SUBNETS_1_MASK=1.2.3.4/24
+HTTP_GW_MULTINET_SUBNETS_1_SOURCE_IPS=1.2.3.4 1.2.3.5
diff --git a/config/config.yaml b/config/config.yaml
index dd985ad..9169acc 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -146,3 +146,20 @@ cors:
expose_headers: []
allow_credentials: false
max_age: 600
+
+# Multinet properties
+multinet:
+ # Enable multinet support
+ enabled: false
+ # Strategy to pick source IP address
+ balancer: roundrobin
+ # Restrict requests with unknown destination subnet
+ restrict: false
+ # Delay between ipv6 to ipv4 fallback switch
+ fallback_delay: 300ms
+ # List of subnets and IP addresses to use as source for those subnets
+ subnets:
+ - mask: 1.2.3.4/24
+ source_ips:
+ - 1.2.3.4
+ - 1.2.3.5
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 7a3eba7..be4b30b 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -58,6 +58,7 @@ $ cat http.log
| `cache` | [Cache configuration](#cache-section) |
| `resolve_bucket` | [Bucket name resolving configuration](#resolve_bucket-section) |
| `index_page` | [Index page configuration](#index_page-section) |
+| `multinet` | [Multinet configuration](#multinet-section) |
# General section
@@ -387,3 +388,42 @@ cors:
| `expose_headers` | `[]string` | yes | | Values for `Access-Control-Expose-Headers` headers. |
| `allow_credentials` | `bool` | yes | `false` | Values for `Access-Control-Allow-Credentials` headers. |
| `max_age` | `int` | yes | `600` | Values for `Access-Control-Max-Age ` headers. |
+
+# `multinet` section
+
+Configuration of multinet support.
+
+```yaml
+multinet:
+ enabled: false
+ balancer: roundrobin
+ restrict: false
+ fallback_delay: 300ms
+ subnets:
+ - mask: 1.2.3.4/24
+ source_ips:
+ - 1.2.3.4
+ - 1.2.3.5
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|------------------|--------------------------------|---------------|---------------|--------------------------------------------------------------------------------------------|
+| `enabled` | `bool` | yes | `false` | Enables multinet setting to manage source ip of outcoming requests. |
+| `balancer` | `string` | yes | `""` | Strategy to pick source IP. By default picks first address. Supports `roundrobin` setting. |
+| `restrict` | `bool` | yes | `false` | Restricts requests to an undefined subnets. |
+| `fallback_delay` | `duration` | yes | `300ms` | Delay between IPv6 and IPv4 fallback stack switch. |
+| `subnets` | [[]Subnet](#subnet-subsection) | yes | | Set of subnets to apply multinet dial settings. |
+
+#### `subnet` subsection
+
+```yaml
+- mask: 1.2.3.4/24
+ source_ips:
+ - 1.2.3.4
+ - 1.2.3.5
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|--------------|------------|---------------|---------------|----------------------------------------------------------------------|
+| `mask` | `string` | yes | | Destination subnet. |
+| `source_ips` | `[]string` | yes | | Array of source IP addresses to use when dialing destination subnet. |
From 679731ee52f72cf060b731d1ccd24e357b121f2c Mon Sep 17 00:00:00 2001
From: Denis Kirillov
Date: Tue, 5 Nov 2024 17:50:50 +0300
Subject: [PATCH 095/161] [#161] Update SDK
Need fix https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pulls/282
Signed-off-by: Denis Kirillov
---
go.mod | 8 ++++----
go.sum | 16 ++++++++--------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/go.mod b/go.mod
index efb79eb..9048009 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,9 @@ module git.frostfs.info/TrueCloudLab/frostfs-http-gw
go 1.22
require (
- git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e
+ git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573
- git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241022124111-5361f0ecebd3
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
github.com/bluele/gcache v0.0.2
@@ -24,7 +24,7 @@ require (
go.opentelemetry.io/otel v1.28.0
go.opentelemetry.io/otel/trace v1.28.0
go.uber.org/zap v1.27.0
- golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
+ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
golang.org/x/net v0.26.0
golang.org/x/sys v0.22.0
google.golang.org/grpc v1.66.2
@@ -41,7 +41,7 @@ require (
github.com/Microsoft/hcsshim v0.9.2 // indirect
github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
- github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
+ github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
diff --git a/go.sum b/go.sum
index ce02ee6..b6069d2 100644
--- a/go.sum
+++ b/go.sum
@@ -37,16 +37,16 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e h1:740ABnOBYx4o6jxULHdSSnVW2fYIO35ohg+Uz59sxd0=
-git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e/go.mod h1:F5GS7hRb62PUy5sTYDC4ajVdeffoAfjHSSHTKUJEaYU=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1 h1:ivcdxQeQDnx4srF2ezoaeVlF0FAycSAztwfIUJnUI4s=
+git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1/go.mod h1:F5GS7hRb62PUy5sTYDC4ajVdeffoAfjHSSHTKUJEaYU=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e h1:kcBqZBiFIUBATUqEuvVigtkJJWQ2Gug/eYXn967o3M4=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
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-observability v0.0.0-20240909114314-666d326cc573 h1:6qCcm1oqFbmf9C5AauXzrL5OPGnTbI9HoB/jAtD9274=
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573/go.mod h1:kbwB4v2o6RyOfCo9kEFeUDZIX3LKhmS0yXPrtvzkQ1g=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98 h1:ijUci3thz0EwWkuRJDocW5D1RkVAJlt9xNG4CYepC90=
-git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240918095938-e580ee991d98/go.mod h1:GeNpo12HcEW4J412sH5yf8xFYapxlrt5fcYzRwg0Ino=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241022124111-5361f0ecebd3 h1:f7jan6eBDN88DKnKj8GKyWpfjBbSzjDALcDejYKRgCs=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241022124111-5361f0ecebd3/go.mod h1:3txOjFJ8M/JFs01h7xOrnQHVn6hZgDNA16ivyUlu1iU=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/960fWeyn2AFHwQUwDsWB3sbP6lTEnFnMzLMM6tx6N8=
@@ -114,8 +114,8 @@ github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
-github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
+github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
+github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
@@ -928,8 +928,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
-golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
From d5b92446bd34ef518f5687b2c607efadbf1ff8d6 Mon Sep 17 00:00:00 2001
From: Vitaliy Potyarkin
Date: Wed, 6 Nov 2024 11:38:03 +0300
Subject: [PATCH 096/161] [#162] Stop using obsolete .github directory
This commit is a part of multi-repo cleanup effort:
https://git.frostfs.info/TrueCloudLab/frostfs-infra/issues/136
Signed-off-by: Vitaliy Potyarkin
---
{.github => .forgejo}/ISSUE_TEMPLATE/bug_report.md | 0
{.github => .forgejo}/ISSUE_TEMPLATE/config.yml | 0
{.github => .forgejo}/ISSUE_TEMPLATE/feature_request.md | 0
{.github => .forgejo}/logo.svg | 0
.github/CODEOWNERS | 1 -
CODEOWNERS | 1 +
README.md | 2 +-
7 files changed, 2 insertions(+), 2 deletions(-)
rename {.github => .forgejo}/ISSUE_TEMPLATE/bug_report.md (100%)
rename {.github => .forgejo}/ISSUE_TEMPLATE/config.yml (100%)
rename {.github => .forgejo}/ISSUE_TEMPLATE/feature_request.md (100%)
rename {.github => .forgejo}/logo.svg (100%)
delete mode 100644 .github/CODEOWNERS
create mode 100644 CODEOWNERS
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.forgejo/ISSUE_TEMPLATE/bug_report.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/bug_report.md
rename to .forgejo/ISSUE_TEMPLATE/bug_report.md
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.forgejo/ISSUE_TEMPLATE/config.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/config.yml
rename to .forgejo/ISSUE_TEMPLATE/config.yml
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.forgejo/ISSUE_TEMPLATE/feature_request.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/feature_request.md
rename to .forgejo/ISSUE_TEMPLATE/feature_request.md
diff --git a/.github/logo.svg b/.forgejo/logo.svg
similarity index 100%
rename from .github/logo.svg
rename to .forgejo/logo.svg
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index c280648..0000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
-* @alexvanin @dkirillov
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000..43df11e
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1 @@
+.* @alexvanin @dkirillov
diff --git a/README.md b/README.md
index 019b8ff..e1af0eb 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
FrostFS is a decentralized distributed object storage integrated with the NEO Blockchain.
From 22d905e51ec963c388aba8930e66d4c4a866aacf Mon Sep 17 00:00:00 2001
From: Vitaliy Potyarkin
Date: Fri, 15 Nov 2024 14:30:23 +0300
Subject: [PATCH 097/161] [#165] Execute CI on push to master
Discussion:
https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/issues/550
Signed-off-by: Vitaliy Potyarkin
---
.forgejo/workflows/builds.yml | 6 +++++-
.forgejo/workflows/tests.yml | 6 +++++-
.forgejo/workflows/vulncheck.yml | 6 +++++-
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/.forgejo/workflows/builds.yml b/.forgejo/workflows/builds.yml
index 490a97c..7c2bb04 100644
--- a/.forgejo/workflows/builds.yml
+++ b/.forgejo/workflows/builds.yml
@@ -1,4 +1,8 @@
-on: [pull_request]
+on:
+ pull_request:
+ push:
+ branches:
+ - master
jobs:
builds:
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index db7f986..81d93dc 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -1,4 +1,8 @@
-on: [pull_request]
+on:
+ pull_request:
+ push:
+ branches:
+ - master
jobs:
lint:
diff --git a/.forgejo/workflows/vulncheck.yml b/.forgejo/workflows/vulncheck.yml
index 7a82bc3..76e2965 100644
--- a/.forgejo/workflows/vulncheck.yml
+++ b/.forgejo/workflows/vulncheck.yml
@@ -1,4 +1,8 @@
-on: [pull_request]
+on:
+ pull_request:
+ push:
+ branches:
+ - master
jobs:
vulncheck:
From 9c0b499ea6b8ea7ef8bdbadd254415b46b579b9a Mon Sep 17 00:00:00 2001
From: Roman Loginov
Date: Fri, 15 Nov 2024 10:53:46 +0300
Subject: [PATCH 098/161] [#164] Add tracing attributes
Signed-off-by: Roman Loginov
---
cmd/http-gw/app.go | 7 +++++++
cmd/http-gw/settings.go | 33 +++++++++++++++++++++++++++++----
config/config.env | 4 ++++
config/config.yaml | 6 ++++++
docs/gate-configuration.md | 34 ++++++++++++++++++++++++++++------
go.mod | 2 +-
go.sum | 4 ++--
7 files changed, 77 insertions(+), 13 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 84379d4..853977c 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -927,6 +927,13 @@ func (a *app) initTracing(ctx context.Context) {
cfg.ServerCaCertPool = certPool
}
+ attributes, err := fetchTracingAttributes(a.cfg)
+ if err != nil {
+ a.log.Warn(logs.FailedToInitializeTracing, zap.Error(err))
+ return
+ }
+ cfg.Attributes = attributes
+
updated, err := tracing.Setup(ctx, cfg)
if err != nil {
a.log.Warn(logs.FailedToInitializeTracing, zap.Error(err))
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index f464fbc..4f1712b 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -86,10 +86,11 @@ const (
cfgPprofAddress = "pprof.address"
// Tracing ...
- cfgTracingEnabled = "tracing.enabled"
- cfgTracingExporter = "tracing.exporter"
- cfgTracingEndpoint = "tracing.endpoint"
- cfgTracingTrustedCa = "tracing.trusted_ca"
+ cfgTracingEnabled = "tracing.enabled"
+ cfgTracingExporter = "tracing.exporter"
+ cfgTracingEndpoint = "tracing.endpoint"
+ cfgTracingTrustedCa = "tracing.trusted_ca"
+ cfgTracingAttributes = "tracing.attributes"
// Pool config.
cfgConTimeout = "connect_timeout"
@@ -790,3 +791,27 @@ func fetchMultinetConfig(v *viper.Viper, l *zap.Logger) (cfg internalnet.Config)
return
}
+
+func fetchTracingAttributes(v *viper.Viper) (map[string]string, error) {
+ attributes := make(map[string]string)
+ for i := 0; ; i++ {
+ key := cfgTracingAttributes + "." + strconv.Itoa(i) + "."
+ attrKey := v.GetString(key + "key")
+ attrValue := v.GetString(key + "value")
+ if attrKey == "" {
+ break
+ }
+
+ if _, ok := attributes[attrKey]; ok {
+ return nil, fmt.Errorf("tracing attribute key %s defined more than once", attrKey)
+ }
+
+ if attrValue == "" {
+ return nil, fmt.Errorf("empty tracing attribute value for key %s", attrKey)
+ }
+
+ attributes[attrKey] = attrValue
+ }
+
+ return attributes, nil
+}
diff --git a/config/config.env b/config/config.env
index 4fd8132..db54c92 100644
--- a/config/config.env
+++ b/config/config.env
@@ -104,6 +104,10 @@ HTTP_GW_TRACING_ENABLED=true
HTTP_GW_TRACING_ENDPOINT="localhost:4317"
HTTP_GW_TRACING_EXPORTER="otlp_grpc"
HTTP_GW_TRACING_TRUSTED_CA=""
+HTTP_GW_TRACING_ATTRIBUTES_0_KEY=key0
+HTTP_GW_TRACING_ATTRIBUTES_0_VALUE=value
+HTTP_GW_TRACING_ATTRIBUTES_1_KEY=key1
+HTTP_GW_TRACING_ATTRIBUTES_1_VALUE=value
HTTP_GW_RUNTIME_SOFT_MEMORY_LIMIT=1073741824
diff --git a/config/config.yaml b/config/config.yaml
index 9169acc..6c89e78 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -9,11 +9,17 @@ pprof:
prometheus:
enabled: false # Enable metrics.
address: localhost:8084
+
tracing:
enabled: true
exporter: "otlp_grpc"
endpoint: "localhost:4317"
trusted_ca: ""
+ attributes:
+ - key: key0
+ value: value
+ - key: key1
+ value: value
logger:
level: debug # Log level.
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index be4b30b..5b5b018 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -268,14 +268,36 @@ tracing:
exporter: "otlp_grpc"
endpoint: "localhost:4317"
trusted_ca: "/etc/ssl/telemetry-trusted-ca.pem"
+ attributes:
+ - key: key0
+ value: value
+ - key: key1
+ value: value
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|--------------|----------|---------------|---------------|---------------------------------------------------------------------------------------------------------------------------------|
-| `enabled` | `bool` | yes | `false` | Flag to enable the tracing. |
-| `exporter` | `string` | yes | | Trace collector type (`stdout` or `otlp_grpc` are supported). |
-| `endpoint` | `string` | yes | | Address of collector endpoint for OTLP exporters. |
-| `trusted_ca` | `string` | yes | | Path to certificate of a certification authority in pem format, that issued the TLS certificate of the telemetry remote server. |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+| ------------ | -------------------------------------- | ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| `enabled` | `bool` | yes | `false` | Flag to enable the tracing. |
+| `exporter` | `string` | yes | | Trace collector type (`stdout` or `otlp_grpc` are supported). |
+| `endpoint` | `string` | yes | | Address of collector endpoint for OTLP exporters. |
+| `trusted_ca` | `string` | yes | | Path to certificate of a certification authority in pem format, that issued the TLS certificate of the telemetry remote server. |
+| `attributes` | [[]Attributes](#attributes-subsection) | yes | | An array of configurable attributes in key-value format. |
+
+
+#### `attributes` subsection
+
+```yaml
+ attributes:
+ - key: key0
+ value: value
+ - key: key1
+ value: value
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|-----------------------|----------|---------------|---------------|----------------------------------------------------------|
+| `key` | `string` | yes | | Attribute key. |
+| `value` | `string` | yes | | Attribute value. |
# `runtime` section
Contains runtime parameters.
diff --git a/go.mod b/go.mod
index 9048009..ca8f4fe 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.22
require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1
- git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573
+ git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241112082307-f17779933e88
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241022124111-5361f0ecebd3
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
diff --git a/go.sum b/go.sum
index b6069d2..197f21c 100644
--- a/go.sum
+++ b/go.sum
@@ -43,8 +43,8 @@ git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
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-observability v0.0.0-20240909114314-666d326cc573 h1:6qCcm1oqFbmf9C5AauXzrL5OPGnTbI9HoB/jAtD9274=
-git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20240909114314-666d326cc573/go.mod h1:kbwB4v2o6RyOfCo9kEFeUDZIX3LKhmS0yXPrtvzkQ1g=
+git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241112082307-f17779933e88 h1:9bvBDLApbbO5sXBKdODpE9tzy3HV99nXxkDWNn22rdI=
+git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20241112082307-f17779933e88/go.mod h1:kbwB4v2o6RyOfCo9kEFeUDZIX3LKhmS0yXPrtvzkQ1g=
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241022124111-5361f0ecebd3 h1:f7jan6eBDN88DKnKj8GKyWpfjBbSzjDALcDejYKRgCs=
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241022124111-5361f0ecebd3/go.mod h1:3txOjFJ8M/JFs01h7xOrnQHVn6hZgDNA16ivyUlu1iU=
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
From 43764772aa6f90fbeaf99cd6bccdebc157b5330a Mon Sep 17 00:00:00 2001
From: Nikita Zinkevich
Date: Thu, 10 Oct 2024 11:59:53 +0300
Subject: [PATCH 099/161] [#151] index page: Add browse via native protocol
Signed-off-by: Nikita Zinkevich
---
cmd/http-gw/app.go | 20 ++-
cmd/http-gw/settings.go | 6 +-
config/config.env | 9 +
config/config.yaml | 3 +
docs/gate-configuration.md | 30 ++--
go.mod | 1 +
go.sum | 2 +
internal/handler/browse.go | 300 ++++++++++++++++++++++++++-----
internal/handler/download.go | 23 +--
internal/handler/handler.go | 87 +++++----
internal/handler/handler_test.go | 12 +-
internal/handler/head.go | 4 +-
internal/handler/utils.go | 21 +--
internal/logs/logs.go | 6 +-
internal/templates/index.gotmpl | 42 +++--
tree/tree.go | 104 ++++++++++-
16 files changed, 537 insertions(+), 133 deletions(-)
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 853977c..0dd53a6 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -40,6 +40,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
+ "github.com/panjf2000/ants/v2"
"github.com/spf13/viper"
"github.com/valyala/fasthttp"
"go.opentelemetry.io/otel/trace"
@@ -89,6 +90,7 @@ type (
appSettings struct {
reconnectInterval time.Duration
dialerSource *internalnet.DialerSource
+ workerPoolSize int
mu sync.RWMutex
defaultTimestamp bool
@@ -184,6 +186,7 @@ func (a *app) initAppSettings() {
a.settings = &appSettings{
reconnectInterval: fetchReconnectInterval(a.cfg),
dialerSource: getDialerSource(a.log, a.cfg),
+ workerPoolSize: a.cfg.GetInt(cfgWorkerPoolSize),
}
a.settings.update(a.cfg, a.log)
}
@@ -490,7 +493,13 @@ func (a *app) setHealthStatus() {
}
func (a *app) Serve() {
- handler := handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool)))
+ workerPool := a.initWorkerPool()
+ defer func() {
+ workerPool.Release()
+ close(a.webDone)
+ }()
+
+ handler := handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool)), workerPool)
// Configure router.
a.configureRouter(handler)
@@ -532,8 +541,14 @@ LOOP:
a.metrics.Shutdown()
a.stopServices()
a.shutdownTracing()
+}
- close(a.webDone)
+func (a *app) initWorkerPool() *ants.Pool {
+ workerPool, err := ants.NewPool(a.settings.workerPoolSize)
+ if err != nil {
+ a.log.Fatal(logs.FailedToCreateWorkerPool, zap.Error(err))
+ }
+ return workerPool
}
func (a *app) shutdownTracing() {
@@ -609,6 +624,7 @@ func (a *app) stopServices() {
svc.ShutDown(ctx)
}
}
+
func (a *app) configureRouter(handler *handler.Handler) {
r := router.New()
r.RedirectTrailingSlash = true
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 4f1712b..316c500 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -71,6 +71,8 @@ const (
cfgIndexPageEnabled = "index_page.enabled"
cfgIndexPageTemplatePath = "index_page.template_path"
+ cfgWorkerPoolSize = "worker_pool_size"
+
// Web.
cfgWebReadBufferSize = "web.read_buffer_size"
cfgWebWriteBufferSize = "web.write_buffer_size"
@@ -228,9 +230,6 @@ func settings() *viper.Viper {
// pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
- v.SetDefault(cfgIndexPageEnabled, false)
- v.SetDefault(cfgIndexPageTemplatePath, "")
-
// frostfs:
v.SetDefault(cfgBufferMaxSizeForPut, defaultBufferMaxSizeForPut)
@@ -242,6 +241,7 @@ func settings() *viper.Viper {
v.SetDefault(cfgWebStreamRequestBody, true)
v.SetDefault(cfgWebMaxRequestBodySize, fasthttp.DefaultMaxRequestBodySize)
+ v.SetDefault(cfgWorkerPoolSize, 1000)
// upload header
v.SetDefault(cfgUploaderHeaderEnableDefaultTimestamp, false)
diff --git a/config/config.env b/config/config.env
index db54c92..fd51392 100644
--- a/config/config.env
+++ b/config/config.env
@@ -150,3 +150,12 @@ HTTP_GW_MULTINET_FALLBACK_DELAY=300ms
# List of subnets and IP addresses to use as source for those subnets
HTTP_GW_MULTINET_SUBNETS_1_MASK=1.2.3.4/24
HTTP_GW_MULTINET_SUBNETS_1_SOURCE_IPS=1.2.3.4 1.2.3.5
+
+# Number of workers in handler's worker pool
+HTTP_GW_WORKER_POOL_SIZE=1000
+
+# Index page
+# Enable index page support
+HTTP_GW_INDEX_PAGE_ENABLED=false
+# Index page template path
+HTTP_GW_INDEX_PAGE_TEMPLATE_PATH=internal/handler/templates/index.gotmpl
\ No newline at end of file
diff --git a/config/config.yaml b/config/config.yaml
index 6c89e78..ef5c529 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -113,6 +113,9 @@ request_timeout: 5s # Timeout to check node health during rebalance.
rebalance_timer: 30s # Interval to check nodes health.
pool_error_threshold: 100 # The number of errors on connection after which node is considered as unhealthy.
+# Number of workers in handler's worker pool
+worker_pool_size: 1000
+
# Enable index page to see objects list for specified container and prefix
index_page:
enabled: false
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index 5b5b018..c6cb617 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -75,18 +75,21 @@ request_timeout: 5s
rebalance_timer: 30s
pool_error_threshold: 100
reconnect_interval: 1m
+worker_pool_size: 1000
+
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|------------------------|------------|---------------|---------------|-------------------------------------------------------------------------------------------------|
-| `rpc_endpoint` | `string` | yes | | The address of the RPC host to which the gateway connects to resolve bucket names. |
-| `resolve_order` | `[]string` | yes | `[nns, dns]` | Order of bucket name resolvers to use. |
-| `connect_timeout` | `duration` | | `10s` | Timeout to connect to a node. |
-| `stream_timeout` | `duration` | | `10s` | Timeout for individual operations in streaming RPC. |
-| `request_timeout` | `duration` | | `15s` | Timeout to check node health during rebalance. |
-| `rebalance_timer` | `duration` | | `60s` | Interval to check node health. |
-| `pool_error_threshold` | `uint32` | | `100` | The number of errors on connection after which node is considered as unhealthy. |
-| `reconnect_interval` | `duration` | no | `1m` | Listeners reconnection interval. |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|------------------------|------------|---------------|---------------|------------------------------------------------------------------------------------|
+| `rpc_endpoint` | `string` | yes | | The address of the RPC host to which the gateway connects to resolve bucket names. |
+| `resolve_order` | `[]string` | yes | `[nns, dns]` | Order of bucket name resolvers to use. |
+| `connect_timeout` | `duration` | | `10s` | Timeout to connect to a node. |
+| `stream_timeout` | `duration` | | `10s` | Timeout for individual operations in streaming RPC. |
+| `request_timeout` | `duration` | | `15s` | Timeout to check node health during rebalance. |
+| `rebalance_timer` | `duration` | | `60s` | Interval to check node health. |
+| `pool_error_threshold` | `uint32` | | `100` | The number of errors on connection after which node is considered as unhealthy. |
+| `reconnect_interval` | `duration` | no | `1m` | Listeners reconnection interval. |
+| `worker_pool_size` | `int` | no | `1000` | Maximum worker count in handler's worker pool. |
# `wallet` section
@@ -374,7 +377,12 @@ resolve_bucket:
# `index_page` section
-Parameters for index HTML-page output with S3-bucket or S3-subdir content for `Get object` request
+Parameters for index HTML-page output. Activates if `GetObject` request returns `not found`. Two
+index page modes available:
+
+* `s3` mode uses tree service for listing objects,
+* `native` sends requests to nodes via native protocol.
+ If request pass S3-bucket name instead of CID, `s3` mode will be used, otherwise `native`.
```yaml
index_page:
diff --git a/go.mod b/go.mod
index ca8f4fe..a2f41d8 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@ require (
github.com/docker/go-units v0.4.0
github.com/fasthttp/router v1.4.1
github.com/nspcc-dev/neo-go v0.106.2
+ github.com/panjf2000/ants/v2 v2.5.0
github.com/prometheus/client_golang v1.19.0
github.com/prometheus/client_model v0.5.0
github.com/spf13/pflag v1.0.5
diff --git a/go.sum b/go.sum
index 197f21c..a7a5be4 100644
--- a/go.sum
+++ b/go.sum
@@ -682,6 +682,8 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
+github.com/panjf2000/ants/v2 v2.5.0 h1:1rWGWSnxCsQBga+nQbA4/iY6VMeNoOIAM0ZWh9u3q2Q=
+github.com/panjf2000/ants/v2 v2.5.0/go.mod h1:cU93usDlihJZ5CfRGNDYsiBYvoilLvBF5Qp/BT2GNRE=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
diff --git a/internal/handler/browse.go b/internal/handler/browse.go
index c89d8c5..b24a569 100644
--- a/internal/handler/browse.go
+++ b/internal/handler/browse.go
@@ -1,15 +1,21 @@
package handler
import (
+ "context"
"html/template"
"net/url"
"sort"
"strconv"
"strings"
+ "sync"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
+ cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
+ "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
+ oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/docker/go-units"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
@@ -25,19 +31,68 @@ const (
type (
BrowsePageData struct {
- BucketName,
- Prefix string
- Objects []ResponseObject
+ HasErrors bool
+ Container string
+ Prefix string
+ Protocol string
+ Objects []ResponseObject
}
ResponseObject struct {
OID string
Created string
FileName string
+ FilePath string
Size string
IsDir bool
+ GetURL string
}
)
+func newListObjectsResponseS3(attrs map[string]string) ResponseObject {
+ return ResponseObject{
+ Created: formatTimestamp(attrs[attrCreated]),
+ OID: attrs[attrOID],
+ FileName: attrs[attrFileName],
+ Size: attrs[attrSize],
+ IsDir: attrs[attrOID] == "",
+ }
+}
+
+func newListObjectsResponseNative(attrs map[string]string) ResponseObject {
+ filename := lastPathElement(attrs[object.AttributeFilePath])
+ if filename == "" {
+ filename = attrs[attrFileName]
+ }
+ return ResponseObject{
+ OID: attrs[attrOID],
+ Created: formatTimestamp(attrs[object.AttributeTimestamp] + "000"),
+ FileName: filename,
+ FilePath: attrs[object.AttributeFilePath],
+ Size: attrs[attrSize],
+ IsDir: false,
+ }
+}
+
+func getNextDir(filepath, prefix string) string {
+ restPath := strings.Replace(filepath, prefix, "", 1)
+ index := strings.Index(restPath, "/")
+ if index == -1 {
+ return ""
+ }
+ return restPath[:index]
+}
+
+func lastPathElement(path string) string {
+ if path == "" {
+ return path
+ }
+ index := strings.LastIndex(path, "/")
+ if index == len(path)-1 {
+ index = strings.LastIndex(path[:index], "/")
+ }
+ return path[index+1:]
+}
+
func parseTimestamp(tstamp string) (time.Time, error) {
millis, err := strconv.ParseInt(tstamp, 10, 64)
if err != nil {
@@ -47,16 +102,6 @@ func parseTimestamp(tstamp string) (time.Time, error) {
return time.UnixMilli(millis), nil
}
-func NewResponseObject(nodes map[string]string) ResponseObject {
- return ResponseObject{
- OID: nodes[attrOID],
- Created: nodes[attrCreated],
- FileName: nodes[attrFileName],
- Size: nodes[attrSize],
- IsDir: nodes[attrOID] == "",
- }
-}
-
func formatTimestamp(strdate string) string {
date, err := parseTimestamp(strdate)
if err != nil || date.IsZero() {
@@ -94,12 +139,9 @@ func trimPrefix(encPrefix string) string {
return prefix[:slashIndex]
}
-func urlencode(prefix, filename string) string {
+func urlencode(path string) string {
var res strings.Builder
- path := filename
- if prefix != "" {
- path = strings.Join([]string{prefix, filename}, "/")
- }
+
prefixParts := strings.Split(path, "/")
for _, prefixPart := range prefixParts {
prefixPart = "/" + url.PathEscape(prefixPart)
@@ -112,46 +154,220 @@ func urlencode(prefix, filename string) string {
return res.String()
}
-func (h *Handler) browseObjects(c *fasthttp.RequestCtx, bucketInfo *data.BucketInfo, prefix string) {
+type GetObjectsResponse struct {
+ objects []ResponseObject
+ hasErrors bool
+}
+
+func (h *Handler) getDirObjectsS3(ctx context.Context, bucketInfo *data.BucketInfo, prefix string) (*GetObjectsResponse, error) {
+ nodes, _, err := h.tree.GetSubTreeByPrefix(ctx, bucketInfo, prefix, true)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &GetObjectsResponse{
+ objects: make([]ResponseObject, 0, len(nodes)),
+ }
+ for _, node := range nodes {
+ meta := node.GetMeta()
+ if meta == nil {
+ continue
+ }
+ var attrs = make(map[string]string, len(meta))
+ for _, m := range meta {
+ attrs[m.GetKey()] = string(m.GetValue())
+ }
+ obj := newListObjectsResponseS3(attrs)
+ obj.FilePath = prefix + obj.FileName
+ obj.GetURL = "/get/" + bucketInfo.Name + urlencode(obj.FilePath)
+ result.objects = append(result.objects, obj)
+ }
+
+ return result, nil
+}
+
+func (h *Handler) getDirObjectsNative(ctx context.Context, bucketInfo *data.BucketInfo, prefix string) (*GetObjectsResponse, error) {
+ var basePath string
+ if ind := strings.LastIndex(prefix, "/"); ind != -1 {
+ basePath = prefix[:ind+1]
+ }
+
+ filters := object.NewSearchFilters()
+ filters.AddRootFilter()
+ if prefix != "" {
+ filters.AddFilter(object.AttributeFilePath, prefix, object.MatchCommonPrefix)
+ }
+
+ prm := PrmObjectSearch{
+ PrmAuth: PrmAuth{
+ BearerToken: bearerToken(ctx),
+ },
+ Container: bucketInfo.CID,
+ Filters: filters,
+ }
+ objectIDs, err := h.frostfs.SearchObjects(ctx, prm)
+ if err != nil {
+ return nil, err
+ }
+ defer objectIDs.Close()
+
+ resp, err := h.headDirObjects(ctx, bucketInfo.CID, objectIDs, basePath)
+ if err != nil {
+ return nil, err
+ }
+
+ log := utils.GetReqLogOrDefault(ctx, h.log)
+ dirs := make(map[string]struct{})
+ result := &GetObjectsResponse{
+ objects: make([]ResponseObject, 0, 100),
+ }
+ for objExt := range resp {
+ if objExt.Error != nil {
+ log.Error(logs.FailedToHeadObject, zap.Error(objExt.Error))
+ result.hasErrors = true
+ continue
+ }
+ if objExt.Object.IsDir {
+ if _, ok := dirs[objExt.Object.FileName]; ok {
+ continue
+ }
+ objExt.Object.GetURL = "/get/" + bucketInfo.CID.EncodeToString() + urlencode(objExt.Object.FilePath)
+ dirs[objExt.Object.FileName] = struct{}{}
+ } else {
+ objExt.Object.GetURL = "/get/" + bucketInfo.CID.EncodeToString() + "/" + objExt.Object.OID
+ }
+ result.objects = append(result.objects, objExt.Object)
+ }
+ return result, nil
+}
+
+type ResponseObjectExtended struct {
+ Object ResponseObject
+ Error error
+}
+
+func (h *Handler) headDirObjects(ctx context.Context, cnrID cid.ID, objectIDs ResObjectSearch, basePath string) (<-chan ResponseObjectExtended, error) {
+ res := make(chan ResponseObjectExtended)
+
+ go func() {
+ defer close(res)
+ log := utils.GetReqLogOrDefault(ctx, h.log).With(
+ zap.String("cid", cnrID.EncodeToString()),
+ zap.String("path", basePath),
+ )
+ var wg sync.WaitGroup
+ err := objectIDs.Iterate(func(id oid.ID) bool {
+ wg.Add(1)
+ err := h.workerPool.Submit(func() {
+ defer wg.Done()
+ var obj ResponseObjectExtended
+ obj.Object, obj.Error = h.headDirObject(ctx, cnrID, id, basePath)
+ res <- obj
+ })
+ if err != nil {
+ wg.Done()
+ log.Warn(logs.FailedToSumbitTaskToPool, zap.Error(err))
+ }
+ select {
+ case <-ctx.Done():
+ return true
+ default:
+ return false
+ }
+ })
+ if err != nil {
+ log.Error(logs.FailedToIterateOverResponse, zap.Error(err))
+ }
+ wg.Wait()
+ }()
+
+ return res, nil
+}
+
+func (h *Handler) headDirObject(ctx context.Context, cnrID cid.ID, objID oid.ID, basePath string) (ResponseObject, error) {
+ addr := newAddress(cnrID, objID)
+ obj, err := h.frostfs.HeadObject(ctx, PrmObjectHead{
+ PrmAuth: PrmAuth{BearerToken: bearerToken(ctx)},
+ Address: addr,
+ })
+ if err != nil {
+ return ResponseObject{}, err
+ }
+
+ attrs := loadAttributes(obj.Attributes())
+ attrs[attrOID] = objID.EncodeToString()
+ if multipartSize, ok := attrs[attributeMultipartObjectSize]; ok {
+ attrs[attrSize] = multipartSize
+ } else {
+ attrs[attrSize] = strconv.FormatUint(obj.PayloadSize(), 10)
+ }
+
+ dirname := getNextDir(attrs[object.AttributeFilePath], basePath)
+ if dirname == "" {
+ return newListObjectsResponseNative(attrs), nil
+ }
+
+ return ResponseObject{
+ FileName: dirname,
+ FilePath: basePath + dirname,
+ IsDir: true,
+ }, nil
+}
+
+type browseParams struct {
+ bucketInfo *data.BucketInfo
+ prefix string
+ isNative bool
+ listObjects func(ctx context.Context, bucketName *data.BucketInfo, prefix string) (*GetObjectsResponse, error)
+}
+
+func (h *Handler) browseObjects(c *fasthttp.RequestCtx, p browseParams) {
+ const S3Protocol = "s3"
+ const FrostfsProtocol = "frostfs"
+
ctx := utils.GetContextFromRequest(c)
reqLog := utils.GetReqLogOrDefault(ctx, h.log)
- log := reqLog.With(zap.String("bucket", bucketInfo.Name))
-
- nodes, err := h.listObjects(ctx, bucketInfo, prefix)
+ log := reqLog.With(
+ zap.String("bucket", p.bucketInfo.Name),
+ zap.String("container", p.bucketInfo.CID.EncodeToString()),
+ zap.String("prefix", p.prefix),
+ )
+ resp, err := p.listObjects(ctx, p.bucketInfo, p.prefix)
if err != nil {
logAndSendBucketError(c, log, err)
return
}
- respObjects := make([]ResponseObject, len(nodes))
-
- for i, node := range nodes {
- respObjects[i] = NewResponseObject(node)
- }
-
- sort.Slice(respObjects, func(i, j int) bool {
- if respObjects[i].IsDir == respObjects[j].IsDir {
- return respObjects[i].FileName < respObjects[j].FileName
+ objects := resp.objects
+ sort.Slice(objects, func(i, j int) bool {
+ if objects[i].IsDir == objects[j].IsDir {
+ return objects[i].FileName < objects[j].FileName
}
- return respObjects[i].IsDir
+ return objects[i].IsDir
})
- indexTemplate := h.config.IndexPageTemplate()
tmpl, err := template.New("index").Funcs(template.FuncMap{
- "formatTimestamp": formatTimestamp,
- "formatSize": formatSize,
- "trimPrefix": trimPrefix,
- "urlencode": urlencode,
- "parentDir": parentDir,
- }).Parse(indexTemplate)
+ "formatSize": formatSize,
+ "trimPrefix": trimPrefix,
+ "urlencode": urlencode,
+ "parentDir": parentDir,
+ }).Parse(h.config.IndexPageTemplate())
if err != nil {
logAndSendBucketError(c, log, err)
return
}
+ bucketName := p.bucketInfo.Name
+ protocol := S3Protocol
+ if p.isNative {
+ bucketName = p.bucketInfo.CID.EncodeToString()
+ protocol = FrostfsProtocol
+ }
if err = tmpl.Execute(c, &BrowsePageData{
- BucketName: bucketInfo.Name,
- Prefix: prefix,
- Objects: respObjects,
+ Container: bucketName,
+ Prefix: p.prefix,
+ Objects: objects,
+ Protocol: protocol,
+ HasErrors: resp.hasErrors,
}); err != nil {
logAndSendBucketError(c, log, err)
return
diff --git a/internal/handler/download.go b/internal/handler/download.go
index 19380d4..cd4e55a 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -23,13 +23,16 @@ import (
// DownloadByAddressOrBucketName handles download requests using simple cid/oid or bucketname/key format.
func (h *Handler) DownloadByAddressOrBucketName(c *fasthttp.RequestCtx) {
- test, _ := c.UserValue("oid").(string)
- var id oid.ID
- err := id.DecodeString(test)
- if err != nil {
- h.byObjectName(c, h.receiveFile)
- } else {
- h.byAddress(c, h.receiveFile)
+ oidURLParam := c.UserValue("oid").(string)
+ downloadQueryParam := c.QueryArgs().GetBool("download")
+
+ switch {
+ case isObjectID(oidURLParam):
+ h.byNativeAddress(c, h.receiveFile)
+ case !isContainerRoot(oidURLParam) && (downloadQueryParam || !isDir(oidURLParam)):
+ h.byS3Path(c, h.receiveFile)
+ default:
+ h.browseIndex(c)
}
}
@@ -45,7 +48,7 @@ func (h *Handler) DownloadByAttribute(c *fasthttp.RequestCtx) {
h.byAttribute(c, h.receiveFile)
}
-func (h *Handler) search(ctx context.Context, cnrID *cid.ID, key, val string, op object.SearchMatchType) (ResObjectSearch, error) {
+func (h *Handler) search(ctx context.Context, cnrID cid.ID, key, val string, op object.SearchMatchType) (ResObjectSearch, error) {
filters := object.NewSearchFilters()
filters.AddRootFilter()
filters.AddFilter(key, val, op)
@@ -54,7 +57,7 @@ func (h *Handler) search(ctx context.Context, cnrID *cid.ID, key, val string, op
PrmAuth: PrmAuth{
BearerToken: bearerToken(ctx),
},
- Container: *cnrID,
+ Container: cnrID,
Filters: filters,
}
@@ -102,7 +105,7 @@ func (h *Handler) DownloadZipped(c *fasthttp.RequestCtx) {
return
}
- resSearch, err := h.search(ctx, &bktInfo.CID, object.AttributeFilePath, prefix, object.MatchCommonPrefix)
+ resSearch, err := h.search(ctx, bktInfo.CID, object.AttributeFilePath, prefix, object.MatchCommonPrefix)
if err != nil {
log.Error(logs.CouldNotSearchForObjects, zap.Error(err))
response.Error(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index 62d0897..9ed7f99 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -22,6 +22,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
+ "github.com/panjf2000/ants/v2"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -165,6 +166,7 @@ type Handler struct {
containerResolver ContainerResolver
tree *tree.Tree
cache *cache.BucketCache
+ workerPool *ants.Pool
}
type AppParams struct {
@@ -175,7 +177,7 @@ type AppParams struct {
Cache *cache.BucketCache
}
-func New(params *AppParams, config Config, tree *tree.Tree) *Handler {
+func New(params *AppParams, config Config, tree *tree.Tree, workerPool *ants.Pool) *Handler {
return &Handler{
log: params.Logger,
frostfs: params.FrostFS,
@@ -184,14 +186,15 @@ func New(params *AppParams, config Config, tree *tree.Tree) *Handler {
containerResolver: params.Resolver,
tree: tree,
cache: params.Cache,
+ workerPool: workerPool,
}
}
-// byAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that
+// byNativeAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that
// prepares request and object address to it.
-func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
+func (h *Handler) byNativeAddress(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
idCnr, _ := c.UserValue("cid").(string)
- idObj, _ := c.UserValue("oid").(string)
+ idObj, _ := url.PathUnescape(c.UserValue("oid").(string))
ctx := utils.GetContextFromRequest(c)
reqLog := utils.GetReqLogOrDefault(ctx, h.log)
@@ -215,12 +218,11 @@ func (h *Handler) byAddress(c *fasthttp.RequestCtx, f func(context.Context, requ
f(ctx, *h.newRequest(c, log), addr)
}
-// byObjectName is a wrapper for function (e.g. request.headObject, request.receiveFile) that
-// prepares request and object address to it.
-func (h *Handler) byObjectName(c *fasthttp.RequestCtx, f func(context.Context, request, oid.Address)) {
+// byS3Path is a wrapper for function (e.g. request.headObject, request.receiveFile) that
+// resolves object address from S3-like path /