diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 8b450a4..f45c864 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -1,9 +1,9 @@ -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 -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 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/.forgejo/workflows/builds.yml b/.forgejo/workflows/builds.yml index 97ac86b..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: @@ -6,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_versions: [ '1.20', '1.21' ] + 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 14b9edf..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: @@ -10,7 +14,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: '1.21' + go-version: '1.23' cache: true - name: Install linters @@ -24,7 +28,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_versions: [ '1.20', '1.21' ] + go_versions: [ '1.22', '1.23' ] fail-fast: false steps: - uses: actions/checkout@v3 @@ -38,4 +42,4 @@ jobs: run: make dep - name: Run tests - run: make test \ No newline at end of file + run: make test diff --git a/.forgejo/workflows/vulncheck.yml b/.forgejo/workflows/vulncheck.yml index 0139e89..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: @@ -12,7 +16,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/.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/.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 105ac41..e528b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,19 +4,89 @@ This document outlines major changes between releases. ## [Unreleased] -## [0.28.1] - 2024-01-24 +## [0.32.0] - Khumbu - 2024-12-20 + +### Fixed +- Getting S3 object with FrostFS Object ID-like key (#166) +- Ignore delete marked objects in versioned bucket in index page (#181) ### Added -- Tree pool traversal limit (#92) -- Add new `reconnect_interval` config param (#100) +- Metric of dropped logs by log sampler (#150) +- Fallback FileName attribute search during FilePath attribute search (#174) -### Update from 0.28.0 -See new `frostfs.tree_pool_max_attempts` config parameter. +### Changed +- Updated tree service pool without api-go dependency (#178) + +## [0.31.0] - Rongbuk - 2024-11-20 + +### Fixed +- Docker warnings during image build (#126) +- `trace_id` parameter in logs (#148) +- SIGHUP support for `tracing.enabled` config parameter (#157) + +### Added +- Vulnerability report document (#123) +- Root CA configuration for tracing (#139) +- Log sampling policy configuration (#147) +- Index page support for buckets and containers (#137, #151) +- CORS support (#158) +- Source IP binding configuration for FrostFS requests (#160) +- Tracing attributes (#164) + +### Changed +- Updated Go version to 1.22 (#132) + +### Removed +- Duplicated NNS Resolver code (#129) + +## [0.30.3] - 2024-10-18 + +### Fixed +- Get response on S3 multipart object (#142) + +### Added +- Support percent-encoding for GET queries (#134) + +### Changed +- Split `FrostFS` interface into separate read methods (#127) + +## [0.30.2] - 2024-09-03 + +### Added +- Fuzzing tests (#135) + +## [0.30.1] - 2024-08-20 + +### Fixed +- Error counting in pool component before connection switch (#131) + +### Added +- Log of endpoint address during tree pool errors (#131) + +## [0.30.0] - Kangshung - 2024-07-22 + +### Fixed +- Handle query unescape and invalid bearer token errors (#107) +- Fix HTTP/2 requests (#110) + +### Added +- Add new `reconnect_interval` config param (#100) +- Erasure coding support in placement policy (#114) +- HTTP Header canonicalizer for well-known headers (#121) + +### 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 +94,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 +177,11 @@ 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 +[0.30.1]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.30.0...v0.30.1 +[0.30.2]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.30.1...v0.30.2 +[0.30.3]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.30.2...v0.30.3 +[0.31.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.30.3...v0.31.0 +[0.32.0]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.31.0...v0.32.0 +[Unreleased]: https://git.frostfs.info/TrueCloudLab/frostfs-http-gw/compare/v0.32.0...master \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..de5e48e --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +.* @TrueCloudLab/storage-services-developers @TrueCloudLab/storage-services-committers +.forgejo/.* @potyarkin +Makefile @potyarkin diff --git a/Makefile b/Makefile index d02d41b..c1f4f50 100755 --- a/Makefile +++ b/Makefile @@ -2,9 +2,9 @@ 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.54.0 -TRUECLOUDLAB_LINT_VERSION ?= 0.0.2 +GO_VERSION ?= 1.22 +LINT_VERSION ?= 1.60.3 +TRUECLOUDLAB_LINT_VERSION ?= 0.0.6 BUILD ?= $(shell date -u --iso=seconds) HUB_IMAGE ?= truecloudlab/frostfs-http-gw @@ -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 6e19d31..adf793c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-
+
FrostFS is a decentralized distributed object storage integrated with the NEO Blockchain.
@@ -217,41 +217,8 @@ Also, in case of downloading, you need to have a file inside a container.
### NNS
In all download/upload routes you can use container name instead of its id (`$CID`).
+Read more about it in [docs/nns.md](./docs/nns.md).
-Steps to start using name resolving:
-
-1. Enable NNS resolving in config (`rpc_endpoint` must be a valid neo rpc node, see [configs](./config) for other examples):
-
-```yaml
-rpc_endpoint: http://morph-chain.frostfs.devenv:30333
-resolve_order:
- - nns
-```
-
-2. Make sure your container is registered in NNS contract. If you use [frostfs-dev-env](https://git.frostfs.info/TrueCloudLab/frostfs-dev-env)
-you can check if your container (e.g. with `container-name` name) is registered in NNS:
-
-```shell
-$ curl -s --data '{"id":1,"jsonrpc":"2.0","method":"getcontractstate","params":[1]}' \
- http://morph-chain.frostfs.devenv:30333 | jq -r '.result.hash'
-
-0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667
-
-$ docker exec -it morph_chain neo-go \
- contract testinvokefunction \
- -r http://morph-chain.frostfs.devenv:30333 0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667 \
- resolve string:container-name.container int:16 \
- | jq -r '.stack[0].value | if type=="array" then .[0].value else . end' \
- | base64 -d && echo
-
-7f3vvkw4iTiS5ZZbu5BQXEmJtETWbi3uUjLNaSs29xrL
-```
-
-3. Use container name instead of its `$CID`. For example:
-
-```shell
-$ curl http://localhost:8082/get_by_attribute/container-name/FileName/object-name
-```
#### Create a container
@@ -462,126 +429,7 @@ object ID, like this:
#### Authentication
-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
-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
-to grant access.
-
-FrostFS Bearer Token basically is a container owner-signed ACL data (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
- * "Bearer" cookie with base64-encoded token contents
-
-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
-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).
-
-Suppose we have:
-* **NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3** (token owner (gateway address))
-
-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==
-```
-
-2. Form a Bearer token (10000 is lifetime expiration in epoch) and save it to **bearer.json**:
-```
-{
- "body": {
- "allowImpersonate": true,
- "ownerID": {
- "value": "NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg=="
- },
- "lifetime": {
- "exp": "10000",
- "nbf": "0",
- "iat": "0"
- }
- },
- "signature": null
-}
-```
-
-3. Sign it with the wallet:
-```
-$ frostfs-cli util sign bearer-token --from bearer.json --to signed.json -w ./wallet.json
-```
-
-4. Encode to base64 to use in header:
-```
-$ base64 -w 0 signed.json
-# output: Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw==
-```
-
-After that, the Bearer token can be used:
-
-```
-$ curl -F 'file=@cat.jpeg;filename=cat.jpeg' -H "Authorization: Bearer Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw==" \
- http://localhost:8082/upload/BJeErH9MWmf52VsR1mLWKkgF3pRm3FkubYxM7TZkBP4K
-# output:
-# {
-# "object_id": "DhfES9nVrFksxGDD2jQLunGADfrXExxNwqXbDafyBn9X",
-# "container_id": "BJeErH9MWmf52VsR1mLWKkgF3pRm3FkubYxM7TZkBP4K"
-# }
-```
-
-##### 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
-
-For example:
-```
-$ frostfs-cli -w ./wallet.json --basic-acl 0x0FFFCFFF -r 192.168.130.72:8080 container create --policy "REP 3" --await
-```
-
-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**:
-```
-{
- "version": {
- "major": 0,
- "minor": 0
- },
- "containerID": {
- "value": "mRnZWzewzxjzIPa7Fqlfqdl3TM1KpJ0YnsXsEhafJJg="
- },
- "records": [
- {
- "operation": "PUT",
- "action": "DENY",
- "filters": [],
- "targets": [
- {
- "role": "OTHERS",
- "keys": []
- }
- ]
- }
- ]
-}
-```
+Read more about request authentication in [docs/authentication.md](./docs/authemtnication.md)
### Metrics and Pprof
@@ -592,3 +440,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/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.
diff --git a/VERSION b/VERSION
index 244df55..420000f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.28.1
+v0.32.0
diff --git a/cmd/http-gw/app.go b/cmd/http-gw/app.go
index 2a20d86..9d90ccd 100644
--- a/cmd/http-gw/app.go
+++ b/cmd/http-gw/app.go
@@ -1,7 +1,9 @@
package main
import (
+ "bytes"
"context"
+ "crypto/x509"
"errors"
"fmt"
"net/http"
@@ -14,19 +16,20 @@ import (
"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"
+ 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"
"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-observability/tracing"
+ v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
"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"
@@ -36,43 +39,48 @@ 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"
"go.uber.org/zap"
"golang.org/x/exp/slices"
)
type (
app struct {
- ctx context.Context
- log *zap.Logger
- logLevel zap.AtomicLevel
- pool *pool.Pool
- treePool *treepool.Pool
- key *keys.PrivateKey
- owner *user.ID
- cfg *viper.Viper
- webServer *fasthttp.Server
- webDone chan struct{}
- resolver *resolver.ContainerResolver
- metrics *gateMetrics
- services []*metrics.Service
- settings *appSettings
+ ctx context.Context
+ log *zap.Logger
+ logLevel zap.AtomicLevel
+ pool *pool.Pool
+ treePool *treepool.Pool
+ key *keys.PrivateKey
+ owner *user.ID
+ cfg *viper.Viper
+ webServer *fasthttp.Server
+ webDone chan struct{}
+ resolver *resolver.ContainerResolver
+ metrics *gateMetrics
+ services []*metrics.Service
+ settings *appSettings
+ loggerSettings *loggerSettings
servers []Server
unbindServers []ServerInfo
mu sync.RWMutex
}
+ loggerSettings struct {
+ mu sync.RWMutex
+ appMetrics *metrics.GateMetrics
+ }
+
// App is an interface for the main gateway function.
App interface {
Wait()
Serve()
}
- // Option is an application option.
- Option func(a *app)
-
gateMetrics struct {
logger *zap.Logger
provider *metrics.GateMetrics
@@ -83,50 +91,52 @@ type (
// appSettings stores reloading parameters, so it has to provide getters and setters which use RWMutex.
appSettings struct {
reconnectInterval time.Duration
+ dialerSource *internalnet.DialerSource
+ workerPoolSize int
- mu sync.RWMutex
- defaultTimestamp bool
- zipCompression bool
- clientCut bool
- 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
+ enableFilepathFallback bool
+ }
+
+ CORS struct {
+ AllowOrigin string
+ AllowMethods []string
+ AllowHeaders []string
+ ExposeHeaders []string
+ AllowCredentials bool
+ MaxAge int
}
)
-// WithLogger returns Option to set a specific logger.
-func WithLogger(l *zap.Logger, lvl zap.AtomicLevel) Option {
- return func(a *app) {
- if l == nil {
- return
- }
- a.log = l
- a.logLevel = lvl
- }
-}
+func newApp(ctx context.Context, v *viper.Viper) App {
+ logSettings := &loggerSettings{}
+ log := pickLogger(v, logSettings)
-// WithConfig returns Option to use specific Viper configuration.
-func WithConfig(c *viper.Viper) Option {
- return func(a *app) {
- if c == nil {
- return
- }
- a.cfg = c
- }
-}
-
-func newApp(ctx context.Context, opt ...Option) App {
a := &app{
- ctx: ctx,
- log: zap.L(),
- cfg: viper.GetViper(),
- webServer: new(fasthttp.Server),
- webDone: make(chan struct{}),
- }
- for i := range opt {
- opt[i](a)
+ ctx: ctx,
+ log: log.logger,
+ cfg: v,
+ loggerSettings: logSettings,
+ webServer: new(fasthttp.Server),
+ webDone: make(chan struct{}),
}
+ a.initAppSettings()
+
// -- setup FastHTTP server --
a.webServer.Name = "frost-http-gw"
a.webServer.ReadBufferSize = a.cfg.GetInt(cfgWebReadBufferSize)
@@ -140,7 +150,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)
@@ -148,7 +158,6 @@ func newApp(ctx context.Context, opt ...Option) App {
a.setRuntimeParameters()
- a.initAppSettings()
a.initResolver()
a.initMetrics()
a.initTracing(ctx)
@@ -156,28 +165,117 @@ func newApp(ctx context.Context, opt ...Option) App {
return a
}
+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)
+}
+
+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)
+ corsAllowOrigin := v.GetString(cfgCORSAllowOrigin)
+ corsAllowMethods := v.GetStringSlice(cfgCORSAllowMethods)
+ corsAllowHeaders := v.GetStringSlice(cfgCORSAllowHeaders)
+ corsExposeHeaders := v.GetStringSlice(cfgCORSExposeHeaders)
+ corsAllowCredentials := v.GetBool(cfgCORSAllowCredentials)
+ corsMaxAge := fetchCORSMaxAge(v)
+ enableFilepathFallback := v.GetBool(cfgFeaturesEnableFilepathFallback)
+
+ 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
+ s.corsAllowOrigin = corsAllowOrigin
+ s.corsAllowMethods = corsAllowMethods
+ s.corsAllowHeaders = corsAllowHeaders
+ s.corsExposeHeaders = corsExposeHeaders
+ s.corsAllowCredentials = corsAllowCredentials
+ s.corsMaxAge = corsMaxAge
+ s.enableFilepathFallback = enableFilepathFallback
+}
+
+func (s *loggerSettings) DroppedLogsInc() {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ if s.appMetrics != nil {
+ s.appMetrics.DroppedLogsInc()
+ }
+}
+
+func (s *loggerSettings) setMetrics(appMetrics *metrics.GateMetrics) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.appMetrics = appMetrics
+}
+
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 (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) 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 {
@@ -186,29 +284,33 @@ func (s *appSettings) ClientCut() bool {
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 (s *appSettings) EnableFilepathFallback() bool {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.enableFilepathFallback
}
func (a *app) initResolver() {
@@ -221,7 +323,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,
}
@@ -243,6 +345,7 @@ func (a *app) initMetrics() {
gateMetricsProvider := metrics.NewGateMetrics(a.pool)
a.metrics = newGateMetrics(a.log, gateMetricsProvider, a.cfg.GetBool(cfgPrometheusEnabled))
a.metrics.SetHealth(metrics.HealthStatusStarting)
+ a.loggerSettings.setMetrics(a.metrics.provider)
}
func newGateMetrics(logger *zap.Logger, provider *metrics.GateMetrics, enabled bool) *gateMetrics {
@@ -398,10 +501,16 @@ func (a *app) setHealthStatus() {
}
func (a *app) Serve() {
- handler := handler.New(a.AppParams(), a.settings, tree.NewTree(services.NewPoolWrapper(a.treePool)))
+ workerPool := a.initWorkerPool()
+ defer func() {
+ workerPool.Release()
+ close(a.webDone)
+ }()
+
+ handle := handler.New(a.AppParams(), a.settings, tree.NewTree(frostfs.NewPoolWrapper(a.treePool)), workerPool)
// Configure router.
- a.configureRouter(handler)
+ a.configureRouter(handle)
a.startServices()
a.initServers(a.ctx)
@@ -440,8 +549,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() {
@@ -471,6 +586,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))
}
@@ -484,7 +603,7 @@ 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)
@@ -493,15 +612,6 @@ func (a *app) configReload(ctx context.Context) {
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.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)
@@ -523,47 +633,171 @@ func (a *app) stopServices() {
}
}
-func (a *app) configureRouter(handler *handler.Handler) {
+func (a *app) configureRouter(h *handler.Handler) {
r := router.New()
r.RedirectTrailingSlash = true
r.NotFound = func(r *fasthttp.RequestCtx) {
- response.Error(r, "Not found", fasthttp.StatusNotFound)
+ handler.ResponseError(r, "Not found", fasthttp.StatusNotFound)
}
r.MethodNotAllowed = func(r *fasthttp.RequestCtx) {
- response.Error(r, "Method Not Allowed", fasthttp.StatusMethodNotAllowed)
+ handler.ResponseError(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.addMiddlewares(h.Upload))
+ r.OPTIONS("/upload/{cid}", a.addPreflight())
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.addMiddlewares(h.DownloadByAddressOrBucketName))
+ r.HEAD("/get/{cid}/{oid:*}", a.addMiddlewares(h.HeadByAddressOrBucketName))
+ r.OPTIONS("/get/{cid}/{oid:*}", a.addPreflight())
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.addMiddlewares(h.DownloadByAttribute))
+ r.HEAD("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.addMiddlewares(h.HeadByAttribute))
+ r.OPTIONS("/get_by_attribute/{cid}/{attr_key}/{attr_val:*}", a.addPreflight())
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.addMiddlewares(h.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) {
- 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)
+ }
+}
+
+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)
+ 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))
- response.Error(req, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
+ log := utils.GetReqLogOrDefault(reqCtx, a.log)
+
+ log.Error(logs.CouldNotFetchAndStoreBearerToken, zap.Error(err))
+ handler.ResponseError(req, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
return
}
utils.SetContextToRequest(appCtx, req)
@@ -573,9 +807,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()
@@ -600,10 +832,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)),
@@ -703,6 +935,29 @@ 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
+ }
+
+ 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))
@@ -728,39 +983,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/integration_test.go b/cmd/http-gw/integration_test.go
index f76c3ce..6a4d428 100644
--- a/cmd/http-gw/integration_test.go
+++ b/cmd/http-gw/integration_test.go
@@ -6,30 +6,35 @@ import (
"archive/zip"
"bytes"
"context"
+ "encoding/base64"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
+ "os"
"sort"
+ "strings"
"testing"
"time"
- containerv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
+ containerv2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/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"
"github.com/testcontainers/testcontainers-go/wait"
- "go.uber.org/zap/zapcore"
)
type putResponse struct {
@@ -47,11 +52,19 @@ 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",
+ "1.5.0",
+ "1.6.5",
}
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,17 +72,26 @@ func TestIntegration(t *testing.T) {
ctx, cancel2 := context.WithCancel(rootCtx)
aioContainer := createDockerContainer(ctx, t, aioImage+version)
- server, cancel := runServer()
+ if strings.HasPrefix(version, "1.6") {
+ registerUser(t, ctx, aioContainer, file.Name())
+ }
+
+ // See the logs from the command execution.
+ server, cancel := runServer(file.Name())
clientPool := getPool(ctx, t, key)
- CID, err := createContainer(ctx, t, clientPool, ownerID, version)
+ CID, err := createContainer(ctx, t, clientPool, ownerID)
require.NoError(t, err, version)
- t.Run("simple put "+version, func(t *testing.T) { simplePut(ctx, t, clientPool, CID, version) })
+ token := makeBearerToken(t, key, ownerID, version)
+
+ t.Run("simple put "+version, func(t *testing.T) { simplePut(ctx, t, clientPool, CID) })
+ 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) })
- 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) })
+ t.Run("simple get "+version, func(t *testing.T) { simpleGet(ctx, t, clientPool, ownerID, CID) })
+ t.Run("get by attribute "+version, func(t *testing.T) { getByAttr(ctx, t, clientPool, ownerID, CID) })
+ t.Run("get zip "+version, func(t *testing.T) { getZip(ctx, t, clientPool, ownerID, CID) })
+ t.Run("test namespaces "+version, func(t *testing.T) { checkNamespaces(ctx, t, clientPool, ownerID, CID) })
cancel()
server.Wait()
@@ -79,18 +101,20 @@ 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()
- l, lvl := newStdoutLogger(zapcore.DebugLevel)
- application := newApp(cancelCtx, WithConfig(v), WithLogger(l, lvl))
+ v.Set(cfgWalletPath, pathToWallet)
+ v.Set(cfgWalletPassphrase, "")
+
+ application := newApp(cancelCtx, v)
go application.Serve()
return application, cancel
}
-func simplePut(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID, version string) {
+func simplePut(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID) {
url := testHost + "/upload/" + CID.String()
makePutRequestAndCheck(ctx, t, p, CID, url)
@@ -98,7 +122,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 +175,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)
@@ -206,7 +262,7 @@ func putWithDuplicateKeys(t *testing.T, CID cid.ID) {
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
}
-func simpleGet(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID, version string) {
+func simpleGet(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID) {
content := "content of file"
attributes := map[string]string{
"some-attr": "some-get-value",
@@ -253,7 +309,7 @@ func checkGetByAttrResponse(t *testing.T, resp *http.Response, content string, a
}
}
-func getByAttr(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID, version string) {
+func getByAttr(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID) {
keyAttr, valAttr := "some-attr", "some-get-by-attr-value"
content := "content of file"
attributes := map[string]string{keyAttr: valAttr}
@@ -275,7 +331,7 @@ func getByAttr(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID
checkGetByAttrResponse(t, resp, content, expectedAttr)
}
-func getZip(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID, version string) {
+func getZip(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID) {
names := []string{"zipfolder/dir/name1.txt", "zipfolder/name2.txt"}
contents := []string{"content of file1", "content of file2"}
attributes1 := map[string]string{object.AttributeFilePath: names[0]}
@@ -340,7 +396,7 @@ 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) {
+func checkNamespaces(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, CID cid.ID) {
content := "content of file"
attributes := map[string]string{
"some-attr": "some-get-value",
@@ -377,7 +433,7 @@ func checkNamespaces(ctx context.Context, t *testing.T, clientPool *pool.Pool, o
func createDockerContainer(ctx context.Context, t *testing.T, image string) testcontainers.Container {
req := testcontainers.ContainerRequest{
Image: image,
- WaitingFor: wait.NewLogStrategy("aio container started").WithStartupTimeout(30 * time.Second),
+ WaitingFor: wait.NewLogStrategy("aio container started").WithStartupTimeout(2 * time.Minute),
Name: "aio",
Hostname: "aio",
NetworkMode: "host",
@@ -417,7 +473,7 @@ func getPool(ctx context.Context, t *testing.T, key *keys.PrivateKey) *pool.Pool
return clientPool
}
-func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID, version string) (cid.ID, error) {
+func createContainer(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID user.ID) (cid.ID, error) {
var policy netmap.PlacementPolicy
err := policy.DecodeString("REP 1")
require.NoError(t, err)
@@ -456,7 +512,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 {
@@ -474,5 +530,51 @@ 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 registerUser(t *testing.T, ctx context.Context, aioContainer testcontainers.Container, pathToWallet string) {
+ err := aioContainer.CopyFileToContainer(ctx, pathToWallet, "/usr/wallet.json", 644)
+ require.NoError(t, err)
+
+ _, err = aioContainer.Exec(ctx, []string{
+ "/usr/bin/frostfs-s3-authmate", "register-user",
+ "--wallet", "/usr/wallet.json",
+ "--rpc-endpoint", "http://localhost:30333",
+ "--contract-wallet", "/config/s3-gw-wallet.json"})
+ require.NoError(t, err)
+}
+
+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)
}
diff --git a/cmd/http-gw/main.go b/cmd/http-gw/main.go
index ea9fbd7..fdd148c 100644
--- a/cmd/http-gw/main.go
+++ b/cmd/http-gw/main.go
@@ -9,9 +9,8 @@ import (
func main() {
globalContext, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
v := settings()
- logger, atomicLevel := pickLogger(v)
- application := newApp(globalContext, WithLogger(logger, atomicLevel), WithConfig(v))
+ application := newApp(globalContext, v)
go application.Serve()
application.Wait()
}
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 0d97dcb..62ef83e 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"
@@ -15,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"
@@ -41,6 +43,8 @@ const (
defaultConnectTimeout = 10 * time.Second
defaultStreamTimeout = 10 * time.Second
+ defaultLoggerSamplerInterval = 1 * time.Second
+
defaultShutdownTimeout = 15 * time.Second
defaultPoolErrorThreshold uint32 = 100
@@ -53,6 +57,10 @@ const (
defaultReconnectInterval = time.Minute
+ defaultCORSMaxAge = 600 // seconds
+
+ defaultMultinetFallbackDelay = 300 * time.Millisecond
+
cfgServer = "server"
cfgTLSEnabled = "tls.enabled"
cfgTLSCertFile = "tls.cert_file"
@@ -60,6 +68,11 @@ const (
cfgReconnectInterval = "reconnect_interval"
+ cfgIndexPageEnabled = "index_page.enabled"
+ cfgIndexPageTemplatePath = "index_page.template_path"
+
+ cfgWorkerPoolSize = "worker_pool_size"
+
// Web.
cfgWebReadBufferSize = "web.read_buffer_size"
cfgWebWriteBufferSize = "web.write_buffer_size"
@@ -75,9 +88,11 @@ 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"
+ cfgTracingAttributes = "tracing.attributes"
// Pool config.
cfgConTimeout = "connect_timeout"
@@ -90,6 +105,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"
@@ -129,6 +149,24 @@ 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"
+
+ // Multinet.
+ cfgMultinetEnabled = "multinet.enabled"
+ cfgMultinetBalancer = "multinet.balancer"
+ cfgMultinetRestrict = "multinet.restrict"
+ cfgMultinetFallbackDelay = "multinet.fallback_delay"
+ cfgMultinetSubnets = "multinet.subnets"
+
+ // Feature.
+ cfgFeaturesEnableFilepathFallback = "features.enable_filepath_fallback"
+
// Command line args.
cmdHelp = "help"
cmdVersion = "version"
@@ -147,6 +185,11 @@ var ignore = map[string]struct{}{
cmdVersion: {},
}
+type Logger struct {
+ logger *zap.Logger
+ lvl zap.AtomicLevel
+}
+
func settings() *viper.Viper {
v := viper.New()
v.AutomaticEnv()
@@ -187,6 +230,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)
@@ -202,6 +249,7 @@ func settings() *viper.Viper {
v.SetDefault(cfgWebStreamRequestBody, true)
v.SetDefault(cfgWebMaxRequestBodySize, fasthttp.DefaultMaxRequestBodySize)
+ v.SetDefault(cfgWorkerPoolSize, 1000)
// upload header
v.SetDefault(cfgUploaderHeaderEnableDefaultTimestamp, false)
@@ -216,6 +264,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)
@@ -375,7 +426,11 @@ func mergeConfig(v *viper.Viper, fileName string) error {
return v.MergeConfig(cfgFile)
}
-func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
+type LoggerAppSettings interface {
+ DroppedLogsInc()
+}
+
+func pickLogger(v *viper.Viper, settings LoggerAppSettings) *Logger {
lvl, err := getLogLevel(v)
if err != nil {
panic(err)
@@ -385,9 +440,9 @@ func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
switch dest {
case destinationStdout:
- return newStdoutLogger(lvl)
+ return newStdoutLogger(v, lvl, settings)
case destinationJournald:
- return newJournaldLogger(lvl)
+ return newJournaldLogger(v, lvl, settings)
default:
panic(fmt.Sprintf("wrong destination for logger: %s", dest))
}
@@ -404,39 +459,60 @@ 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, settings LoggerAppSettings) *Logger {
+ 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 = applyZapCoreMiddlewares(consoleOutCore, v, settings)
+
+ return &Logger{
+ logger: zap.New(consoleOutCore, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel))),
+ lvl: level,
}
-
- 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)
+func newJournaldLogger(v *viper.Viper, lvl zapcore.Level, settings LoggerAppSettings) *Logger {
+ 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(),
})
- l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
+ coreWithContext = applyZapCoreMiddlewares(coreWithContext, v, settings)
- return l, c.Level
+ return &Logger{
+ logger: zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel))),
+ lvl: level,
+ }
+}
+
+func newLogEncoder() zapcore.Encoder {
+ c := zap.NewProductionEncoderConfig()
+ c.EncodeTime = zapcore.ISO8601TimeEncoder
+
+ return zapcore.NewConsoleEncoder(c)
+}
+
+func applyZapCoreMiddlewares(core zapcore.Core, v *viper.Viper, settings LoggerAppSettings) zapcore.Core {
+ if v.GetBool(cfgLoggerSamplingEnabled) {
+ core = zapcore.NewSamplerWithOptions(core,
+ v.GetDuration(cfgLoggerSamplingInterval),
+ v.GetInt(cfgLoggerSamplingInitial),
+ v.GetInt(cfgLoggerSamplingThereafter),
+ zapcore.SamplerHook(func(_ zapcore.Entry, dec zapcore.SamplingDecision) {
+ if dec&zapcore.LogDropped > 0 {
+ settings.DroppedLogsInc()
+ }
+ }))
+ }
+
+ return core
}
func getLogLevel(v *viper.Viper) (zapcore.Level, error) {
@@ -467,6 +543,46 @@ 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 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{})
@@ -495,7 +611,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))
@@ -551,18 +667,13 @@ 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()),
+ grpc.WithContextDialer(dialSource.GrpcContextDialer()),
}
- prm.SetGRPCDialOptions(apiGRPCDialOpts...)
- prmTree.SetGRPCDialOptions(treeGRPCDialOpts...)
+ prm.SetGRPCDialOptions(interceptors...)
+ prmTree.SetGRPCDialOptions(interceptors...)
p, err := pool.NewPool(prm)
if err != nil {
@@ -662,3 +773,58 @@ 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
+}
+
+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 05b83b3..2822357 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
@@ -99,6 +103,11 @@ 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_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
@@ -121,3 +130,35 @@ 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
+
+# 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
+
+# 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
+
+# Enable using fallback path to search for a object by attribute
+HTTP_GW_FEATURES_ENABLE_FILEPATH_FALLBACK=false
diff --git a/config/config.yaml b/config/config.yaml
index 7f8077b..6296bd9 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -9,14 +9,26 @@ 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.
destination: stdout
+ sampling:
+ enabled: false
+ initial: 100
+ thereafter: 100
+ interval: 1s
server:
- address: 0.0.0.0:8080
@@ -101,6 +113,14 @@ 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
+ template_path: internal/handler/templates/index.gotmpl
+
zip:
compression: false # Enable zip compression to download files by common prefix.
@@ -126,4 +146,33 @@ cache:
resolve_bucket:
namespace_header: X-Frostfs-Namespace
- default_namespaces: [ "", "root" ]
\ No newline at end of file
+ default_namespaces: [ "", "root" ]
+
+cors:
+ allow_origin: ""
+ allow_methods: []
+ allow_headers: []
+ 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
+
+features:
+ # Enable using fallback path to search for a object by attribute
+ enable_filepath_fallback: false
diff --git a/docs/api.md b/docs/api.md
index 78df766..e59956a 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -8,7 +8,7 @@
| `/zip/{cid}/{prefix}` | [Download objects in archive](#download-zip) |
**Note:** `cid` parameter can be base58 encoded container ID or container name
-(the name must be registered in NNS, see appropriate section in [README](../README.md#nns)).
+(the name must be registered in NNS, see appropriate section in [nns.md](./nns.md)).
Route parameters can be:
@@ -18,7 +18,7 @@ Route parameters can be:
### Bearer token
-All routes can accept [bearer token](../README.md#authentication) from:
+All routes can accept [bearer token](./authentication.md) from:
* `Authorization` header with `Bearer` type and base64-encoded token in
credentials field
@@ -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/authentication.md b/docs/authentication.md
new file mode 100644
index 0000000..d8bb235
--- /dev/null
+++ b/docs/authentication.md
@@ -0,0 +1,108 @@
+# Request authentication
+
+HTTP Gateway does not authorize requests. Gateway converts HTTP request to a
+FrostFS request and signs it with its own private key.
+
+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 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 and pass them from the client via gate down to FrostFS level
+to grant access.
+
+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
+* "Bearer" cookie with base64-encoded token contents
+
+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 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)
+
+1. Suppose you have a container with private policy for wallet key
+
+```
+$ frostfs-cli container create -r
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
@@ -256,13 +270,37 @@ tracing:
enabled: true
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. |
+| 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.
@@ -335,4 +373,100 @@ 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. 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:
+ 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. |
+
+# `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. |
+
+# `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. |
+
+# `features` section
+
+Contains parameters for enabling features.
+
+```yaml
+features:
+ enable_filepath_fallback: true
+```
+
+| Parameter | Type | SIGHUP reload | Default value | Description |
+| ----------------------------------- | ------ | ------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `features.enable_filepath_fallback` | `bool` | yes | `false` | Enable using fallback path to search for a object by attribute. If the value of the `FilePath` attribute in the request contains no `/` symbols or single leading `/` symbol and the object was not found, then an attempt is made to search for the object by the attribute `FileName`. |
diff --git a/docs/nns.md b/docs/nns.md
new file mode 100644
index 0000000..acb9f21
--- /dev/null
+++ b/docs/nns.md
@@ -0,0 +1,36 @@
+# Nicename Resolving with NNS
+
+Steps to start using name resolving:
+
+1. Enable NNS resolving in config (`rpc_endpoint` must be a valid neo rpc node, see [configs](./config) for other examples):
+
+```yaml
+rpc_endpoint: http://morph-chain.frostfs.devenv:30333
+resolve_order:
+ - nns
+```
+
+2. Make sure your container is registered in NNS contract. If you use [frostfs-dev-env](https://git.frostfs.info/TrueCloudLab/frostfs-dev-env)
+ you can check if your container (e.g. with `container-name` name) is registered in NNS:
+
+```shell
+$ curl -s --data '{"id":1,"jsonrpc":"2.0","method":"getcontractstate","params":[1]}' \
+ http://morph-chain.frostfs.devenv:30333 | jq -r '.result.hash'
+
+0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667
+
+$ docker exec -it morph_chain neo-go \
+ contract testinvokefunction \
+ -r http://morph-chain.frostfs.devenv:30333 0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667 \
+ resolve string:container-name.container int:16 \
+ | jq -r '.stack[0].value | if type=="array" then .[0].value else . end' \
+ | base64 -d && echo
+
+7f3vvkw4iTiS5ZZbu5BQXEmJtETWbi3uUjLNaSs29xrL
+```
+
+3. Use container name instead of its `$CID`. For example:
+
+```shell
+$ curl http://localhost:8082/get_by_attribute/container-name/FileName/object-name
+```
diff --git a/go.mod b/go.mod
index dd2896c..3dd27b8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,33 +1,37 @@
module git.frostfs.info/TrueCloudLab/frostfs-http-gw
-go 1.20
+go 1.22
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/frostfs-observability v0.0.0-20241112082307-f17779933e88
+ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241206094944-81c423e7094d
+ 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
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/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
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/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.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
+ 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-20240506185415-9bf2ced13842
+ golang.org/x/net v0.26.0
+ golang.org/x/sys v0.22.0
+ google.golang.org/grpc v1.66.2
)
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
@@ -35,53 +39,53 @@ 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/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v4 v4.2.1 // indirect
- github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/cenkalti/backoff/v4 v4.3.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.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
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.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/google/uuid v1.3.0 // 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/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/gorilla/websocket v1.5.1 // 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/josharian/intern 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/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
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,24 +97,23 @@ 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
- 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/atomic v1.10.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.9.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/crypto v0.24.0 // indirect
+ golang.org/x/sync v0.7.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-20230410155749-daa745c078e1 // indirect
- google.golang.org/protobuf v1.30.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 1bd67bc..7f43c86 100644
--- a/go.sum
+++ b/go.sum
@@ -37,18 +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.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-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-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-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-20241206094944-81c423e7094d h1:FpXI+mOrmJk3t2MKQFZuhLjCHDyDeo5rtP1WXl7gUWc=
+git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20241206094944-81c423e7094d/go.mod h1:eoK7+KZQ9GJxbzIs6vTnoUJqFDppavInLRHaN4MYgZg=
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=
@@ -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,22 @@ 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/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=
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/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=
-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,39 +125,27 @@ 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=
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.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/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=
@@ -187,12 +162,12 @@ 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=
+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=
@@ -241,6 +216,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=
@@ -302,8 +278,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=
@@ -314,13 +290,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=
@@ -360,20 +334,16 @@ 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/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=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -388,17 +358,13 @@ 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=
-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=
@@ -412,13 +378,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=
@@ -435,8 +399,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.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=
@@ -451,7 +413,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=
@@ -469,14 +430,9 @@ 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.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=
@@ -491,8 +447,8 @@ 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.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 +471,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=
@@ -530,8 +487,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=
@@ -539,23 +496,20 @@ 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=
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=
@@ -566,31 +520,24 @@ 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/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=
-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=
@@ -605,8 +552,8 @@ 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=
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=
@@ -621,23 +568,18 @@ 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.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=
@@ -646,6 +588,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=
@@ -663,74 +607,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=
@@ -761,12 +680,13 @@ 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=
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=
@@ -781,32 +701,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=
@@ -818,16 +730,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.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=
@@ -852,7 +762,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=
@@ -894,15 +803,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=
@@ -910,7 +817,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/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
+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=
@@ -926,8 +834,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=
@@ -946,17 +852,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=
@@ -967,43 +871,34 @@ 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/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.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=
@@ -1013,19 +908,16 @@ 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.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
-golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+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=
@@ -1036,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-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
-golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
+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=
@@ -1062,8 +954,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.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=
@@ -1111,15 +1003,12 @@ 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.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+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=
@@ -1129,9 +1018,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-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=
@@ -1143,17 +1029,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.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
-golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+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=
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=
@@ -1170,14 +1053,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=
@@ -1205,7 +1085,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=
@@ -1232,37 +1111,32 @@ 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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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-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.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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+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=
@@ -1272,7 +1146,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=
@@ -1294,7 +1167,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=
@@ -1325,7 +1197,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=
@@ -1335,11 +1206,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.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=
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=
@@ -1409,9 +1283,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-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
+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=
@@ -1435,10 +1310,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.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.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=
@@ -1452,10 +1325,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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY=
+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=
@@ -1464,6 +1335,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=
@@ -1476,6 +1348,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=
@@ -1485,6 +1358,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=
@@ -1536,6 +1410,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/data/bucket.go b/internal/data/info.go
similarity index 100%
rename from internal/data/bucket.go
rename to internal/data/info.go
diff --git a/internal/api/tree.go b/internal/data/tree.go
similarity index 61%
rename from internal/api/tree.go
rename to internal/data/tree.go
index 4d16cc7..fcf8add 100644
--- a/internal/api/tree.go
+++ b/internal/data/tree.go
@@ -1,4 +1,4 @@
-package api
+package data
import (
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@@ -7,11 +7,21 @@ import (
// NodeVersion represent node from tree service.
type NodeVersion struct {
BaseNodeVersion
- DeleteMarker bool
}
// BaseNodeVersion is minimal node info from tree service.
// Basically used for "system" object.
type BaseNodeVersion struct {
- OID oid.ID
+ ID uint64
+ OID oid.ID
+ IsDeleteMarker bool
+}
+
+type NodeInfo struct {
+ Meta []NodeMeta
+}
+
+type NodeMeta interface {
+ GetKey() string
+ GetValue() []byte
}
diff --git a/internal/frostfs/services/pool_wrapper.go b/internal/frostfs/services/pool_wrapper.go
deleted file mode 100644
index 039d575..0000000
--- a/internal/frostfs/services/pool_wrapper.go
+++ /dev/null
@@ -1,115 +0,0 @@
-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/internal/handler/browse.go b/internal/handler/browse.go
new file mode 100644
index 0000000..64ad1f5
--- /dev/null
+++ b/internal/handler/browse.go
@@ -0,0 +1,382 @@
+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"
+)
+
+const (
+ dateFormat = "02-01-2006 15:04"
+ attrOID = "OID"
+ attrCreated = "Created"
+ attrFileName = "FileName"
+ attrFilePath = "FilePath"
+ attrSize = "Size"
+ attrDeleteMarker = "IsDeleteMarker"
+)
+
+type (
+ BrowsePageData struct {
+ 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
+ IsDeleteMarker bool
+ }
+)
+
+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] == "",
+ IsDeleteMarker: attrs[attrDeleteMarker] == "true",
+ }
+}
+
+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 {
+ return time.Time{}, err
+ }
+
+ return time.UnixMilli(millis), nil
+}
+
+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(path string) string {
+ var res strings.Builder
+
+ 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()
+}
+
+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.Meta
+ 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)
+ if obj.IsDeleteMarker {
+ continue
+ }
+ 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", 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
+ }
+
+ 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 objects[i].IsDir
+ })
+
+ tmpl, err := template.New("index").Funcs(template.FuncMap{
+ "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{
+ 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 a7aee64..8766f0c 100644
--- a/internal/handler/download.go
+++ b/internal/handler/download.go
@@ -4,38 +4,66 @@ import (
"archive/zip"
"bufio"
"context"
+ "errors"
"fmt"
"io"
"net/http"
"net/url"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/layer"
"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"
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)
+ cidParam := c.UserValue("cid").(string)
+ oidParam := c.UserValue("oid").(string)
+ downloadParam := c.QueryArgs().GetBool("download")
+
+ ctx := utils.GetContextFromRequest(c)
+ log := utils.GetReqLogOrDefault(ctx, h.log).With(
+ zap.String("cid", cidParam),
+ zap.String("oid", oidParam),
+ )
+
+ bktInfo, err := h.getBucketInfo(ctx, cidParam, log)
if err != nil {
- h.byObjectName(c, h.receiveFile)
+ logAndSendBucketError(c, log, err)
+ return
+ }
+
+ checkS3Err := h.tree.CheckSettingsNodeExists(ctx, bktInfo)
+ if checkS3Err != nil && !errors.Is(checkS3Err, layer.ErrNodeNotFound) {
+ logAndSendBucketError(c, log, checkS3Err)
+ return
+ }
+
+ req := h.newRequest(c, log)
+
+ var objID oid.ID
+ if checkS3Err == nil && shouldDownload(oidParam, downloadParam) {
+ h.byS3Path(ctx, req, bktInfo.CID, oidParam, h.receiveFile)
+ } else if err = objID.DecodeString(oidParam); err == nil {
+ h.byNativeAddress(ctx, req, bktInfo.CID, objID, h.receiveFile)
} else {
- h.byAddress(c, h.receiveFile)
+ h.browseIndex(c, checkS3Err != nil)
}
}
-func (h *Handler) newRequest(ctx *fasthttp.RequestCtx, log *zap.Logger) *request {
- return &request{
+func shouldDownload(oidParam string, downloadParam bool) bool {
+ return !isDir(oidParam) || downloadParam
+}
+
+func (h *Handler) newRequest(ctx *fasthttp.RequestCtx, log *zap.Logger) request {
+ return request{
RequestCtx: ctx,
log: log,
}
@@ -46,19 +74,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) {
@@ -84,16 +113,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))
- response.Error(c, "could not unescape prefix: "+err.Error(), fasthttp.StatusBadRequest)
+ log.Error(logs.FailedToUnescapeQuery, zap.String("cid", scid), zap.String("prefix", prefix), zap.Error(err))
+ ResponseError(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 {
@@ -101,10 +131,10 @@ 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)
+ ResponseError(c, "could not search for objects: "+err.Error(), fasthttp.StatusBadRequest)
return
}
@@ -153,13 +183,14 @@ 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 := PrmObjectGet{
+ PrmAuth: PrmAuth{
+ BearerToken: btoken,
+ },
+ Address: addr,
}
- resGet, err := h.pool.GetObject(ctx, prm)
+ resGet, err := h.frostfs.GetObject(ctx, prm)
if err != nil {
return fmt.Errorf("get FrostFS object: %v", err)
}
diff --git a/internal/handler/frostfs_mock.go b/internal/handler/frostfs_mock.go
new file mode 100644
index 0000000..b60915e
--- /dev/null
+++ b/internal/handler/frostfs_mock.go
@@ -0,0 +1,275 @@
+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) retrieveObject(addr oid.Address, btoken *bearer.Token) (*object.Object, error) {
+ sAddr := addr.EncodeToString()
+
+ if obj, ok := t.objects[sAddr]; ok {
+ owner := t.requestOwner(btoken)
+
+ if !t.isAllowed(addr.Container(), owner, acl.OpObjectGet, addr.Object()) {
+ return nil, ErrAccessDenied
+ }
+
+ return obj, nil
+ }
+
+ 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 {
+ 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 (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() {
+ 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
+}
diff --git a/internal/handler/handler.go b/internal/handler/handler.go
index f88dff1..ed90163 100644
--- a/internal/handler/handler.go
+++ b/internal/handler/handler.go
@@ -11,18 +11,17 @@ 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/layer"
"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/panjf2000/ants/v2"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -31,163 +30,278 @@ type Config interface {
DefaultTimestamp() bool
ZipCompression() bool
ClientCut() bool
+ IndexPageEnabled() bool
+ IndexPageTemplate() string
BufferMaxSizeForPut() uint64
NamespaceHeader() string
+ EnableFilepathFallback() bool
+}
+
+// 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
+}
+
+// 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
+
+ // Address to read the object header from.
+ Address oid.Address
+
+ // Offset-length range of the object payload to be read.
+ PayloadRange [2]uint64
+}
+
+// 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.
+ 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 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
+ 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")
+ // ErrQuotaLimitReached is returned from FrostFS in case of quota exceeded.
+ ErrQuotaLimitReached = errors.New("quota limit reached")
+)
+
+// FrostFS represents virtual connection to FrostFS network.
+type FrostFS interface {
+ Container(context.Context, PrmContainer) (*container.Container, 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)
+ InitMultiObjectReader(ctx context.Context, p PrmInitMultiObjectReader) (io.Reader, 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
- tree *tree.Tree
+ containerResolver ContainerResolver
+ tree layer.TreeService
cache *cache.BucketCache
+ workerPool *ants.Pool
}
-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 layer.TreeService, workerPool *ants.Pool) *Handler {
return &Handler{
log: params.Logger,
- pool: params.Pool,
+ frostfs: params.FrostFS,
ownerID: params.Owner,
config: config,
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)) {
- var (
- idCnr, _ = c.UserValue("cid").(string)
- idObj, _ = c.UserValue("oid").(string)
- log = h.log.With(zap.String("cid", idCnr), zap.String("oid", idObj))
- )
+func (h *Handler) byNativeAddress(ctx context.Context, req request, cnrID cid.ID, objID oid.ID, handler func(context.Context, request, oid.Address)) {
+ addr := newAddress(cnrID, objID)
+ handler(ctx, req, addr)
+}
- ctx := utils.GetContextFromRequest(c)
+// byS3Path is a wrapper for function (e.g. request.headObject, request.receiveFile) that
+// resolves object address from S3-like path