Compare commits
No commits in common. "master" and "bugfix/wallet-from-config-in-preset" have entirely different histories.
master
...
bugfix/wal
67 changed files with 1915 additions and 3129 deletions
|
@ -1,21 +0,0 @@
|
||||||
name: DCO action
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
dco:
|
|
||||||
name: DCO
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
|
|
||||||
- name: Run commit format checker
|
|
||||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v2
|
|
||||||
with:
|
|
||||||
from: 'origin/${{ github.event.pull_request.base.ref }}'
|
|
|
@ -1,56 +0,0 @@
|
||||||
name: Tests and linters
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.23'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install linters
|
|
||||||
run: make lint-install
|
|
||||||
|
|
||||||
- name: Run linters
|
|
||||||
run: make lint
|
|
||||||
|
|
||||||
tests:
|
|
||||||
name: Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
go_versions: [ '1.22', '1.23' ]
|
|
||||||
fail-fast: false
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '${{ matrix.go_versions }}'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: make test
|
|
||||||
|
|
||||||
tests-race:
|
|
||||||
name: Tests with -race
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: go test ./... -count=1 -race
|
|
||||||
|
|
0
.forgejo/CODEOWNERS → .github/CODEOWNERS
vendored
0
.forgejo/CODEOWNERS → .github/CODEOWNERS
vendored
0
.forgejo/logo.svg → .github/logo.svg
vendored
0
.forgejo/logo.svg → .github/logo.svg
vendored
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
21
.github/workflows/dco.yml
vendored
Normal file
21
.github/workflows/dco.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: DCO check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
commits_check_job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Commits Check
|
||||||
|
steps:
|
||||||
|
- name: Get PR Commits
|
||||||
|
id: 'get-pr-commits'
|
||||||
|
uses: tim-actions/get-pr-commits@master
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: DCO Check
|
||||||
|
uses: tim-actions/dco@master
|
||||||
|
with:
|
||||||
|
commits: ${{ steps.get-pr-commits.outputs.commits }}
|
34
.github/workflows/go.yml
vendored
Normal file
34
.github/workflows/go.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths-ignore:
|
||||||
|
- '**/*.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: --timeout=2m
|
||||||
|
|
||||||
|
tests:
|
||||||
|
name: Tests
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go_versions: [ '1.17', '1.18', '1.19' ]
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,5 +2,3 @@ k6
|
||||||
*.bolt
|
*.bolt
|
||||||
presets
|
presets
|
||||||
bin
|
bin
|
||||||
# Preset script artifacts.
|
|
||||||
__pycache__
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
First, thank you for contributing! We love and encourage pull requests from
|
First, thank you for contributing! We love and encourage pull requests from
|
||||||
everyone. Please follow the guidelines:
|
everyone. Please follow the guidelines:
|
||||||
|
|
||||||
- Check the open [issues](https://git.frostfs.info/TrueCloudLab/xk6-frostfs/issues) and
|
- Check the open [issues](https://github.com/TrueCloudLab/xk6-frostfs/issues) and
|
||||||
[pull requests](https://git.frostfs.info/TrueCloudLab/xk6-frostfs/pulls) for existing
|
[pull requests](https://github.com/TrueCloudLab/xk6-frostfs/pulls) for existing
|
||||||
discussions.
|
discussions.
|
||||||
|
|
||||||
- Open an issue first, to discuss a new feature or enhancement.
|
- Open an issue first, to discuss a new feature or enhancement.
|
||||||
|
@ -27,20 +27,19 @@ Start by forking the `xk6-frostfs` repository, make changes in a branch and then
|
||||||
send a pull request. We encourage pull requests to discuss code changes. Here
|
send a pull request. We encourage pull requests to discuss code changes. Here
|
||||||
are the steps in details:
|
are the steps in details:
|
||||||
|
|
||||||
### Set up your repository
|
### Set up your GitHub Repository
|
||||||
|
Fork [xk6-frostfs upstream](https://github.com/TrueCloudLab/xk6-frostfs/fork) source
|
||||||
Fork [xk6-frostfs upstream](https://git.frostfs.info/TrueCloudLab/xk6-frostfs/fork) source
|
|
||||||
repository to your own personal repository. Copy the URL of your fork (you will
|
repository to your own personal repository. Copy the URL of your fork (you will
|
||||||
need it for the `git clone` command below).
|
need it for the `git clone` command below).
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ git clone https://git.frostfs.info/TrueCloudLab/xk6-frostfs
|
$ git clone https://github.com/TrueCloudLab/xk6-frostfs
|
||||||
```
|
```
|
||||||
|
|
||||||
### Set up git remote as ``upstream``
|
### Set up git remote as ``upstream``
|
||||||
```sh
|
```sh
|
||||||
$ cd xk6-frostfs
|
$ cd xk6-frostfs
|
||||||
$ git remote add upstream https://git.frostfs.info/TrueCloudLab/xk6-frostfs
|
$ git remote add upstream https://github.com/TrueCloudLab/xk6-frostfs
|
||||||
$ git fetch upstream
|
$ git fetch upstream
|
||||||
$ git merge upstream/master
|
$ git merge upstream/master
|
||||||
...
|
...
|
||||||
|
@ -90,7 +89,7 @@ $ git push origin feature/123-something_awesome
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create a Pull Request
|
### Create a Pull Request
|
||||||
Pull requests can be created via git.frostfs.info. Refer to [this
|
Pull requests can be created via GitHub. Refer to [this
|
||||||
document](https://help.github.com/articles/creating-a-pull-request/) for
|
document](https://help.github.com/articles/creating-a-pull-request/) for
|
||||||
detailed steps on how to create a pull request. After a Pull Request gets peer
|
detailed steps on how to create a pull request. After a Pull Request gets peer
|
||||||
reviewed and approved, it will be merged.
|
reviewed and approved, it will be merged.
|
||||||
|
|
26
Makefile
26
Makefile
|
@ -3,15 +3,10 @@
|
||||||
# Common variables
|
# Common variables
|
||||||
REPO ?= $(shell go list -m)
|
REPO ?= $(shell go list -m)
|
||||||
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
|
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
|
||||||
GO_VERSION ?= 1.22
|
GO_VERSION ?= 1.19
|
||||||
LINT_VERSION ?= 1.60.3
|
LINT_VERSION ?= 1.49.0
|
||||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.7
|
|
||||||
BINDIR = bin
|
BINDIR = bin
|
||||||
|
|
||||||
OUTPUT_LINT_DIR ?= $(abspath $(BINDIR))/linters
|
|
||||||
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
|
|
||||||
TMP_DIR := .cache
|
|
||||||
|
|
||||||
# Binaries to build
|
# Binaries to build
|
||||||
CMDS = $(addprefix frostfs-, $(notdir $(wildcard cmd/*)))
|
CMDS = $(addprefix frostfs-, $(notdir $(wildcard cmd/*)))
|
||||||
BINS = $(addprefix $(BINDIR)/, $(CMDS))
|
BINS = $(addprefix $(BINDIR)/, $(CMDS))
|
||||||
|
@ -69,22 +64,7 @@ format:
|
||||||
|
|
||||||
# Run linters
|
# Run linters
|
||||||
lint:
|
lint:
|
||||||
@if [ ! -d "$(LINT_DIR)" ]; then \
|
@golangci-lint --timeout=5m run
|
||||||
make lint-install; \
|
|
||||||
fi
|
|
||||||
$(LINT_DIR)/golangci-lint run --timeout=5m
|
|
||||||
|
|
||||||
# Install linters
|
|
||||||
lint-install:
|
|
||||||
@rm -rf $(OUTPUT_LINT_DIR)
|
|
||||||
@mkdir -p $(OUTPUT_LINT_DIR)
|
|
||||||
@mkdir -p $(TMP_DIR)
|
|
||||||
@rm -rf $(TMP_DIR)/linters
|
|
||||||
@git -c advice.detachedHead=false clone --branch v$(TRUECLOUDLAB_LINT_VERSION) https://git.frostfs.info/TrueCloudLab/linters.git $(TMP_DIR)/linters
|
|
||||||
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
|
||||||
@rm -rf $(TMP_DIR)/linters
|
|
||||||
@rmdir $(TMP_DIR) 2>/dev/null || true
|
|
||||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install -trimpath github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
|
||||||
|
|
||||||
# Run linters in Docker
|
# Run linters in Docker
|
||||||
docker/lint:
|
docker/lint:
|
||||||
|
|
32
README.md
32
README.md
|
@ -1,5 +1,5 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./.forgejo/logo.svg" width="500px" alt="FrostFS logo">
|
<img src="./.github/logo.svg" width="500px" alt="FrostFS logo">
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://go.k6.io/k6">k6</a> extension to test and benchmark FrostFS related protocols.
|
<a href="https://go.k6.io/k6">k6</a> extension to test and benchmark FrostFS related protocols.
|
||||||
|
@ -48,16 +48,15 @@ Create native client with `connect` method. Arguments:
|
||||||
- dial timeout in seconds (0 for the default value)
|
- dial timeout in seconds (0 for the default value)
|
||||||
- stream timeout in seconds (0 for the default value)
|
- stream timeout in seconds (0 for the default value)
|
||||||
- generate object header on the client side (for big object - split locally too)
|
- generate object header on the client side (for big object - split locally too)
|
||||||
- max size for generated object header on the client side (for big object - the size that the object is splitted into)
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import native from 'k6/x/frostfs/native';
|
import native from 'k6/x/frostfs/native';
|
||||||
const frostfs_cli = native.connect("s01.frostfs.devenv:8080", "", 0, 0, false, 0)
|
const frostfs_cli = native.connect("s01.frostfs.devenv:8080", "", 0, 0, false)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
- `putContainer(params)`. The `params` is a dictionary (e.g.
|
- `putContainer(params)`. The `params` is a dictionary (e.g.
|
||||||
`{placement_policy:'REP 3',name:'container-name',name_global_scope:'false'}`).
|
`{acl:'public-read-write',placement_policy:'REP 3',name:'container-name',name_global_scope:'false'}`).
|
||||||
Returns dictionary with `success`
|
Returns dictionary with `success`
|
||||||
boolean flag, `container_id` string, and `error` string.
|
boolean flag, `container_id` string, and `error` string.
|
||||||
- `setBufferSize(size)`. Sets internal buffer size for data upload and
|
- `setBufferSize(size)`. Sets internal buffer size for data upload and
|
||||||
|
@ -75,13 +74,12 @@ const frostfs_cli = native.connect("s01.frostfs.devenv:8080", "", 0, 0, false, 0
|
||||||
|
|
||||||
Create a local client with `connect` method. Arguments:
|
Create a local client with `connect` method. Arguments:
|
||||||
- local path to frostfs storage node configuration file
|
- local path to frostfs storage node configuration file
|
||||||
- local path to frostfs storage node configuration directory
|
|
||||||
- hex encoded private key (empty value produces random key)
|
- hex encoded private key (empty value produces random key)
|
||||||
- whether to use the debug logger (warning: very verbose)
|
- whether to use the debug logger (warning: very verbose)
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import local from 'k6/x/frostfs/local';
|
import local from 'k6/x/frostfs/local';
|
||||||
const local_client = local.connect("/path/to/config.yaml", "/path/to/config/dir", "", false)
|
const local_client = local.connect("/path/to/config.yaml", "", false)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
@ -125,7 +123,6 @@ const s3_cli = s3.connect("https://s3.frostfs.devenv:8080", {'no_verify_ssl': 't
|
||||||
|
|
||||||
Create local s3 client with `connect` method. Arguments:
|
Create local s3 client with `connect` method. Arguments:
|
||||||
- local path to frostfs storage node configuration file
|
- local path to frostfs storage node configuration file
|
||||||
- local path to frostfs storage node configuration directory
|
|
||||||
- parameter map with the following options:
|
- parameter map with the following options:
|
||||||
* `hex_key`: private key to use as a hexadecimal string. A random one is created if none is provided.
|
* `hex_key`: private key to use as a hexadecimal string. A random one is created if none is provided.
|
||||||
* `node_position`: position of this node in the node array if loading multiple nodes independently (default: 0).
|
* `node_position`: position of this node in the node array if loading multiple nodes independently (default: 0).
|
||||||
|
@ -138,7 +135,7 @@ Create local s3 client with `connect` method. Arguments:
|
||||||
import local from 'k6/x/frostfs/local';
|
import local from 'k6/x/frostfs/local';
|
||||||
const params = {'node_position': 1, 'node_count': 3}
|
const params = {'node_position': 1, 'node_count': 3}
|
||||||
const bucketMapping = {'mytestbucket': 'GBQDDUM1hdodXmiRHV57EUkFWJzuntsG8BG15wFSwam6'}
|
const bucketMapping = {'mytestbucket': 'GBQDDUM1hdodXmiRHV57EUkFWJzuntsG8BG15wFSwam6'}
|
||||||
const local_client = local.connect("/path/to/config.yaml", "/path/to/config/dir", params, bucketMapping)
|
const local_client = local.connect("/path/to/config.yaml", params, bucketMapping)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
@ -186,25 +183,6 @@ Flags:
|
||||||
-v, --version version for registry-exporter
|
-v, --version version for registry-exporter
|
||||||
```
|
```
|
||||||
|
|
||||||
## Import pregen into registry db
|
|
||||||
|
|
||||||
You can import pregenerated json files into registry bolt db. Use `frostfs-xk6-registry import`. Usage examples are in help:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ ./bin/frostfs-xk6-registry import -h
|
|
||||||
Import objects into registry from pregenerated files
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
xk6-registry import [flags]
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
xk6-registry import registry.bolt preset.json
|
|
||||||
xk6-registry import registry.bolt preset.json another_preset.json
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
-h, --help help for import
|
|
||||||
```
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
- [GNU General Public License v3.0](LICENSE)
|
- [GNU General Public License v3.0](LICENSE)
|
||||||
|
|
|
@ -78,7 +78,7 @@ func rootCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
objRegistry := registry.NewObjRegistry(cmd.Context(), args[0])
|
objRegistry := registry.NewObjRegistry(cmd.Context(), args[0])
|
||||||
objSelector := registry.NewObjSelector(objRegistry, 0, registry.SelectorAwaiting, ®istry.ObjFilter{
|
objSelector := registry.NewObjSelector(objRegistry, 0, ®istry.ObjFilter{
|
||||||
Status: status,
|
Status: status,
|
||||||
Age: age,
|
Age: age,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
package importer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PreGenObj struct {
|
|
||||||
Bucket string `json:"bucket"`
|
|
||||||
Object string `json:"object"`
|
|
||||||
Container string `json:"container"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PreGenerateInfo struct {
|
|
||||||
Buckets []string `json:"buckets"`
|
|
||||||
Containers []string `json:"containers"`
|
|
||||||
Objects []PreGenObj `json:"objects"`
|
|
||||||
ObjSize string `json:"obj_size"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImportJSONPreGen writes objects from pregenerated JSON file
|
|
||||||
// to the registry.
|
|
||||||
// Note that ImportJSONPreGen does not check if object already
|
|
||||||
// exists in the registry so in case of re-entry the registry
|
|
||||||
// will have two entities representing the same object.
|
|
||||||
func ImportJSONPreGen(o *registry.ObjRegistry, filename string) error {
|
|
||||||
f, err := os.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pregenInfo PreGenerateInfo
|
|
||||||
err = json.Unmarshal(f, &pregenInfo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddObject uses DB.Batch to combine concurrent Batch calls
|
|
||||||
// into a single Bolt transaction. DB.Batch is limited by
|
|
||||||
// DB.MaxBatchDelay which may affect perfomance.
|
|
||||||
for _, obj := range pregenInfo.Objects {
|
|
||||||
if obj.Bucket != "" {
|
|
||||||
err = o.AddObject("", "", obj.Bucket, obj.Object, "")
|
|
||||||
} else {
|
|
||||||
err = o.AddObject(obj.Container, obj.Object, "", "", "")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package importer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/registry"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Cmd represents the import command.
|
|
||||||
var Cmd = &cobra.Command{
|
|
||||||
Use: "import",
|
|
||||||
Short: "Import objects into registry",
|
|
||||||
Long: "Import objects into registry from pregenerated files",
|
|
||||||
Example: `xk6-registry import registry.bolt preset.json
|
|
||||||
xk6-registry import registry.bolt preset.json another_preset.json`,
|
|
||||||
RunE: runCmd,
|
|
||||||
Args: cobra.MinimumNArgs(2),
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCmd(cmd *cobra.Command, args []string) error {
|
|
||||||
objRegistry := registry.NewObjRegistry(cmd.Context(), args[0])
|
|
||||||
for i := 1; i < len(args); i++ {
|
|
||||||
if err := ImportJSONPreGen(objRegistry, args[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
|
||||||
|
|
||||||
if cmd, err := rootCmd.ExecuteContextC(ctx); err != nil {
|
|
||||||
cmd.PrintErrln("Error:", err.Error())
|
|
||||||
cmd.PrintErrf("Run '%v --help' for usage.\n", cmd.CommandPath())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/cmd/xk6-registry/importer"
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/version"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: "xk6-registry",
|
|
||||||
Version: version.Version,
|
|
||||||
Short: "Command Line Tool to work with Registry",
|
|
||||||
Long: `Registry provides tools to work with object registry for xk6.
|
|
||||||
It contains command for importing objects in registry from preset`,
|
|
||||||
SilenceErrors: true,
|
|
||||||
SilenceUsage: true,
|
|
||||||
Run: rootCmdRun,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
cobra.AddTemplateFunc("runtimeVersion", runtime.Version)
|
|
||||||
rootCmd.SetVersionTemplate(`FrostFS xk6-registry
|
|
||||||
{{printf "Version: %s" .Version }}
|
|
||||||
GoVersion: {{ runtimeVersion }}
|
|
||||||
`)
|
|
||||||
rootCmd.AddCommand(importer.Cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rootCmdRun(cmd *cobra.Command, _ []string) {
|
|
||||||
_ = cmd.Usage()
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ import local from 'k6/x/frostfs/local';
|
||||||
import { uuidv4 } from '../scenarios/libs/k6-utils-1.4.0.js';
|
import { uuidv4 } from '../scenarios/libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
const payload = open('../go.sum', 'b');
|
const payload = open('../go.sum', 'b');
|
||||||
const local_cli = local.connect("/path/to/config.yaml", "/path/to/config/dir", "", false)
|
const local_cli = local.connect("/path/to/config.yaml", "", false)
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
stages: [
|
stages: [
|
||||||
|
|
|
@ -3,16 +3,17 @@ import { fail } from "k6";
|
||||||
import { uuidv4 } from '../scenarios/libs/k6-utils-1.4.0.js';
|
import { uuidv4 } from '../scenarios/libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
const payload = open('../go.sum', 'b');
|
const payload = open('../go.sum', 'b');
|
||||||
const frostfs_cli = native.connect("s01.frostfs.devenv:8080", "1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb", 0, 0, false, 0)
|
const frostfs_cli = native.connect("s01.frostfs.devenv:8080", "1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb", 0, 0, false)
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
stages: [
|
stages: [
|
||||||
{ duration: '30s', target: 10 },
|
{duration: '30s', target: 10},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const params = {
|
const params = {
|
||||||
|
acl: 'public-read-write',
|
||||||
placement_policy: 'REP 3',
|
placement_policy: 'REP 3',
|
||||||
name: 'container-name',
|
name: 'container-name',
|
||||||
name_global_scope: 'false'
|
name_global_scope: 'false'
|
||||||
|
@ -23,7 +24,7 @@ export function setup() {
|
||||||
fail(res.error)
|
fail(res.error)
|
||||||
}
|
}
|
||||||
console.info("created container", res.container_id)
|
console.info("created container", res.container_id)
|
||||||
return { container_id: res.container_id }
|
return {container_id: res.container_id}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (data) {
|
export default function (data) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { uuidv4 } from '../scenarios/libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
const payload = open('../go.sum', 'b');
|
const payload = open('../go.sum', 'b');
|
||||||
const container = "AjSxSNNXbJUDPqqKYm1VbFVDGCakbpUNH8aGjPmGAH3B"
|
const container = "AjSxSNNXbJUDPqqKYm1VbFVDGCakbpUNH8aGjPmGAH3B"
|
||||||
const frostfs_cli = native.connect("s01.frostfs.devenv:8080", "", 0, 0, false, 0)
|
const frostfs_cli = native.connect("s01.frostfs.devenv:8080", "", 0, 0, false)
|
||||||
const frostfs_obj = frostfs_cli.onsite(container, payload)
|
const frostfs_obj = frostfs_cli.onsite(container, payload)
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
|
@ -14,11 +14,11 @@ export const options = {
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
let headers = {
|
let headers = {
|
||||||
'unique_header': uuidv4()
|
'unique_header': uuidv4()
|
||||||
}
|
}
|
||||||
let resp = frostfs_obj.put(headers)
|
let resp = frostfs_obj.put(headers)
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
frostfs_cli.get(container, resp.object_id)
|
frostfs_cli.get(container, resp.object_id)
|
||||||
} else {
|
} else {
|
||||||
console.log(resp.error)
|
console.log(resp.error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { uuidv4 } from '../scenarios/libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
const bucket = "testbucket"
|
const bucket = "testbucket"
|
||||||
const payload = open('../go.sum', 'b');
|
const payload = open('../go.sum', 'b');
|
||||||
const s3local_cli = s3local.connect("path/to/storage/config.yml", "path/to/storage/config/dir", {}, {
|
const s3local_cli = s3local.connect("path/to/storage/config.yml", {}, {
|
||||||
'testbucket': 'GBQDDUM1hdodXmiRHV57EUkFWJzuntsG8BG15wFSwam6',
|
'testbucket': 'GBQDDUM1hdodXmiRHV57EUkFWJzuntsG8BG15wFSwam6',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
123
go.mod
123
go.mod
|
@ -1,11 +1,11 @@
|
||||||
module git.frostfs.info/TrueCloudLab/xk6-frostfs
|
module git.frostfs.info/TrueCloudLab/xk6-frostfs
|
||||||
|
|
||||||
go 1.22
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-node v0.38.3-0.20240502170333-ec2873caa7c6
|
git.frostfs.info/TrueCloudLab/frostfs-node v0.22.2-0.20230704155826-b520a3049e6f
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-s3-gw v0.29.0-rc.1.0.20240422122918-034396d554ec
|
git.frostfs.info/TrueCloudLab/frostfs-s3-gw v0.27.0-rc.2
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240502080121-12ddefe07877
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230705125206-769f6eec0565
|
||||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
||||||
github.com/aws/aws-sdk-go-v2 v1.19.0
|
github.com/aws/aws-sdk-go-v2 v1.19.0
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.18.28
|
github.com/aws/aws-sdk-go-v2/config v1.18.28
|
||||||
|
@ -13,28 +13,25 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.37.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.37.0
|
||||||
github.com/dop251/goja v0.0.0-20230626124041-ba8a63e79201
|
github.com/dop251/goja v0.0.0-20230626124041-ba8a63e79201
|
||||||
github.com/go-loremipsum/loremipsum v1.1.3
|
github.com/go-loremipsum/loremipsum v1.1.3
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/nspcc-dev/neo-go v0.105.1
|
github.com/nspcc-dev/neo-go v0.101.2
|
||||||
github.com/panjf2000/ants/v2 v2.9.0
|
github.com/panjf2000/ants/v2 v2.8.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
go.etcd.io/bbolt v1.3.8
|
go.etcd.io/bbolt v1.3.7
|
||||||
go.k6.io/k6 v0.45.1
|
go.k6.io/k6 v0.45.1
|
||||||
go.uber.org/zap v1.26.0
|
go.uber.org/zap v1.24.0
|
||||||
golang.org/x/sys v0.18.0
|
golang.org/x/sys v0.10.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240427200446-67c6f305b21f // indirect
|
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230602142716-68021b910acb // indirect
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409115729-6eb492025bdd // indirect
|
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65 // indirect
|
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 // indirect
|
||||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect
|
git.frostfs.info/TrueCloudLab/hrw v1.2.1 // indirect
|
||||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240416071728-04a79f57ef1f // indirect
|
|
||||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
|
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
|
||||||
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02 // indirect
|
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.44.296 // indirect
|
github.com/aws/aws-sdk-go v1.44.296 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||||
|
@ -56,77 +53,77 @@ require (
|
||||||
github.com/bluele/gcache v0.0.2 // indirect
|
github.com/bluele/gcache v0.0.2 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||||
github.com/fatih/color v1.15.0 // indirect
|
github.com/fatih/color v1.15.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-pkgz/expirable-cache/v3 v3.0.0 // indirect
|
|
||||||
github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible // indirect
|
github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect
|
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru v0.6.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.4 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
|
github.com/minio/highwayhash v1.0.2 // indirect
|
||||||
github.com/minio/sio v0.3.1 // indirect
|
github.com/minio/sio v0.3.1 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd // indirect
|
github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd // indirect
|
||||||
github.com/nats-io/nats.go v1.32.0 // indirect
|
github.com/nats-io/jwt/v2 v2.4.1 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
github.com/nats-io/nats.go v1.27.1 // indirect
|
||||||
|
github.com/nats-io/nkeys v0.4.4 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240112074137-296698a162ae // indirect
|
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
github.com/onsi/gomega v1.20.2 // indirect
|
github.com/onsi/gomega v1.20.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.18.0 // indirect
|
github.com/prometheus/client_golang v1.16.0 // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.4.0 // indirect
|
||||||
github.com/prometheus/common v0.46.0 // indirect
|
github.com/prometheus/common v0.44.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.11.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
|
||||||
github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e // indirect
|
github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/spf13/afero v1.9.5 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/cast v1.5.1 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/spf13/viper v1.18.2 // indirect
|
github.com/spf13/viper v1.16.0 // indirect
|
||||||
github.com/ssgreg/journald v1.0.0 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
|
||||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
github.com/twmb/murmur3 v1.1.8 // indirect
|
||||||
go.opentelemetry.io/otel v1.22.0 // indirect
|
go.opentelemetry.io/otel v1.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.22.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.22.0 // indirect
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
|
go.opentelemetry.io/otel/metric v1.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.22.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
|
go.opentelemetry.io/otel/trace v1.16.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v0.20.0 // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
golang.org/x/crypto v0.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
||||||
golang.org/x/net v0.23.0 // indirect
|
golang.org/x/net v0.12.0 // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.11.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
|
||||||
google.golang.org/grpc v1.63.2 // indirect
|
google.golang.org/grpc v1.56.1 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
gopkg.in/guregu/null.v3 v3.5.0 // indirect
|
gopkg.in/guregu/null.v3 v3.5.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|
685
go.sum
685
go.sum
|
@ -1,29 +1,62 @@
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240427200446-67c6f305b21f h1:YyjsQNtrngQzIKOUtApXoi5r5pewatM+cXfpY19vZWo=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240427200446-67c6f305b21f/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409115729-6eb492025bdd h1:fujTUMMn0wnpEKNDWLejFL916EPuaYD1MdZpk1ZokU8=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409115729-6eb492025bdd/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||||
|
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||||
|
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230602142716-68021b910acb h1:gaq4wgk7b+i9+rkDa+goPkv5FzqYlMxEuCKdZ57mSc0=
|
||||||
|
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230602142716-68021b910acb/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-node v0.38.3-0.20240502170333-ec2873caa7c6 h1:EQpYWBFMFvBd234Jk94SQjNTCI+Ug5L8A2SactQu7Lw=
|
git.frostfs.info/TrueCloudLab/frostfs-node v0.22.2-0.20230704155826-b520a3049e6f h1:4Cs8iPGf9fJ/ozhUwwCEz9KkNEaFPRYWDm1+hz68Go4=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-node v0.38.3-0.20240502170333-ec2873caa7c6/go.mod h1:mzaiHBrmeLqmY0wysnzL2sxWG7a3iVTyrmpcxLkgebI=
|
git.frostfs.info/TrueCloudLab/frostfs-node v0.22.2-0.20230704155826-b520a3049e6f/go.mod h1:tmU4hNqMjhyKewfX6PpzIy7y/t30diQPUxR0X/11vik=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65 h1:PaZ8GpnUoXxUoNsc1qp36bT2u7FU+neU4Jn9cl8AWqI=
|
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-20231101111734-b3ad3335ff65/go.mod h1:6aAX80dvJ3r5fjN9CzzPglRptoiPgIC9KFGGsUA+1Hw=
|
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6/go.mod h1:W8Nn08/l6aQ7UlIbpF7FsQou7TVpcRD1ZT1KG4TrFhE=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-s3-gw v0.29.0-rc.1.0.20240422122918-034396d554ec h1:X9zTHGQyeE0xExhWSbn47PKEao8za58qR3RaV238bSU=
|
git.frostfs.info/TrueCloudLab/frostfs-s3-gw v0.27.0-rc.2 h1:C9Hya/NN8U7P45od221YCtL78zmIbHs9eq5AWlcNkJQ=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-s3-gw v0.29.0-rc.1.0.20240422122918-034396d554ec/go.mod h1:d6Kk1okMB/jNx6u90o9/xe+Ow1gkY1Ytg6WcyvgDYpM=
|
git.frostfs.info/TrueCloudLab/frostfs-s3-gw v0.27.0-rc.2/go.mod h1:sYR/v3/WzBDAR8dLewjTxaSNYnvQtMVj0ypSy1FuLRo=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240502080121-12ddefe07877 h1:kJgCZvKcWGOsTGRyFDf9hIzkjypR0UkGVK77vzJp1dw=
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230705125206-769f6eec0565 h1:wjXxt9dpkPNQQH3bj8N/Qn7vupiB1/UWFEg1EiJUJrg=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240502080121-12ddefe07877/go.mod h1:e7H9nNFpx1Tj3R20Zoxy0Vo6Srlb6zV5L7ZQXqg9rn4=
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230705125206-769f6eec0565/go.mod h1:w+s3ozlbFfTDFHhjX0A3Iif3BRtnTkwiACxFZD+Q0cQ=
|
||||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
|
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/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM=
|
||||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240416071728-04a79f57ef1f h1:hP1Q/MJvRsHSBIWXn48C+hVsRHfPWWLhdOg6IxjaWBs=
|
|
||||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240416071728-04a79f57ef1f/go.mod h1:H/AW85RtYxVTbcgwHW76DqXeKlsiCIOeNXHPqyDBrfQ=
|
|
||||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
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/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
|
||||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
|
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
|
||||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0/go.mod h1:dhY+oy274hV8wGvGL4MwwMpdL3GYvaX1a8GQZQHvlF8=
|
git.frostfs.info/TrueCloudLab/tzhash v1.8.0/go.mod h1:dhY+oy274hV8wGvGL4MwwMpdL3GYvaX1a8GQZQHvlF8=
|
||||||
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02 h1:HeY8n27VyPRQe49l/fzyVMkWEB2fsLJYKp64pwA7tz4=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02/go.mod h1:rQFJJdEOV7KbbMtQYR2lNfiZk+ONRDJSbMCTWxKt8Fw=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
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.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||||
github.com/aws/aws-sdk-go v1.44.296 h1:ALRZIIKI+6EBWDiWP4RHWmOtHZ7dywRzenL4NWgNI2A=
|
github.com/aws/aws-sdk-go v1.44.296 h1:ALRZIIKI+6EBWDiWP4RHWmOtHZ7dywRzenL4NWgNI2A=
|
||||||
|
@ -66,14 +99,14 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 h1:e5mnydVdCVWxP+5rPAGi2PYxC7u2
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3/go.mod h1:yVGZA1CPkmUhBdA039jXNJJG7/6t+G+EBWmFq23xqnY=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3/go.mod h1:yVGZA1CPkmUhBdA039jXNJJG7/6t+G+EBWmFq23xqnY=
|
||||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
||||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||||
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
|
||||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
|
||||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
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/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
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.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
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.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
@ -82,17 +115,15 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/consensys/gnark-crypto v0.12.2-0.20231222162921-eb75782795d2 h1:tYj5Ydh5D7Xg2R1tJnoG36Yta7NVB8C0vx36oPA3Bbw=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/consensys/gnark-crypto v0.12.2-0.20231222162921-eb75782795d2/go.mod h1:wKqwsieaKPThcFkHe0d0zMsbHEUWFmZcG7KBCse210o=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
|
@ -104,67 +135,114 @@ github.com/dop251/goja v0.0.0-20230626124041-ba8a63e79201 h1:+9NRIliCUhliHMCixEO
|
||||||
github.com/dop251/goja v0.0.0-20230626124041-ba8a63e79201/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
github.com/dop251/goja v0.0.0-20230626124041-ba8a63e79201/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||||
github.com/frankban/quicktest v1.14.6/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.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.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-loremipsum/loremipsum v1.1.3 h1:ZRhA0ZmJ49lGe5HhWeMONr+iGftWDsHfrYBl5ktDXso=
|
github.com/go-loremipsum/loremipsum v1.1.3 h1:ZRhA0ZmJ49lGe5HhWeMONr+iGftWDsHfrYBl5ktDXso=
|
||||||
github.com/go-loremipsum/loremipsum v1.1.3/go.mod h1:OJQjXdvwlG9hsyhmMQoT4HOm4DG4l62CYywebw0XBoo=
|
github.com/go-loremipsum/loremipsum v1.1.3/go.mod h1:OJQjXdvwlG9hsyhmMQoT4HOm4DG4l62CYywebw0XBoo=
|
||||||
github.com/go-pkgz/expirable-cache/v3 v3.0.0 h1:u3/gcu3sabLYiTCevoRKv+WzjIn5oo7P8XtiXBeRDLw=
|
|
||||||
github.com/go-pkgz/expirable-cache/v3 v3.0.0/go.mod h1:2OQiDyEGQalYecLWmXprm3maPXeVb5/6/X7yRPYTzec=
|
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||||
github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible h1:bopx7t9jyUNX1ebhr0G4gtQWmUOgwQRI0QsYhdYLgkU=
|
github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible h1:bopx7t9jyUNX1ebhr0G4gtQWmUOgwQRI0QsYhdYLgkU=
|
||||||
github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
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.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/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
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=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
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.5/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.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
||||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo=
|
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA=
|
||||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
||||||
|
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.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.4 h1:7GHuZcgid37q8o5i3QI9KMT4nCWQQ3Kx3Ov6bb9MfK0=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.4/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
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/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=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
@ -177,17 +255,16 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
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/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||||
github.com/klauspost/reedsolomon v1.12.1 h1:NhWgum1efX1x58daOBGCFWcxtEhOhXKKl1HAPQUp03Q=
|
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/klauspost/reedsolomon v1.12.1/go.mod h1:nEi5Kjb6QqtbofI6s+cbG/j1da11c96IBYBSnVGtuBs=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/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.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
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.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
@ -201,30 +278,32 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
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/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa h1:lx8ZnNPwjkXSzOROz0cg69RlErRXs+L3eDkggASWKLo=
|
github.com/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa h1:lx8ZnNPwjkXSzOROz0cg69RlErRXs+L3eDkggASWKLo=
|
||||||
github.com/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa/go.mod h1:fhpOYavp5g2K74XDl/ao2y4KvhqVtKlkg1e+0UaQv7I=
|
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||||
|
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||||
github.com/minio/sio v0.3.1 h1:d59r5RTHb1OsQaSl1EaTWurzMMDRLA5fgNmjzD4eVu4=
|
github.com/minio/sio v0.3.1 h1:d59r5RTHb1OsQaSl1EaTWurzMMDRLA5fgNmjzD4eVu4=
|
||||||
github.com/minio/sio v0.3.1/go.mod h1:S0ovgVgc+sTlQyhiXA1ppBLv7REM7TYi5yyq2qL/Y6o=
|
github.com/minio/sio v0.3.1/go.mod h1:S0ovgVgc+sTlQyhiXA1ppBLv7REM7TYi5yyq2qL/Y6o=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
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/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
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/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
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/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd h1:AC3N94irbx2kWGA8f/2Ks7EQl2LxKIRQYuT9IJDwgiI=
|
github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd h1:AC3N94irbx2kWGA8f/2Ks7EQl2LxKIRQYuT9IJDwgiI=
|
||||||
github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd/go.mod h1:9vRHVuLCjoFfE3GT06X0spdOAO+Zzo4AMjdIwUHBvAk=
|
github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd/go.mod h1:9vRHVuLCjoFfE3GT06X0spdOAO+Zzo4AMjdIwUHBvAk=
|
||||||
github.com/mstoykov/envconfig v1.4.1-0.20220114105314-765c6d8c76f1 h1:94EkGmhXrVUEal+uLwFUf4fMXPhZpM5tYxuIsxrCCbI=
|
github.com/mstoykov/envconfig v1.4.1-0.20220114105314-765c6d8c76f1 h1:94EkGmhXrVUEal+uLwFUf4fMXPhZpM5tYxuIsxrCCbI=
|
||||||
github.com/mstoykov/envconfig v1.4.1-0.20220114105314-765c6d8c76f1/go.mod h1:vk/d9jpexY2Z9Bb0uB4Ndesss1Sr0Z9ZiGUrg5o9VGk=
|
github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4=
|
||||||
github.com/nats-io/nats.go v1.32.0 h1:Bx9BZS+aXYlxW08k8Gd3yR2s73pV5XSoAQUyp1Kwvp0=
|
github.com/nats-io/jwt/v2 v2.4.1/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI=
|
||||||
github.com/nats-io/nats.go v1.32.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
github.com/nats-io/nats-server/v2 v2.7.4 h1:c+BZJ3rGzUKCBIM4IXO8uNT2u1vajGbD1kPA6wqCEaM=
|
||||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
github.com/nats-io/nats.go v1.27.1 h1:OuYnal9aKVSnOzLQIzf7554OXMCG7KbaTkCSBHRcSoo=
|
||||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
github.com/nats-io/nats.go v1.27.1/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
|
||||||
|
github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA=
|
||||||
|
github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240112074137-296698a162ae h1:UFgMXcZthqiCqCyr3dOAtGICJ10gM8q0mFHyLR0UPQU=
|
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-20240112074137-296698a162ae/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
|
github.com/nspcc-dev/neo-go v0.101.2 h1:E7sosxIoY0QN1IvAbkn7gjf+n8Qn2PWHTNM3kNr8Nwo=
|
||||||
github.com/nspcc-dev/neo-go v0.105.1 h1:r0b2yIwLBi+ARBKU94gHL9oTFEB/XMJ0YlS2HN9Qw34=
|
github.com/nspcc-dev/neo-go v0.101.2/go.mod h1:J4tspxWw7jknX06F+VSMsKvIiNpYGfVTb2IxVC005YU=
|
||||||
github.com/nspcc-dev/neo-go v0.105.1/go.mod h1:GNh0cRALV/cuj+/xg2ZHDsrFbqcInqG7jjhqsLEnlNc=
|
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
|
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/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
@ -232,210 +311,438 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
|
||||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
|
||||||
github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY=
|
github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY=
|
||||||
github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
||||||
github.com/panjf2000/ants/v2 v2.9.0 h1:SztCLkVxBRigbg+vt0S5QvF5vxAbxbKt09/YfAJ0tEo=
|
github.com/panjf2000/ants/v2 v2.8.0 h1:4p4gPabD6iNM9Y5NpMc0g0L15uXDmfn6jkW5KP+oiHQ=
|
||||||
github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
|
github.com/panjf2000/ants/v2 v2.8.0/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||||
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
|
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||||
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
|
github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
|
||||||
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=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
|
||||||
github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e h1:zWKUYT07mGmVBH+9UgnHXd/ekCK99C8EbDSAt5qsjXE=
|
github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e h1:zWKUYT07mGmVBH+9UgnHXd/ekCK99C8EbDSAt5qsjXE=
|
||||||
github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
||||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
||||||
github.com/ssgreg/journald v1.0.0 h1:0YmTDPJXxcWDPba12qNMdO6TxvfkFSYpFIJ31CwmLcU=
|
|
||||||
github.com/ssgreg/journald v1.0.0/go.mod h1:RUckwmTM8ghGWPslq2+ZBZzbb9/2KgjzYZ4JEP+oRt0=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
|
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
|
||||||
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
|
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/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||||
go.k6.io/k6 v0.45.1 h1:z+iVxE7Qze2Ka8tKvnjerOsoTuQb8e27Vqd1wcG2IFI=
|
go.k6.io/k6 v0.45.1 h1:z+iVxE7Qze2Ka8tKvnjerOsoTuQb8e27Vqd1wcG2IFI=
|
||||||
go.k6.io/k6 v0.45.1/go.mod h1:SBO/sqx6h/a0lJqEioMEpneb6zULogIyDmz+ufFqtIE=
|
go.k6.io/k6 v0.45.1/go.mod h1:SBO/sqx6h/a0lJqEioMEpneb6zULogIyDmz+ufFqtIE=
|
||||||
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.22.0 h1:zr8ymM5OWWjjiWRzwTfZ67c905+2TMHYp2lMJ52QTyM=
|
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.22.0/go.mod h1:sQs7FT2iLVJ+67vYngGJkPe1qr39IzaBzaj9IDNNY8k=
|
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
|
||||||
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
|
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0=
|
||||||
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
|
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0/go.mod h1:vLarbg68dH2Wa77g71zmKQqlQ8+8Rq3GRG31uc0WcWI=
|
||||||
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78=
|
||||||
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs=
|
||||||
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc=
|
||||||
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo=
|
||||||
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 h1:+XWJd3jf75RXJq29mxbuXhCXFDG3S3R4vBUeSI2P7tE=
|
||||||
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0/go.mod h1:hqgzBPTf4yONMFgdZvL/bK42R/iinTyVQtiWihs3SZc=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
|
||||||
|
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
|
||||||
|
go.opentelemetry.io/proto/otlp v0.20.0 h1:BLOA1cZBAGSbRiNuGCCKiFrCdYB7deeHDeD1SueyOfA=
|
||||||
|
go.opentelemetry.io/proto/otlp v0.20.0/go.mod h1:3QgjzPALBIv9pcknj2EXGPXjYPFdUh/RQfF8Lz3+Vnw=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
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/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
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=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
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-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||||
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||||
|
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=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
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=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
|
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=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190130150945-aca44879d564/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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
|
||||||
golang.org/x/text v0.3.0/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
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-20180917221912-90fa682c2a6e/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
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-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=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
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-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=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
|
||||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-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-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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
|
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||||
|
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
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-20230628200519-e449d1ea0e82 h1:Wdfp5Hc1bqGCWYZNrir4A1Jb+SmVaV2j1DL/pbMMTGI=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 h1:s5YSX+ZH5b5vS9rnpGymvIyMpLRJizowqDlOuyjXnTk=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
|
||||||
|
google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
@ -458,5 +765,13 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
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=
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (d *Datagen) Exports() modules.Exports {
|
||||||
return modules.Exports{Default: d}
|
return modules.Exports{Default: d}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Datagen) Generator(size int, typ string, streaming bool) *Generator {
|
func (d *Datagen) Generator(size int, typ string) *Generator {
|
||||||
g := NewGenerator(d.vu, size, strings.ToLower(typ), streaming)
|
g := NewGenerator(d.vu, size, strings.ToLower(typ))
|
||||||
return &g
|
return &g
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@ package datagen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
"github.com/go-loremipsum/loremipsum"
|
"github.com/go-loremipsum/loremipsum"
|
||||||
"go.k6.io/k6/js/modules"
|
"go.k6.io/k6/js/modules"
|
||||||
)
|
)
|
||||||
|
@ -26,9 +28,11 @@ type (
|
||||||
buf []byte
|
buf []byte
|
||||||
typ string
|
typ string
|
||||||
offset int
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
streaming bool
|
GenPayloadResponse struct {
|
||||||
seed *atomic.Int64
|
Payload goja.ArrayBuffer
|
||||||
|
Hash string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,7 +45,7 @@ var payloadTypes = []string{
|
||||||
"",
|
"",
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGenerator(vu modules.VU, size int, typ string, streaming bool) Generator {
|
func NewGenerator(vu modules.VU, size int, typ string) Generator {
|
||||||
if size <= 0 {
|
if size <= 0 {
|
||||||
panic("size should be positive")
|
panic("size should be positive")
|
||||||
}
|
}
|
||||||
|
@ -56,20 +60,17 @@ func NewGenerator(vu modules.VU, size int, typ string, streaming bool) Generator
|
||||||
if !found {
|
if !found {
|
||||||
vu.InitEnv().Logger.Info("Unknown payload type '%s', random will be used.", typ)
|
vu.InitEnv().Logger.Info("Unknown payload type '%s', random will be used.", typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
buf := make([]byte, size+TailSize)
|
||||||
g := Generator{
|
g := Generator{
|
||||||
vu: vu,
|
vu: vu,
|
||||||
size: size,
|
size: size,
|
||||||
|
rand: r,
|
||||||
|
buf: buf,
|
||||||
typ: typ,
|
typ: typ,
|
||||||
}
|
}
|
||||||
|
g.fillBuffer()
|
||||||
if streaming {
|
|
||||||
g.streaming = true
|
|
||||||
g.seed = new(atomic.Int64)
|
|
||||||
} else {
|
|
||||||
g.rand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
||||||
g.buf = make([]byte, size+TailSize)
|
|
||||||
g.fillBuffer()
|
|
||||||
}
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,17 +85,21 @@ func (g *Generator) fillBuffer() {
|
||||||
}
|
}
|
||||||
g.buf = b.Bytes()
|
g.buf = b.Bytes()
|
||||||
default:
|
default:
|
||||||
g.rand.Read(g.buf) // Per docs, err is always nil here
|
rand.Read(g.buf) // Per docs, err is always nil here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Generator) GenPayload() Payload {
|
func (g *Generator) GenPayload(calcHash bool) GenPayloadResponse {
|
||||||
if g.streaming {
|
data := g.nextSlice()
|
||||||
return NewStreamPayload(g.size, g.seed.Add(1), g.typ)
|
|
||||||
|
dataHash := ""
|
||||||
|
if calcHash {
|
||||||
|
hashBytes := sha256.Sum256(data)
|
||||||
|
dataHash = hex.EncodeToString(hashBytes[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
data := g.nextSlice()
|
payload := g.vu.Runtime().NewArrayBuffer(data)
|
||||||
return NewFixedPayload(data)
|
return GenPayloadResponse{Payload: payload, Hash: dataHash}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Generator) nextSlice() []byte {
|
func (g *Generator) nextSlice() []byte {
|
||||||
|
|
|
@ -16,25 +16,25 @@ func TestGenerator(t *testing.T) {
|
||||||
|
|
||||||
t.Run("fails on negative size", func(t *testing.T) {
|
t.Run("fails on negative size", func(t *testing.T) {
|
||||||
require.Panics(t, func() {
|
require.Panics(t, func() {
|
||||||
_ = NewGenerator(vu, -1, "", false)
|
_ = NewGenerator(vu, -1, "")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("fails on zero size", func(t *testing.T) {
|
t.Run("fails on zero size", func(t *testing.T) {
|
||||||
require.Panics(t, func() {
|
require.Panics(t, func() {
|
||||||
_ = NewGenerator(vu, 0, "", false)
|
_ = NewGenerator(vu, 0, "")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("creates slice of specified size", func(t *testing.T) {
|
t.Run("creates slice of specified size", func(t *testing.T) {
|
||||||
size := 10
|
size := 10
|
||||||
g := NewGenerator(vu, size, "", false)
|
g := NewGenerator(vu, size, "")
|
||||||
slice := g.nextSlice()
|
slice := g.nextSlice()
|
||||||
require.Len(t, slice, size)
|
require.Len(t, slice, size)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("creates a different slice on each call", func(t *testing.T) {
|
t.Run("creates a different slice on each call", func(t *testing.T) {
|
||||||
g := NewGenerator(vu, 1000, "", false)
|
g := NewGenerator(vu, 1000, "")
|
||||||
slice1 := g.nextSlice()
|
slice1 := g.nextSlice()
|
||||||
slice2 := g.nextSlice()
|
slice2 := g.nextSlice()
|
||||||
// Each slice should be unique (assuming that 1000 random bytes will never coincide
|
// Each slice should be unique (assuming that 1000 random bytes will never coincide
|
||||||
|
@ -43,7 +43,7 @@ func TestGenerator(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("keeps generating slices after consuming entire tail", func(t *testing.T) {
|
t.Run("keeps generating slices after consuming entire tail", func(t *testing.T) {
|
||||||
g := NewGenerator(vu, 1000, "", false)
|
g := NewGenerator(vu, 1000, "")
|
||||||
initialSlice := g.nextSlice()
|
initialSlice := g.nextSlice()
|
||||||
for i := 0; i < TailSize; i++ {
|
for i := 0; i < TailSize; i++ {
|
||||||
g.nextSlice()
|
g.nextSlice()
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
package datagen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/go-loremipsum/loremipsum"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Payload represents arbitrary data to be packed into S3 or native object.
|
|
||||||
// Implementations could be thread-unsafe.
|
|
||||||
type Payload interface {
|
|
||||||
// Reader returns io.Reader instance to read the payload.
|
|
||||||
// Must not be called twice.
|
|
||||||
Reader() io.Reader
|
|
||||||
// Bytes is a helper which reads all data from Reader() into slice.
|
|
||||||
// The sole purpose of this method is to simplify HTTP scenario,
|
|
||||||
// where all payload needs to be read and wrapped.
|
|
||||||
Bytes() []byte
|
|
||||||
// Size returns payload size, which is equal to the total amount of data
|
|
||||||
// that could be read from the Reader().
|
|
||||||
Size() int
|
|
||||||
// Hash returns payload sha256 hash. Must be called after all data is read from the reader.
|
|
||||||
Hash() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type bytesPayload struct {
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *bytesPayload) Reader() io.Reader {
|
|
||||||
return bytes.NewReader(p.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *bytesPayload) Size() int {
|
|
||||||
return len(p.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *bytesPayload) Hash() string {
|
|
||||||
h := sha256.Sum256(p.data[:])
|
|
||||||
return hex.EncodeToString(h[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *bytesPayload) Bytes() []byte {
|
|
||||||
return p.data
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFixedPayload(data []byte) Payload {
|
|
||||||
return &bytesPayload{data: data}
|
|
||||||
}
|
|
||||||
|
|
||||||
type randomPayload struct {
|
|
||||||
r io.Reader
|
|
||||||
s hash.Hash
|
|
||||||
h string
|
|
||||||
size int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStreamPayload(size int, seed int64, typ string) Payload {
|
|
||||||
var rr io.Reader
|
|
||||||
switch typ {
|
|
||||||
case "text":
|
|
||||||
rr = &textReader{li: loremipsum.NewWithSeed(seed)}
|
|
||||||
default:
|
|
||||||
rr = rand.New(rand.NewSource(seed))
|
|
||||||
}
|
|
||||||
|
|
||||||
lr := io.LimitReader(rr, int64(size))
|
|
||||||
// We need some buffering to write complete blocks in the TeeReader.
|
|
||||||
// Streaming payload read is expected to be used for big objects, thus 4k seems like a good choice.
|
|
||||||
br := bufio.NewReaderSize(lr, 4096)
|
|
||||||
s := sha256.New()
|
|
||||||
tr := io.TeeReader(br, s)
|
|
||||||
return &randomPayload{
|
|
||||||
r: tr,
|
|
||||||
s: s,
|
|
||||||
size: size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *randomPayload) Reader() io.Reader {
|
|
||||||
return p.r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *randomPayload) Size() int {
|
|
||||||
return p.size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *randomPayload) Hash() string {
|
|
||||||
if p.h == "" {
|
|
||||||
p.h = hex.EncodeToString(p.s.Sum(nil))
|
|
||||||
// Prevent possible misuse.
|
|
||||||
p.r = nil
|
|
||||||
p.s = nil
|
|
||||||
}
|
|
||||||
return p.h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *randomPayload) Bytes() []byte {
|
|
||||||
data, err := io.ReadAll(p.r)
|
|
||||||
if err != nil {
|
|
||||||
// We use only 2 readers, either `bytes.Reader` or `rand.Reader`.
|
|
||||||
// None of them returns errors, thus encountering an error is a fatal error.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
type textReader struct {
|
|
||||||
li *loremipsum.LoremIpsum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *textReader) Read(p []byte) (n int, err error) {
|
|
||||||
paragraph := r.li.Paragraph()
|
|
||||||
return copy(p, paragraph), nil
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package datagen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFixedPayload(t *testing.T) {
|
|
||||||
const size = 123
|
|
||||||
data := make([]byte, size)
|
|
||||||
_, err := rand.Read(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
p := NewFixedPayload(data)
|
|
||||||
require.Equal(t, size, p.Size())
|
|
||||||
|
|
||||||
actual, err := io.ReadAll(p.Reader())
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, data, actual)
|
|
||||||
|
|
||||||
h := sha256.Sum256(data)
|
|
||||||
require.Equal(t, hex.EncodeToString(h[:]), p.Hash())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStreamingPayload(t *testing.T) {
|
|
||||||
const size = 123
|
|
||||||
|
|
||||||
p := NewStreamPayload(size, 0, "")
|
|
||||||
require.Equal(t, size, p.Size())
|
|
||||||
|
|
||||||
actual, err := io.ReadAll(p.Reader())
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, size, len(actual))
|
|
||||||
require.Equal(t, sha256.Size*2, len(p.Hash()))
|
|
||||||
}
|
|
|
@ -5,15 +5,14 @@ import (
|
||||||
|
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/datagen"
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/local/rawclient"
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/local/rawclient"
|
||||||
|
"github.com/dop251/goja"
|
||||||
"go.k6.io/k6/js/modules"
|
"go.k6.io/k6/js/modules"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
vu modules.VU
|
vu modules.VU
|
||||||
rc *rawclient.RawClient
|
rc *rawclient.RawClient
|
||||||
l Limiter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -26,21 +25,13 @@ type (
|
||||||
Success bool
|
Success bool
|
||||||
ObjectID string
|
ObjectID string
|
||||||
Error string
|
Error string
|
||||||
Abort bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GetResponse SuccessOrErrorResponse
|
GetResponse SuccessOrErrorResponse
|
||||||
DeleteResponse SuccessOrErrorResponse
|
DeleteResponse SuccessOrErrorResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) Put(containerID string, headers map[string]string, payload datagen.Payload) PutResponse {
|
func (c *Client) Put(containerID string, headers map[string]string, payload goja.ArrayBuffer) PutResponse {
|
||||||
if c.l.IsFull() {
|
|
||||||
return PutResponse{
|
|
||||||
Success: false,
|
|
||||||
Error: "engine size limit reached",
|
|
||||||
Abort: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id, err := c.rc.Put(c.vu.Context(), mustParseContainerID(containerID), nil, headers, payload.Bytes())
|
id, err := c.rc.Put(c.vu.Context(), mustParseContainerID(containerID), nil, headers, payload.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PutResponse{Error: err.Error()}
|
return PutResponse{Error: err.Error()}
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
package local
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ Limiter = &noopLimiter{}
|
|
||||||
_ Limiter = &sizeLimiter{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Limiter interface {
|
|
||||||
engine.MetricRegister
|
|
||||||
IsFull() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLimiter(maxSizeGB int64) Limiter {
|
|
||||||
if maxSizeGB < 0 {
|
|
||||||
panic("max size is negative")
|
|
||||||
}
|
|
||||||
if maxSizeGB == 0 {
|
|
||||||
return &noopLimiter{}
|
|
||||||
}
|
|
||||||
return &sizeLimiter{
|
|
||||||
maxSize: maxSizeGB * 1024 * 1024 * 1024,
|
|
||||||
currentSize: &atomic.Int64{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type sizeLimiter struct {
|
|
||||||
maxSize int64
|
|
||||||
currentSize *atomic.Int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*sizeLimiter) AddMethodDuration(method string, d time.Duration) {}
|
|
||||||
func (*sizeLimiter) AddToContainerSize(cnrID string, size int64) {}
|
|
||||||
func (*sizeLimiter) AddToObjectCounter(shardID string, objectType string, delta int) {}
|
|
||||||
func (*sizeLimiter) ClearErrorCounter(shardID string) {}
|
|
||||||
func (*sizeLimiter) DeleteShardMetrics(shardID string) {}
|
|
||||||
func (*sizeLimiter) GC() metrics.GCMetrics { return &noopGCMetrics{} }
|
|
||||||
func (*sizeLimiter) IncErrorCounter(shardID string) {}
|
|
||||||
func (*sizeLimiter) SetMode(shardID string, mode mode.Mode) {}
|
|
||||||
func (*sizeLimiter) SetObjectCounter(shardID string, objectType string, v uint64) {}
|
|
||||||
func (*sizeLimiter) WriteCache() metrics.WriteCacheMetrics { return &noopWriteCacheMetrics{} }
|
|
||||||
func (*sizeLimiter) DeleteContainerSize(cnrID string) {}
|
|
||||||
func (*sizeLimiter) DeleteContainerCount(cnrID string) {}
|
|
||||||
func (*sizeLimiter) SetContainerObjectCounter(_, _, _ string, _ uint64) {}
|
|
||||||
func (*sizeLimiter) IncContainerObjectCounter(_, _, _ string) {}
|
|
||||||
func (*sizeLimiter) SubContainerObjectCounter(_, _, _ string, _ uint64) {}
|
|
||||||
func (*sizeLimiter) IncRefillObjectsCount(_, _ string, _ int, _ bool) {}
|
|
||||||
func (*sizeLimiter) SetRefillPercent(_, _ string, _ uint32) {}
|
|
||||||
func (*sizeLimiter) SetRefillStatus(_, _, _ string) {}
|
|
||||||
|
|
||||||
func (sl *sizeLimiter) AddToPayloadCounter(shardID string, size int64) {
|
|
||||||
sl.currentSize.Add(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sl *sizeLimiter) IsFull() bool {
|
|
||||||
cur := sl.currentSize.Load()
|
|
||||||
return cur > sl.maxSize
|
|
||||||
}
|
|
||||||
|
|
||||||
type noopLimiter struct{}
|
|
||||||
|
|
||||||
func (*noopLimiter) AddMethodDuration(method string, d time.Duration) {}
|
|
||||||
func (*noopLimiter) AddToContainerSize(cnrID string, size int64) {}
|
|
||||||
func (*noopLimiter) AddToObjectCounter(shardID string, objectType string, delta int) {}
|
|
||||||
func (*noopLimiter) AddToPayloadCounter(shardID string, size int64) {}
|
|
||||||
func (*noopLimiter) ClearErrorCounter(shardID string) {}
|
|
||||||
func (*noopLimiter) DeleteShardMetrics(shardID string) {}
|
|
||||||
func (*noopLimiter) GC() metrics.GCMetrics { return &noopGCMetrics{} }
|
|
||||||
func (*noopLimiter) IncErrorCounter(shardID string) {}
|
|
||||||
func (*noopLimiter) SetMode(shardID string, mode mode.Mode) {}
|
|
||||||
func (*noopLimiter) SetObjectCounter(shardID string, objectType string, v uint64) {}
|
|
||||||
func (*noopLimiter) WriteCache() metrics.WriteCacheMetrics { return &noopWriteCacheMetrics{} }
|
|
||||||
func (*noopLimiter) IsFull() bool { return false }
|
|
||||||
func (*noopLimiter) DeleteContainerSize(cnrID string) {}
|
|
||||||
func (*noopLimiter) DeleteContainerCount(cnrID string) {}
|
|
||||||
func (*noopLimiter) SetContainerObjectCounter(_, _, _ string, _ uint64) {}
|
|
||||||
func (*noopLimiter) IncContainerObjectCounter(_, _, _ string) {}
|
|
||||||
func (*noopLimiter) SubContainerObjectCounter(_, _, _ string, _ uint64) {}
|
|
||||||
func (*noopLimiter) IncRefillObjectsCount(_, _ string, _ int, _ bool) {}
|
|
||||||
func (*noopLimiter) SetRefillPercent(_, _ string, _ uint32) {}
|
|
||||||
func (*noopLimiter) SetRefillStatus(_, _, _ string) {}
|
|
||||||
|
|
||||||
type noopGCMetrics struct{}
|
|
||||||
|
|
||||||
func (*noopGCMetrics) AddDeletedCount(shardID string, deleted uint64, failed uint64) {}
|
|
||||||
func (*noopGCMetrics) AddExpiredObjectCollectionDuration(string, time.Duration, bool, string) {}
|
|
||||||
func (*noopGCMetrics) AddInhumedObjectCount(shardID string, count uint64, objectType string) {}
|
|
||||||
func (*noopGCMetrics) AddRunDuration(shardID string, d time.Duration, success bool) {}
|
|
||||||
|
|
||||||
type noopWriteCacheMetrics struct{}
|
|
||||||
|
|
||||||
func (*noopWriteCacheMetrics) AddMethodDuration(_, _, _, _ string, _ bool, _ time.Duration) {}
|
|
||||||
func (*noopWriteCacheMetrics) Close(_, _ string) {}
|
|
||||||
func (*noopWriteCacheMetrics) IncOperationCounter(_, _, _, _ string, _ metrics.NullBool) {}
|
|
||||||
func (*noopWriteCacheMetrics) SetActualCount(_, _, _ string, count uint64) {}
|
|
||||||
func (*noopWriteCacheMetrics) SetEstimateSize(_, _, _ string, _ uint64) {}
|
|
||||||
func (*noopWriteCacheMetrics) SetMode(shardID string, mode string) {}
|
|
|
@ -40,18 +40,15 @@ type RootModule struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
// configFile is the name of the configuration file used during one test.
|
// configFile is the name of the configuration file used during one test.
|
||||||
configFile string
|
configFile string
|
||||||
// configDir is the name of the configuration directory used during one test.
|
|
||||||
configDir string
|
|
||||||
// ng is the engine instance used during one test, corresponding to the configFile. Each VU
|
// ng is the engine instance used during one test, corresponding to the configFile. Each VU
|
||||||
// gets the same engine instance.
|
// gets the same engine instance.
|
||||||
ng *engine.StorageEngine
|
ng *engine.StorageEngine
|
||||||
l Limiter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local represents an instance of the module for every VU.
|
// Local represents an instance of the module for every VU.
|
||||||
type Local struct {
|
type Local struct {
|
||||||
vu modules.VU
|
vu modules.VU
|
||||||
ResolveEngine func(context.Context, string, string, bool, int64) (*engine.StorageEngine, Limiter, error)
|
ResolveEngine func(context.Context, string, bool) (*engine.StorageEngine, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the interfaces are implemented correctly.
|
// Ensure the interfaces are implemented correctly.
|
||||||
|
@ -59,9 +56,9 @@ var (
|
||||||
_ modules.Module = &RootModule{}
|
_ modules.Module = &RootModule{}
|
||||||
_ modules.Instance = &Local{}
|
_ modules.Instance = &Local{}
|
||||||
|
|
||||||
objPutSuccess, objPutFails, objPutDuration, objPutData *metrics.Metric
|
objPutTotal, objPutFails, objPutDuration *metrics.Metric
|
||||||
objGetSuccess, objGetFails, objGetDuration, objGetData *metrics.Metric
|
objGetTotal, objGetFails, objGetDuration *metrics.Metric
|
||||||
objDeleteSuccess, objDeleteFails, objDeleteDuration *metrics.Metric
|
objDeleteTotal, objDeleteFails, objDeleteDuration *metrics.Metric
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -74,7 +71,7 @@ func (r *RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
|
||||||
return NewLocalModuleInstance(vu, r.GetOrCreateEngine)
|
return NewLocalModuleInstance(vu, r.GetOrCreateEngine)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocalModuleInstance(vu modules.VU, resolveEngine func(context.Context, string, string, bool, int64) (*engine.StorageEngine, Limiter, error)) *Local {
|
func NewLocalModuleInstance(vu modules.VU, resolveEngine func(context.Context, string, bool) (*engine.StorageEngine, error)) *Local {
|
||||||
return &Local{
|
return &Local{
|
||||||
vu: vu,
|
vu: vu,
|
||||||
ResolveEngine: resolveEngine,
|
ResolveEngine: resolveEngine,
|
||||||
|
@ -103,53 +100,45 @@ func checkResourceLimits() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrCreateEngine returns the current engine instance for the given configuration file or directory,
|
// GetOrCreateEngine returns the current engine instance for the given configuration file,
|
||||||
// creating a new one if none exists. Note that the identity of configuration files is their
|
// creating a new one if none exists. Note that the identity of configuration files is their
|
||||||
// file name for the purposes of test runs.
|
// file name for the purposes of test runs.
|
||||||
func (r *RootModule) GetOrCreateEngine(ctx context.Context, configFile string, configDir string, debug bool, maxSizeGB int64) (*engine.StorageEngine, Limiter, error) {
|
func (r *RootModule) GetOrCreateEngine(ctx context.Context, configFile string, debug bool) (*engine.StorageEngine, error) {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
if len(configFile) == 0 && len(configDir) == 0 {
|
if len(configFile) == 0 {
|
||||||
return nil, nil, errors.New("provide configFile or configDir")
|
return nil, errors.New("configFile cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.l == nil {
|
|
||||||
r.l = NewLimiter(maxSizeGB)
|
|
||||||
}
|
|
||||||
// Create and initialize engine for the given configFile if it doesn't exist already
|
// Create and initialize engine for the given configFile if it doesn't exist already
|
||||||
if r.ng == nil {
|
if r.ng == nil {
|
||||||
r.configFile = configFile
|
r.configFile = configFile
|
||||||
r.configDir = configDir
|
appCfg := config.New(configFile, "", "")
|
||||||
appCfg := config.New(configFile, configDir, "")
|
ngOpts, shardOpts, err := storageEngineOptionsFromConfig(appCfg, debug)
|
||||||
ngOpts, shardOpts, err := storageEngineOptionsFromConfig(ctx, appCfg, debug, r.l)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("creating engine options from config: %v", err)
|
return nil, fmt.Errorf("creating engine options from config: %v", err)
|
||||||
}
|
}
|
||||||
if err := checkResourceLimits(); err != nil {
|
if err := checkResourceLimits(); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.ng = engine.New(ngOpts...)
|
r.ng = engine.New(ngOpts...)
|
||||||
for i, opts := range shardOpts {
|
for i, opts := range shardOpts {
|
||||||
if _, err := r.ng.AddShard(ctx, opts...); err != nil {
|
if _, err := r.ng.AddShard(opts...); err != nil {
|
||||||
return nil, nil, fmt.Errorf("adding shard %d: %v", i, err)
|
return nil, fmt.Errorf("adding shard %d: %v", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := r.ng.Open(ctx); err != nil {
|
if err := r.ng.Open(); err != nil {
|
||||||
return nil, nil, fmt.Errorf("opening engine: %v", err)
|
return nil, fmt.Errorf("opening engine: %v", err)
|
||||||
}
|
}
|
||||||
if err := r.ng.Init(ctx); err != nil {
|
if err := r.ng.Init(ctx); err != nil {
|
||||||
return nil, nil, fmt.Errorf("initializing engine: %v", err)
|
return nil, fmt.Errorf("initializing engine: %v", err)
|
||||||
}
|
}
|
||||||
} else if configFile != r.configFile {
|
} else if configFile != r.configFile {
|
||||||
return nil, nil, fmt.Errorf("GetOrCreateEngine called with mismatching configFile after engine was "+
|
return nil, fmt.Errorf("GetOrCreateEngine called with mismatching configFile after engine was initialized: got %q, want %q", configFile, r.configFile)
|
||||||
"initialized: got %q, want %q", configFile, r.configFile)
|
|
||||||
} else if configDir != r.configDir {
|
|
||||||
return nil, nil, fmt.Errorf("GetOrCreateEngine called with mismatching configDir after engine was "+
|
|
||||||
"initialized: got %q, want %q", configDir, r.configDir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.ng, r.l, nil
|
return r.ng, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exports implements the modules.Instance interface and returns the exports
|
// Exports implements the modules.Instance interface and returns the exports
|
||||||
|
@ -160,10 +149,10 @@ func (s *Local) Exports() modules.Exports {
|
||||||
|
|
||||||
func (s *Local) VU() modules.VU { return s.vu }
|
func (s *Local) VU() modules.VU { return s.vu }
|
||||||
|
|
||||||
func (s *Local) Connect(configFile, configDir, hexKey string, debug bool, maxSizeGB int64) (*Client, error) {
|
func (s *Local) Connect(configFile, hexKey string, debug bool) (*Client, error) {
|
||||||
ng, l, err := s.ResolveEngine(s.VU().Context(), configFile, configDir, debug, maxSizeGB)
|
ng, err := s.ResolveEngine(s.VU().Context(), configFile, debug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connecting to engine for config - file %q dir %q: %v", configFile, configDir, err)
|
return nil, fmt.Errorf("connecting to engine for config %q: %v", configFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := ParseOrCreateKey(hexKey)
|
key, err := ParseOrCreateKey(hexKey)
|
||||||
|
@ -172,19 +161,18 @@ func (s *Local) Connect(configFile, configDir, hexKey string, debug bool, maxSiz
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register metrics.
|
// Register metrics.
|
||||||
objPutSuccess, _ = stats.Registry.NewMetric("local_obj_put_success", metrics.Counter)
|
registry := metrics.NewRegistry()
|
||||||
objPutFails, _ = stats.Registry.NewMetric("local_obj_put_fails", metrics.Counter)
|
objPutTotal, _ = registry.NewMetric("local_obj_put_total", metrics.Counter)
|
||||||
objPutDuration, _ = stats.Registry.NewMetric("local_obj_put_duration", metrics.Trend, metrics.Time)
|
objPutFails, _ = registry.NewMetric("local_obj_put_fails", metrics.Counter)
|
||||||
objPutData, _ = stats.Registry.NewMetric("local_obj_put_bytes", metrics.Counter, metrics.Data)
|
objPutDuration, _ = registry.NewMetric("local_obj_put_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
objGetSuccess, _ = stats.Registry.NewMetric("local_obj_get_success", metrics.Counter)
|
objGetTotal, _ = registry.NewMetric("local_obj_get_total", metrics.Counter)
|
||||||
objGetFails, _ = stats.Registry.NewMetric("local_obj_get_fails", metrics.Counter)
|
objGetFails, _ = registry.NewMetric("local_obj_get_fails", metrics.Counter)
|
||||||
objGetDuration, _ = stats.Registry.NewMetric("local_obj_get_duration", metrics.Trend, metrics.Time)
|
objGetDuration, _ = registry.NewMetric("local_obj_get_duration", metrics.Trend, metrics.Time)
|
||||||
objGetData, _ = stats.Registry.NewMetric("local_obj_get_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
|
||||||
objDeleteSuccess, _ = stats.Registry.NewMetric("local_obj_delete_success", metrics.Counter)
|
objDeleteTotal, _ = registry.NewMetric("local_obj_delete_total", metrics.Counter)
|
||||||
objDeleteFails, _ = stats.Registry.NewMetric("local_obj_delete_fails", metrics.Counter)
|
objDeleteFails, _ = registry.NewMetric("local_obj_delete_fails", metrics.Counter)
|
||||||
objDeleteDuration, _ = stats.Registry.NewMetric("local_obj_delete_duration", metrics.Trend, metrics.Time)
|
objDeleteDuration, _ = registry.NewMetric("local_obj_delete_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
// Create raw client backed by local storage engine.
|
// Create raw client backed by local storage engine.
|
||||||
rc := rawclient.New(ng,
|
rc := rawclient.New(ng,
|
||||||
|
@ -193,32 +181,30 @@ func (s *Local) Connect(configFile, configDir, hexKey string, debug bool, maxSiz
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stats.Report(s.vu, objPutFails, 1)
|
stats.Report(s.vu, objPutFails, 1)
|
||||||
} else {
|
} else {
|
||||||
stats.Report(s.vu, objPutSuccess, 1)
|
stats.Report(s.vu, objPutTotal, 1)
|
||||||
stats.ReportDataSent(s.vu, float64(sz))
|
stats.ReportDataSent(s.vu, float64(sz))
|
||||||
stats.Report(s.vu, objPutDuration, metrics.D(dt))
|
stats.Report(s.vu, objPutDuration, metrics.D(dt))
|
||||||
stats.Report(s.vu, objPutData, float64(sz))
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
rawclient.WithGetHandler(func(sz uint64, err error, dt time.Duration) {
|
rawclient.WithGetHandler(func(sz uint64, err error, dt time.Duration) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stats.Report(s.vu, objGetFails, 1)
|
stats.Report(s.vu, objGetFails, 1)
|
||||||
} else {
|
} else {
|
||||||
stats.Report(s.vu, objGetSuccess, 1)
|
stats.Report(s.vu, objGetTotal, 1)
|
||||||
stats.Report(s.vu, objGetDuration, metrics.D(dt))
|
stats.Report(s.vu, objGetDuration, metrics.D(dt))
|
||||||
stats.ReportDataReceived(s.vu, float64(sz))
|
stats.ReportDataReceived(s.vu, float64(sz))
|
||||||
stats.Report(s.vu, objGetData, float64(sz))
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
rawclient.WithDeleteHandler(func(err error, dt time.Duration) {
|
rawclient.WithDeleteHandler(func(err error, dt time.Duration) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stats.Report(s.vu, objDeleteFails, 1)
|
stats.Report(s.vu, objDeleteFails, 1)
|
||||||
} else {
|
} else {
|
||||||
stats.Report(s.vu, objDeleteSuccess, 1)
|
stats.Report(s.vu, objDeleteTotal, 1)
|
||||||
stats.Report(s.vu, objDeleteDuration, metrics.D(dt))
|
stats.Report(s.vu, objDeleteDuration, metrics.D(dt))
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
return &Client{vu: s.vu, rc: rc, l: l}, nil
|
return &Client{vu: s.vu, rc: rc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type epochState struct{}
|
type epochState struct{}
|
||||||
|
@ -231,7 +217,7 @@ func (epochState) CurrentEpoch() uint64 { return 0 }
|
||||||
// preloaded the storage (if any), by using the same configuration file.
|
// preloaded the storage (if any), by using the same configuration file.
|
||||||
//
|
//
|
||||||
// Note that the configuration file only needs to contain the storage-specific sections.
|
// Note that the configuration file only needs to contain the storage-specific sections.
|
||||||
func storageEngineOptionsFromConfig(ctx context.Context, c *config.Config, debug bool, l Limiter) ([]engine.Option, [][]shard.Option, error) {
|
func storageEngineOptionsFromConfig(c *config.Config, debug bool) ([]engine.Option, [][]shard.Option, error) {
|
||||||
log := zap.L()
|
log := zap.L()
|
||||||
if debug {
|
if debug {
|
||||||
var err error
|
var err error
|
||||||
|
@ -245,12 +231,11 @@ func storageEngineOptionsFromConfig(ctx context.Context, c *config.Config, debug
|
||||||
engine.WithErrorThreshold(engineconfig.ShardErrorThreshold(c)),
|
engine.WithErrorThreshold(engineconfig.ShardErrorThreshold(c)),
|
||||||
engine.WithShardPoolSize(engineconfig.ShardPoolSize(c)),
|
engine.WithShardPoolSize(engineconfig.ShardPoolSize(c)),
|
||||||
engine.WithLogger(&logger.Logger{Logger: log}),
|
engine.WithLogger(&logger.Logger{Logger: log}),
|
||||||
engine.WithMetrics(l),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var shOpts [][]shard.Option
|
var shOpts [][]shard.Option
|
||||||
|
|
||||||
err := engineconfig.IterateShards(c, false, func(sc *shardconfig.Config) error {
|
engineconfig.IterateShards(c, false, func(sc *shardconfig.Config) error {
|
||||||
opts := []shard.Option{
|
opts := []shard.Option{
|
||||||
shard.WithRefillMetabase(sc.RefillMetabase()),
|
shard.WithRefillMetabase(sc.RefillMetabase()),
|
||||||
shard.WithMode(sc.Mode()),
|
shard.WithMode(sc.Mode()),
|
||||||
|
@ -266,7 +251,6 @@ func storageEngineOptionsFromConfig(ctx context.Context, c *config.Config, debug
|
||||||
cfg := blobovniczaconfig.From((*config.Config)(scfg))
|
cfg := blobovniczaconfig.From((*config.Config)(scfg))
|
||||||
ss := blobstor.SubStorage{
|
ss := blobstor.SubStorage{
|
||||||
Storage: blobovniczatree.NewBlobovniczaTree(
|
Storage: blobovniczatree.NewBlobovniczaTree(
|
||||||
ctx,
|
|
||||||
blobovniczatree.WithRootPath(scfg.Path()),
|
blobovniczatree.WithRootPath(scfg.Path()),
|
||||||
blobovniczatree.WithPermissions(scfg.Perm()),
|
blobovniczatree.WithPermissions(scfg.Perm()),
|
||||||
blobovniczatree.WithBlobovniczaSize(cfg.Size()),
|
blobovniczatree.WithBlobovniczaSize(cfg.Size()),
|
||||||
|
@ -311,17 +295,15 @@ func storageEngineOptionsFromConfig(ctx context.Context, c *config.Config, debug
|
||||||
opts = append(opts,
|
opts = append(opts,
|
||||||
shard.WithWriteCache(true),
|
shard.WithWriteCache(true),
|
||||||
shard.WithWriteCacheOptions(
|
shard.WithWriteCacheOptions(
|
||||||
[]writecache.Option{
|
writecache.WithPath(wc.Path()),
|
||||||
writecache.WithPath(wc.Path()),
|
writecache.WithMaxBatchSize(wc.BoltDB().MaxBatchSize()),
|
||||||
writecache.WithMaxBatchSize(wc.BoltDB().MaxBatchSize()),
|
writecache.WithMaxBatchDelay(wc.BoltDB().MaxBatchDelay()),
|
||||||
writecache.WithMaxBatchDelay(wc.BoltDB().MaxBatchDelay()),
|
writecache.WithMaxObjectSize(wc.MaxObjectSize()),
|
||||||
writecache.WithMaxObjectSize(wc.MaxObjectSize()),
|
writecache.WithSmallObjectSize(wc.SmallObjectSize()),
|
||||||
writecache.WithSmallObjectSize(wc.SmallObjectSize()),
|
writecache.WithFlushWorkersCount(wc.WorkersNumber()),
|
||||||
writecache.WithFlushWorkersCount(wc.WorkerCount()),
|
writecache.WithMaxCacheSize(wc.SizeLimit()),
|
||||||
writecache.WithMaxCacheSize(wc.SizeLimit()),
|
writecache.WithNoSync(wc.NoSync()),
|
||||||
writecache.WithNoSync(wc.NoSync()),
|
writecache.WithLogger(&logger.Logger{Logger: log}),
|
||||||
writecache.WithLogger(&logger.Logger{Logger: log}),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -375,9 +357,7 @@ func storageEngineOptionsFromConfig(ctx context.Context, c *config.Config, debug
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("iterate shards: %w", err)
|
|
||||||
}
|
|
||||||
return ngOpts, shOpts, nil
|
return ngOpts, shOpts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (c *RawClient) Put(ctx context.Context, containerID cid.ID, ownerID *user.I
|
||||||
|
|
||||||
obj := object.New()
|
obj := object.New()
|
||||||
obj.SetContainerID(containerID)
|
obj.SetContainerID(containerID)
|
||||||
obj.SetOwnerID(*ownerID)
|
obj.SetOwnerID(ownerID)
|
||||||
obj.SetAttributes(attrs...)
|
obj.SetAttributes(attrs...)
|
||||||
obj.SetPayload(payload)
|
obj.SetPayload(payload)
|
||||||
obj.SetPayloadSize(uint64(sz))
|
obj.SetPayloadSize(uint64(sz))
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
package native
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const networkCacheTTL = time.Minute
|
|
||||||
|
|
||||||
var networkInfoCache = &networkInfoCacheT{}
|
|
||||||
|
|
||||||
type networkInfoCacheT struct {
|
|
||||||
guard sync.RWMutex
|
|
||||||
current *netmap.NetworkInfo
|
|
||||||
fetchTS time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *networkInfoCacheT) getOrFetch(ctx context.Context, cli *client.Client) (*netmap.NetworkInfo, error) {
|
|
||||||
if v := c.get(); v != nil {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
return c.fetch(ctx, cli)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *networkInfoCacheT) get() *netmap.NetworkInfo {
|
|
||||||
c.guard.RLock()
|
|
||||||
defer c.guard.RUnlock()
|
|
||||||
|
|
||||||
if c.current == nil || time.Since(c.fetchTS) > networkCacheTTL {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.current
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *networkInfoCacheT) fetch(ctx context.Context, cli *client.Client) (*netmap.NetworkInfo, error) {
|
|
||||||
c.guard.Lock()
|
|
||||||
defer c.guard.Unlock()
|
|
||||||
|
|
||||||
if time.Since(c.fetchTS) <= networkCacheTTL {
|
|
||||||
return c.current, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := cli.NetworkInfo(ctx, client.PrmNetworkInfo{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
v := res.Info()
|
|
||||||
c.current = &v
|
|
||||||
c.fetchTS = time.Now()
|
|
||||||
|
|
||||||
return c.current, nil
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"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"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
@ -21,8 +23,8 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||||
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
"git.frostfs.info/TrueCloudLab/tzhash/tz"
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/datagen"
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
||||||
|
"github.com/dop251/goja"
|
||||||
"go.k6.io/k6/js/modules"
|
"go.k6.io/k6/js/modules"
|
||||||
"go.k6.io/k6/metrics"
|
"go.k6.io/k6/metrics"
|
||||||
)
|
)
|
||||||
|
@ -34,7 +36,6 @@ type (
|
||||||
tok session.Object
|
tok session.Object
|
||||||
cli *client.Client
|
cli *client.Client
|
||||||
prepareLocally bool
|
prepareLocally bool
|
||||||
maxObjSize uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PutResponse struct {
|
PutResponse struct {
|
||||||
|
@ -71,13 +72,12 @@ type (
|
||||||
hdr object.Object
|
hdr object.Object
|
||||||
payload []byte
|
payload []byte
|
||||||
prepareLocally bool
|
prepareLocally bool
|
||||||
maxObjSize uint64
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultBufferSize = 64 * 1024
|
const defaultBufferSize = 64 * 1024
|
||||||
|
|
||||||
func (c *Client) Put(containerID string, headers map[string]string, payload datagen.Payload, chunkSize int) PutResponse {
|
func (c *Client) Put(containerID string, headers map[string]string, payload goja.ArrayBuffer, chunkSize int) PutResponse {
|
||||||
cliContainerID := parseContainerID(containerID)
|
cliContainerID := parseContainerID(containerID)
|
||||||
|
|
||||||
tok := c.tok
|
tok := c.tok
|
||||||
|
@ -101,10 +101,10 @@ func (c *Client) Put(containerID string, headers map[string]string, payload data
|
||||||
|
|
||||||
var o object.Object
|
var o object.Object
|
||||||
o.SetContainerID(cliContainerID)
|
o.SetContainerID(cliContainerID)
|
||||||
o.SetOwnerID(owner)
|
o.SetOwnerID(&owner)
|
||||||
o.SetAttributes(attrs...)
|
o.SetAttributes(attrs...)
|
||||||
|
|
||||||
resp, err := put(c.vu, c.cli, c.prepareLocally, &tok, &o, payload, chunkSize, c.maxObjSize)
|
resp, err := put(c.vu, c.cli, c.prepareLocally, &tok, &o, payload.Bytes(), chunkSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PutResponse{Success: false, Error: err.Error()}
|
return PutResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
@ -128,9 +128,9 @@ func (c *Client) Delete(containerID string, objectID string) DeleteResponse {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
var prm client.PrmObjectDelete
|
var prm client.PrmObjectDelete
|
||||||
prm.ObjectID = &cliObjectID
|
prm.ByID(cliObjectID)
|
||||||
prm.ContainerID = &cliContainerID
|
prm.FromContainer(cliContainerID)
|
||||||
prm.Session = &tok
|
prm.WithinSession(tok)
|
||||||
|
|
||||||
_, err = c.cli.ObjectDelete(c.vu.Context(), prm)
|
_, err = c.cli.ObjectDelete(c.vu.Context(), prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -138,7 +138,7 @@ func (c *Client) Delete(containerID string, objectID string) DeleteResponse {
|
||||||
return DeleteResponse{Success: false, Error: err.Error()}
|
return DeleteResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objDeleteSuccess, 1)
|
stats.Report(c.vu, objDeleteTotal, 1)
|
||||||
stats.Report(c.vu, objDeleteDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objDeleteDuration, metrics.D(time.Since(start)))
|
||||||
return DeleteResponse{Success: true}
|
return DeleteResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
@ -159,11 +159,11 @@ func (c *Client) Get(containerID, objectID string) GetResponse {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
var prm client.PrmObjectGet
|
var prm client.PrmObjectGet
|
||||||
prm.ObjectID = &cliObjectID
|
prm.ByID(cliObjectID)
|
||||||
prm.ContainerID = &cliContainerID
|
prm.FromContainer(cliContainerID)
|
||||||
prm.Session = &tok
|
prm.WithinSession(tok)
|
||||||
|
|
||||||
objSize := 0
|
var objSize = 0
|
||||||
err = get(c.cli, prm, c.vu.Context(), func(data []byte) {
|
err = get(c.cli, prm, c.vu.Context(), func(data []byte) {
|
||||||
objSize += len(data)
|
objSize += len(data)
|
||||||
})
|
})
|
||||||
|
@ -172,10 +172,9 @@ func (c *Client) Get(containerID, objectID string) GetResponse {
|
||||||
return GetResponse{Success: false, Error: err.Error()}
|
return GetResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objGetSuccess, 1)
|
stats.Report(c.vu, objGetTotal, 1)
|
||||||
stats.Report(c.vu, objGetDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objGetDuration, metrics.D(time.Since(start)))
|
||||||
stats.ReportDataReceived(c.vu, float64(objSize))
|
stats.ReportDataReceived(c.vu, float64(objSize))
|
||||||
stats.Report(c.vu, objGetData, float64(objSize))
|
|
||||||
return GetResponse{Success: true}
|
return GetResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +184,7 @@ func get(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
onDataChunk func(chunk []byte),
|
onDataChunk func(chunk []byte),
|
||||||
) error {
|
) error {
|
||||||
buf := make([]byte, defaultBufferSize)
|
var buf = make([]byte, defaultBufferSize)
|
||||||
|
|
||||||
objectReader, err := cli.ObjectGetInit(ctx, prm)
|
objectReader, err := cli.ObjectGetInit(ctx, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -228,9 +227,9 @@ func (c *Client) VerifyHash(containerID, objectID, expectedHash string) VerifyHa
|
||||||
}
|
}
|
||||||
|
|
||||||
var prm client.PrmObjectGet
|
var prm client.PrmObjectGet
|
||||||
prm.ObjectID = &cliObjectID
|
prm.ByID(cliObjectID)
|
||||||
prm.ContainerID = &cliContainerID
|
prm.FromContainer(cliContainerID)
|
||||||
prm.Session = &tok
|
prm.WithinSession(tok)
|
||||||
|
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
err = get(c.cli, prm, c.vu.Context(), func(data []byte) {
|
err = get(c.cli, prm, c.vu.Context(), func(data []byte) {
|
||||||
|
@ -241,7 +240,7 @@ func (c *Client) VerifyHash(containerID, objectID, expectedHash string) VerifyHa
|
||||||
}
|
}
|
||||||
actualHash := hex.EncodeToString(hasher.Sum(nil))
|
actualHash := hex.EncodeToString(hasher.Sum(nil))
|
||||||
if actualHash != expectedHash {
|
if actualHash != expectedHash {
|
||||||
return VerifyHashResponse{Success: false, Error: "hash mismatch"}
|
return VerifyHashResponse{Success: true, Error: "hash mismatch"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return VerifyHashResponse{Success: true}
|
return VerifyHashResponse{Success: true}
|
||||||
|
@ -264,6 +263,16 @@ func (c *Client) PutContainer(params map[string]string) PutContainerResponse {
|
||||||
container.SetCreationTime(&cnr, time.Now())
|
container.SetCreationTime(&cnr, time.Now())
|
||||||
cnr.SetOwner(usr)
|
cnr.SetOwner(usr)
|
||||||
|
|
||||||
|
if basicACLStr, ok := params["acl"]; ok {
|
||||||
|
var basicACL acl.Basic
|
||||||
|
err := basicACL.DecodeString(basicACLStr)
|
||||||
|
if err != nil {
|
||||||
|
return c.putCnrErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cnr.SetBasicACL(basicACL)
|
||||||
|
}
|
||||||
|
|
||||||
placementPolicyStr, ok := params["placement_policy"]
|
placementPolicyStr, ok := params["placement_policy"]
|
||||||
if ok {
|
if ok {
|
||||||
var placementPolicy netmap.PlacementPolicy
|
var placementPolicy netmap.PlacementPolicy
|
||||||
|
@ -300,9 +309,10 @@ func (c *Client) PutContainer(params map[string]string) PutContainerResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err := c.cli.ContainerPut(c.vu.Context(), client.PrmContainerPut{
|
var prm client.PrmContainerPut
|
||||||
Container: &cnr,
|
prm.SetContainer(cnr)
|
||||||
})
|
|
||||||
|
res, err := c.cli.ContainerPut(c.vu.Context(), prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.putCnrErrorResponse(err)
|
return c.putCnrErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
@ -318,7 +328,7 @@ func (c *Client) PutContainer(params map[string]string) PutContainerResponse {
|
||||||
return PutContainerResponse{Success: true, ContainerID: res.ID().EncodeToString()}
|
return PutContainerResponse{Success: true, ContainerID: res.ID().EncodeToString()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Onsite(containerID string, payload datagen.Payload) PreparedObject {
|
func (c *Client) Onsite(containerID string, payload goja.ArrayBuffer) PreparedObject {
|
||||||
maxObjectSize, epoch, hhDisabled, err := parseNetworkInfo(c.vu.Context(), c.cli)
|
maxObjectSize, epoch, hhDisabled, err := parseNetworkInfo(c.vu.Context(), c.cli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -345,7 +355,7 @@ func (c *Client) Onsite(containerID string, payload datagen.Payload) PreparedObj
|
||||||
obj.SetVersion(&apiVersion)
|
obj.SetVersion(&apiVersion)
|
||||||
obj.SetType(object.TypeRegular)
|
obj.SetType(object.TypeRegular)
|
||||||
obj.SetContainerID(cliContainerID)
|
obj.SetContainerID(cliContainerID)
|
||||||
obj.SetOwnerID(owner)
|
obj.SetOwnerID(&owner)
|
||||||
obj.SetPayloadSize(uint64(ln))
|
obj.SetPayloadSize(uint64(ln))
|
||||||
obj.SetCreationEpoch(epoch)
|
obj.SetCreationEpoch(epoch)
|
||||||
|
|
||||||
|
@ -364,7 +374,6 @@ func (c *Client) Onsite(containerID string, payload datagen.Payload) PreparedObj
|
||||||
hdr: *obj,
|
hdr: *obj,
|
||||||
payload: data,
|
payload: data,
|
||||||
prepareLocally: c.prepareLocally,
|
prepareLocally: c.prepareLocally,
|
||||||
maxObjSize: c.maxObjSize,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +399,7 @@ func (p PreparedObject) Put(headers map[string]string) PutResponse {
|
||||||
return PutResponse{Success: false, Error: err.Error()}
|
return PutResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = put(p.vu, p.cli, p.prepareLocally, nil, &obj, datagen.NewFixedPayload(p.payload), 0, p.maxObjSize)
|
_, err = put(p.vu, p.cli, p.prepareLocally, nil, &obj, p.payload, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PutResponse{Success: false, Error: err.Error()}
|
return PutResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
@ -405,37 +414,33 @@ func (s epochSource) CurrentEpoch() uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func put(vu modules.VU, cli *client.Client, prepareLocally bool, tok *session.Object,
|
func put(vu modules.VU, cli *client.Client, prepareLocally bool, tok *session.Object,
|
||||||
hdr *object.Object, payload datagen.Payload, chunkSize int, maxObjSize uint64,
|
hdr *object.Object, payload []byte, chunkSize int) (*client.ResObjectPut, error) {
|
||||||
) (*client.ResObjectPut, error) {
|
|
||||||
bufSize := defaultBufferSize
|
bufSize := defaultBufferSize
|
||||||
if chunkSize > 0 {
|
if chunkSize > 0 {
|
||||||
bufSize = chunkSize
|
bufSize = chunkSize
|
||||||
}
|
}
|
||||||
buf := make([]byte, bufSize)
|
buf := make([]byte, bufSize)
|
||||||
rdr := payload.Reader()
|
rdr := bytes.NewReader(payload)
|
||||||
sz := payload.Size()
|
sz := rdr.Size()
|
||||||
|
|
||||||
// starting upload
|
// starting upload
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
var prm client.PrmObjectPutInit
|
var prm client.PrmObjectPutInit
|
||||||
if tok != nil {
|
if tok != nil {
|
||||||
prm.Session = tok
|
prm.WithinSession(*tok)
|
||||||
}
|
}
|
||||||
if chunkSize > 0 {
|
if chunkSize > 0 {
|
||||||
prm.MaxChunkLength = chunkSize
|
prm.SetGRPCPayloadChunkLen(chunkSize)
|
||||||
}
|
}
|
||||||
if prepareLocally {
|
if prepareLocally {
|
||||||
ni, err := networkInfoCache.getOrFetch(vu.Context(), cli)
|
res, err := cli.NetworkInfo(vu.Context(), client.PrmNetworkInfo{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
prm.MaxSize = ni.MaxObjectSize()
|
prm.WithObjectMaxSize(res.Info().MaxObjectSize())
|
||||||
prm.EpochSource = epochSource(ni.CurrentEpoch())
|
prm.WithEpochSource(epochSource(res.Info().CurrentEpoch()))
|
||||||
prm.WithoutHomomorphHash = true
|
prm.WithoutHomomorphicHash(true)
|
||||||
if maxObjSize > 0 {
|
|
||||||
prm.MaxSize = maxObjSize
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
objectWriter, err := cli.ObjectPutInit(vu.Context(), prm)
|
objectWriter, err := cli.ObjectPutInit(vu.Context(), prm)
|
||||||
|
@ -464,10 +469,9 @@ func put(vu modules.VU, cli *client.Client, prepareLocally bool, tok *session.Ob
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(vu, objPutSuccess, 1)
|
stats.Report(vu, objPutTotal, 1)
|
||||||
stats.ReportDataSent(vu, float64(sz))
|
stats.ReportDataSent(vu, float64(sz))
|
||||||
stats.Report(vu, objPutDuration, metrics.D(time.Since(start)))
|
stats.Report(vu, objPutDuration, metrics.D(time.Since(start)))
|
||||||
stats.Report(vu, objPutData, float64(sz))
|
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
@ -495,9 +499,10 @@ func (x *waitParams) setDefaults() {
|
||||||
|
|
||||||
func (c *Client) waitForContainerPresence(ctx context.Context, cnrID cid.ID, wp *waitParams) error {
|
func (c *Client) waitForContainerPresence(ctx context.Context, cnrID cid.ID, wp *waitParams) error {
|
||||||
return waitFor(ctx, wp, func(ctx context.Context) bool {
|
return waitFor(ctx, wp, func(ctx context.Context) bool {
|
||||||
_, err := c.cli.ContainerGet(ctx, client.PrmContainerGet{
|
var prm client.PrmContainerGet
|
||||||
ContainerID: &cnrID,
|
prm.SetContainer(cnrID)
|
||||||
})
|
|
||||||
|
_, err := c.cli.ContainerGet(ctx, prm)
|
||||||
return err == nil
|
return err == nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"go.k6.io/k6/js/modules"
|
"go.k6.io/k6/js/modules"
|
||||||
|
@ -29,10 +28,10 @@ var (
|
||||||
_ modules.Instance = &Native{}
|
_ modules.Instance = &Native{}
|
||||||
_ modules.Module = &RootModule{}
|
_ modules.Module = &RootModule{}
|
||||||
|
|
||||||
objPutSuccess, objPutFails, objPutDuration, objPutData *metrics.Metric
|
objPutTotal, objPutFails, objPutDuration *metrics.Metric
|
||||||
objGetSuccess, objGetFails, objGetDuration, objGetData *metrics.Metric
|
objGetTotal, objGetFails, objGetDuration *metrics.Metric
|
||||||
objDeleteSuccess, objDeleteFails, objDeleteDuration *metrics.Metric
|
objDeleteTotal, objDeleteFails, objDeleteDuration *metrics.Metric
|
||||||
cnrPutTotal, cnrPutFails, cnrPutDuration *metrics.Metric
|
cnrPutTotal, cnrPutFails, cnrPutDuration *metrics.Metric
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -52,17 +51,13 @@ func (n *Native) Exports() modules.Exports {
|
||||||
return modules.Exports{Default: n}
|
return modules.Exports{Default: n}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Native) Connect(endpoint, hexPrivateKey string, dialTimeout, streamTimeout int, prepareLocally bool, maxObjSize int) (*Client, error) {
|
func (n *Native) Connect(endpoint, hexPrivateKey string, dialTimeout, streamTimeout int, prepareLocally bool) (*Client, error) {
|
||||||
var (
|
var (
|
||||||
cli client.Client
|
cli client.Client
|
||||||
pk *keys.PrivateKey
|
pk *keys.PrivateKey
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if maxObjSize < 0 {
|
|
||||||
return nil, fmt.Errorf("max object size value must be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
pk, err = keys.NewPrivateKey()
|
pk, err = keys.NewPrivateKey()
|
||||||
if len(hexPrivateKey) != 0 {
|
if len(hexPrivateKey) != 0 {
|
||||||
pk, err = keys.NewPrivateKeyFromHex(hexPrivateKey)
|
pk, err = keys.NewPrivateKeyFromHex(hexPrivateKey)
|
||||||
|
@ -72,18 +67,19 @@ func (n *Native) Connect(endpoint, hexPrivateKey string, dialTimeout, streamTime
|
||||||
}
|
}
|
||||||
|
|
||||||
var prmInit client.PrmInit
|
var prmInit client.PrmInit
|
||||||
prmInit.Key = pk.PrivateKey
|
prmInit.ResolveFrostFSFailures()
|
||||||
|
prmInit.SetDefaultPrivateKey(pk.PrivateKey)
|
||||||
cli.Init(prmInit)
|
cli.Init(prmInit)
|
||||||
|
|
||||||
var prmDial client.PrmDial
|
var prmDial client.PrmDial
|
||||||
prmDial.Endpoint = endpoint
|
prmDial.SetServerURI(endpoint)
|
||||||
|
|
||||||
if dialTimeout > 0 {
|
if dialTimeout > 0 {
|
||||||
prmDial.DialTimeout = time.Duration(dialTimeout) * time.Second
|
prmDial.SetTimeout(time.Duration(dialTimeout) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
if streamTimeout > 0 {
|
if streamTimeout > 0 {
|
||||||
prmDial.StreamTimeout = time.Duration(streamTimeout) * time.Second
|
prmDial.SetStreamTimeout(time.Duration(streamTimeout) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cli.Dial(n.vu.Context(), prmDial)
|
err = cli.Dial(n.vu.Context(), prmDial)
|
||||||
|
@ -93,9 +89,9 @@ func (n *Native) Connect(endpoint, hexPrivateKey string, dialTimeout, streamTime
|
||||||
|
|
||||||
// generate session token
|
// generate session token
|
||||||
exp := uint64(math.MaxUint64)
|
exp := uint64(math.MaxUint64)
|
||||||
sessionResp, err := cli.SessionCreate(n.vu.Context(), client.PrmSessionCreate{
|
var prmSessionCreate client.PrmSessionCreate
|
||||||
Expiration: exp,
|
prmSessionCreate.SetExp(exp)
|
||||||
})
|
sessionResp, err := cli.SessionCreate(n.vu.Context(), prmSessionCreate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("dial endpoint: %s %w", endpoint, err)
|
return nil, fmt.Errorf("dial endpoint: %s %w", endpoint, err)
|
||||||
}
|
}
|
||||||
|
@ -118,35 +114,23 @@ func (n *Native) Connect(endpoint, hexPrivateKey string, dialTimeout, streamTime
|
||||||
tok.SetAuthKey(&key)
|
tok.SetAuthKey(&key)
|
||||||
tok.SetExp(exp)
|
tok.SetExp(exp)
|
||||||
|
|
||||||
if prepareLocally && maxObjSize > 0 {
|
|
||||||
res, err := cli.NetworkInfo(n.vu.Context(), client.PrmNetworkInfo{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if uint64(maxObjSize) > res.Info().MaxObjectSize() {
|
|
||||||
return nil, fmt.Errorf("max object size must be not greater than %d bytes", res.Info().MaxObjectSize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// register metrics
|
// register metrics
|
||||||
|
registry := metrics.NewRegistry()
|
||||||
|
objPutTotal, _ = registry.NewMetric("frostfs_obj_put_total", metrics.Counter)
|
||||||
|
objPutFails, _ = registry.NewMetric("frostfs_obj_put_fails", metrics.Counter)
|
||||||
|
objPutDuration, _ = registry.NewMetric("frostfs_obj_put_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
objPutSuccess, _ = stats.Registry.NewMetric("frostfs_obj_put_success", metrics.Counter)
|
objGetTotal, _ = registry.NewMetric("frostfs_obj_get_total", metrics.Counter)
|
||||||
objPutFails, _ = stats.Registry.NewMetric("frostfs_obj_put_fails", metrics.Counter)
|
objGetFails, _ = registry.NewMetric("frostfs_obj_get_fails", metrics.Counter)
|
||||||
objPutDuration, _ = stats.Registry.NewMetric("frostfs_obj_put_duration", metrics.Trend, metrics.Time)
|
objGetDuration, _ = registry.NewMetric("frostfs_obj_get_duration", metrics.Trend, metrics.Time)
|
||||||
objPutData, _ = stats.Registry.NewMetric("frostfs_obj_put_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
|
||||||
objGetSuccess, _ = stats.Registry.NewMetric("frostfs_obj_get_success", metrics.Counter)
|
objDeleteTotal, _ = registry.NewMetric("frostfs_obj_delete_total", metrics.Counter)
|
||||||
objGetFails, _ = stats.Registry.NewMetric("frostfs_obj_get_fails", metrics.Counter)
|
objDeleteFails, _ = registry.NewMetric("frostfs_obj_delete_fails", metrics.Counter)
|
||||||
objGetDuration, _ = stats.Registry.NewMetric("frostfs_obj_get_duration", metrics.Trend, metrics.Time)
|
objDeleteDuration, _ = registry.NewMetric("frostfs_obj_delete_duration", metrics.Trend, metrics.Time)
|
||||||
objGetData, _ = stats.Registry.NewMetric("frostfs_obj_get_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
|
||||||
objDeleteSuccess, _ = stats.Registry.NewMetric("frostfs_obj_delete_success", metrics.Counter)
|
cnrPutTotal, _ = registry.NewMetric("frostfs_cnr_put_total", metrics.Counter)
|
||||||
objDeleteFails, _ = stats.Registry.NewMetric("frostfs_obj_delete_fails", metrics.Counter)
|
cnrPutFails, _ = registry.NewMetric("frostfs_cnr_put_fails", metrics.Counter)
|
||||||
objDeleteDuration, _ = stats.Registry.NewMetric("frostfs_obj_delete_duration", metrics.Trend, metrics.Time)
|
cnrPutDuration, _ = registry.NewMetric("frostfs_cnr_put_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
cnrPutTotal, _ = stats.Registry.NewMetric("frostfs_cnr_put_total", metrics.Counter)
|
|
||||||
cnrPutFails, _ = stats.Registry.NewMetric("frostfs_cnr_put_fails", metrics.Counter)
|
|
||||||
cnrPutDuration, _ = stats.Registry.NewMetric("frostfs_cnr_put_duration", metrics.Trend, metrics.Time)
|
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
vu: n.vu,
|
vu: n.vu,
|
||||||
|
@ -154,6 +138,5 @@ func (n *Native) Connect(endpoint, hexPrivateKey string, dialTimeout, streamTime
|
||||||
tok: tok,
|
tok: tok,
|
||||||
cli: &cli,
|
cli: &cli,
|
||||||
prepareLocally: prepareLocally,
|
prepareLocally: prepareLocally,
|
||||||
maxObjSize: uint64(maxObjSize),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,14 @@ type ObjExporter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PreGenerateInfo struct {
|
type PreGenerateInfo struct {
|
||||||
Buckets []string `json:"buckets"`
|
Buckets []string `json:"buckets"`
|
||||||
Containers []string `json:"containers"`
|
Objects []ObjInfo `json:"objects"`
|
||||||
Objects []ObjInfo `json:"objects"`
|
ObjSize string `json:"obj_size"`
|
||||||
ObjSize string `json:"obj_size"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjInfo struct {
|
type ObjInfo struct {
|
||||||
Bucket string `json:"bucket"`
|
Bucket string `json:"bucket"`
|
||||||
Object string `json:"object"`
|
Object string `json:"object"`
|
||||||
CID string `json:"cid"`
|
|
||||||
OID string `json:"oid"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObjExporter(selector *ObjSelector) *ObjExporter {
|
func NewObjExporter(selector *ObjSelector) *ObjExporter {
|
||||||
|
@ -40,7 +37,6 @@ func (o *ObjExporter) ExportJSONPreGen(fileName string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
bucketMap := make(map[string]struct{})
|
bucketMap := make(map[string]struct{})
|
||||||
containerMap := make(map[string]struct{})
|
|
||||||
|
|
||||||
count, err := o.selector.Count()
|
count, err := o.selector.Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,7 +50,7 @@ func (o *ObjExporter) ExportJSONPreGen(fileName string) error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = writeObjectInfo(comma, info, f); err != nil {
|
if _, err = f.WriteString(fmt.Sprintf(`%s{"bucket":"%s","object":"%s"}`, comma, info.S3Bucket, info.S3Key)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,54 +58,15 @@ func (o *ObjExporter) ExportJSONPreGen(fileName string) error {
|
||||||
comma = ","
|
comma = ","
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.S3Bucket != "" {
|
bucketMap[info.S3Bucket] = struct{}{}
|
||||||
bucketMap[info.S3Bucket] = struct{}{}
|
|
||||||
}
|
|
||||||
if info.CID != "" {
|
|
||||||
containerMap[info.CID] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = f.WriteString(`]`); err != nil {
|
if _, err = f.WriteString(`],"buckets":[`); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(bucketMap) > 0 {
|
|
||||||
if err = writeContainerInfo("buckets", bucketMap, f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(containerMap) > 0 {
|
|
||||||
if err = writeContainerInfo("containers", containerMap, f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = f.WriteString(`}`); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeObjectInfo(comma string, info *ObjectInfo, f *os.File) (err error) {
|
|
||||||
var res string
|
|
||||||
if info.S3Bucket != "" || info.S3Key != "" {
|
|
||||||
res = fmt.Sprintf(`%s{"bucket":"%s","object":"%s"}`, comma, info.S3Bucket, info.S3Key)
|
|
||||||
} else {
|
|
||||||
res = fmt.Sprintf(`%s{"cid":"%s","oid":"%s"}`, comma, info.CID, info.OID)
|
|
||||||
}
|
|
||||||
_, err = f.WriteString(res)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeContainerInfo(attrName string, bucketMap map[string]struct{}, f *os.File) (err error) {
|
|
||||||
if _, err = f.WriteString(fmt.Sprintf(`,"%s":[`, attrName)); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
comma := ""
|
comma = ""
|
||||||
for bucket := range bucketMap {
|
for bucket := range bucketMap {
|
||||||
if _, err = f.WriteString(fmt.Sprintf(`%s"%s"`, comma, bucket)); err != nil {
|
if _, err = f.WriteString(fmt.Sprintf(`%s"%s"`, comma, bucket)); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -119,6 +76,7 @@ func writeContainerInfo(attrName string, bucketMap map[string]struct{}, f *os.Fi
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
_, err = f.WriteString(`]`)
|
|
||||||
|
_, err = f.WriteString(`]}`)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
package registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type expectedResult struct {
|
|
||||||
mode string
|
|
||||||
objects []ObjectInfo
|
|
||||||
dir string
|
|
||||||
dbName string
|
|
||||||
jsonName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestObjectExporter(t *testing.T) {
|
|
||||||
names := []string{"s3", "grpc"}
|
|
||||||
for _, name := range names {
|
|
||||||
t.Run(name, runExportTest)
|
|
||||||
t.Run(name+"-changed", runExportChangedTest)
|
|
||||||
t.Run(name+"-empty", runExportEmptyTest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runExportTest(t *testing.T) {
|
|
||||||
expected := getExpectedResult(t)
|
|
||||||
objReg := getFilledRegistry(t, expected)
|
|
||||||
objExp := NewObjExporter(NewObjSelector(objReg, 0, SelectorOneshot, &ObjFilter{Status: statusCreated}))
|
|
||||||
|
|
||||||
require.NoError(t, objExp.ExportJSONPreGen(expected.jsonName))
|
|
||||||
require.NoError(t, checkExported(expected.objects, expected.jsonName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func runExportChangedTest(t *testing.T) {
|
|
||||||
expected := getExpectedResult(t)
|
|
||||||
objReg := getFilledRegistry(t, expected)
|
|
||||||
|
|
||||||
newStatus := randString(10)
|
|
||||||
num := randPositiveInt(1, len(expected.objects))
|
|
||||||
changedObjects := make([]ObjectInfo, num)
|
|
||||||
require.Equal(t, num, copy(changedObjects[:], expected.objects[:]))
|
|
||||||
|
|
||||||
sel := NewObjSelector(objReg, 0, SelectorOneshot, &ObjFilter{Status: statusCreated})
|
|
||||||
for i := range changedObjects {
|
|
||||||
changedObjects[i].Status = newStatus
|
|
||||||
require.NoError(t, objReg.SetObjectStatus(sel.NextObject().Id, statusCreated, newStatus))
|
|
||||||
}
|
|
||||||
|
|
||||||
objExp := NewObjExporter(NewObjSelector(objReg, 0, SelectorOneshot, &ObjFilter{Status: newStatus}))
|
|
||||||
require.NoError(t, objExp.ExportJSONPreGen(expected.jsonName))
|
|
||||||
require.NoError(t, checkExported(changedObjects, expected.jsonName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func runExportEmptyTest(t *testing.T) {
|
|
||||||
expected := getExpectedResult(t)
|
|
||||||
expected.objects = make([]ObjectInfo, 0)
|
|
||||||
objReg := getFilledRegistry(t, expected)
|
|
||||||
objExp := NewObjExporter(NewObjSelector(objReg, 0, SelectorOneshot, &ObjFilter{Status: statusCreated}))
|
|
||||||
|
|
||||||
require.NoError(t, objExp.ExportJSONPreGen(expected.jsonName))
|
|
||||||
require.NoError(t, checkExported(expected.objects, expected.jsonName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getExpectedResult(t *testing.T) expectedResult {
|
|
||||||
num := randPositiveInt(2, 100)
|
|
||||||
mode := getMode(t.Name())
|
|
||||||
require.NotEqual(t, "", mode, "test mode should contain either \"s3\" or\"grpc\"")
|
|
||||||
dir := t.TempDir()
|
|
||||||
res := expectedResult{
|
|
||||||
mode: mode,
|
|
||||||
objects: generateObjectInfo(num, t.Name()),
|
|
||||||
dir: dir,
|
|
||||||
dbName: filepath.Join(dir, "registry-"+mode+".db"),
|
|
||||||
jsonName: filepath.Join(dir, "registry-"+mode+".json"),
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func randPositiveInt(min, max int) int {
|
|
||||||
return rand.Intn(max-min) + min
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMode(name string) (res string) {
|
|
||||||
if strings.Contains(name, "s3") {
|
|
||||||
res = filepath.Base(name)
|
|
||||||
}
|
|
||||||
if strings.Contains(name, "grpc") {
|
|
||||||
res = filepath.Base(name)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateObjectInfo(num int, mode string) []ObjectInfo {
|
|
||||||
res := make([]ObjectInfo, num)
|
|
||||||
for i := range res {
|
|
||||||
res[i] = randomObjectInfo()
|
|
||||||
if !strings.Contains(mode, "s3") {
|
|
||||||
res[i].S3Bucket = ""
|
|
||||||
res[i].S3Key = ""
|
|
||||||
}
|
|
||||||
if !strings.Contains(mode, "grpc") {
|
|
||||||
res[i].CID = ""
|
|
||||||
res[i].OID = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFilledRegistry(t *testing.T, expected expectedResult) *ObjRegistry {
|
|
||||||
objReg := NewObjRegistry(context.Background(), expected.dbName)
|
|
||||||
for i := range expected.objects {
|
|
||||||
require.NoError(t, objReg.AddObject(expected.objects[i].CID, expected.objects[i].OID, expected.objects[i].S3Bucket, expected.objects[i].S3Key, expected.objects[i].PayloadHash))
|
|
||||||
}
|
|
||||||
return objReg
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkExported(expected []ObjectInfo, fileName string) error {
|
|
||||||
file, err := os.ReadFile(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !json.Valid(file) {
|
|
||||||
return fmt.Errorf("exported json file %s is invalid", fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
var actual PreGenerateInfo
|
|
||||||
if json.Unmarshal(file, &actual) != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(expected) != len(actual.Objects) {
|
|
||||||
return fmt.Errorf("expected len(): %v, got len(): %v", len(expected), len(actual.Objects))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range expected {
|
|
||||||
if !slices.ContainsFunc(actual.Objects, func(oi ObjInfo) bool {
|
|
||||||
compareS3 := oi.Bucket == expected[i].S3Bucket && oi.Object == expected[i].S3Key
|
|
||||||
comparegRPC := oi.CID == expected[i].CID && oi.OID == expected[i].OID
|
|
||||||
return compareS3 && comparegRPC
|
|
||||||
}) {
|
|
||||||
return fmt.Errorf("object %v not found in exported json file %s", expected[i], fileName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -101,7 +101,7 @@ func randomObjectInfo() ObjectInfo {
|
||||||
func randString(n int) string {
|
func randString(n int) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
sb.WriteRune('a' + rune(rand.Int31())%('z'-'a'+1))
|
sb.WriteRune('a' + rune(rand.Int())%('z'-'a'+1))
|
||||||
}
|
}
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ func NewObjRegistry(ctx context.Context, dbFilePath string) *ObjRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ObjRegistry) AddObject(cid, oid, s3Bucket, s3Key, payloadHash string) error {
|
func (o *ObjRegistry) AddObject(cid, oid, s3Bucket, s3Key, payloadHash string) error {
|
||||||
return o.boltDB.Batch(func(tx *bbolt.Tx) error {
|
return o.boltDB.Update(func(tx *bbolt.Tx) error {
|
||||||
b, err := tx.CreateBucketIfNotExists([]byte(statusCreated))
|
b, err := tx.CreateBucketIfNotExists([]byte(statusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -75,7 +75,7 @@ func (o *ObjRegistry) AddObject(cid, oid, s3Bucket, s3Key, payloadHash string) e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ObjRegistry) SetObjectStatus(id uint64, oldStatus, newStatus string) error {
|
func (o *ObjRegistry) SetObjectStatus(id uint64, oldStatus, newStatus string) error {
|
||||||
return o.boltDB.Batch(func(tx *bbolt.Tx) error {
|
return o.boltDB.Update(func(tx *bbolt.Tx) error {
|
||||||
oldB := tx.Bucket([]byte(oldStatus))
|
oldB := tx.Bucket([]byte(oldStatus))
|
||||||
if oldB == nil {
|
if oldB == nil {
|
||||||
return fmt.Errorf("bucket doesn't exist: '%s'", oldStatus)
|
return fmt.Errorf("bucket doesn't exist: '%s'", oldStatus)
|
||||||
|
@ -110,7 +110,7 @@ func (o *ObjRegistry) SetObjectStatus(id uint64, oldStatus, newStatus string) er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ObjRegistry) DeleteObject(id uint64) error {
|
func (o *ObjRegistry) DeleteObject(id uint64) error {
|
||||||
return o.boltDB.Batch(func(tx *bbolt.Tx) error {
|
return o.boltDB.Update(func(tx *bbolt.Tx) error {
|
||||||
return tx.ForEach(func(_ []byte, b *bbolt.Bucket) error {
|
return tx.ForEach(func(_ []byte, b *bbolt.Bucket) error {
|
||||||
return b.Delete(encodeId(id))
|
return b.Delete(encodeId(id))
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,15 +3,12 @@ package registry
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const nextObjectTimeout = 10 * time.Second
|
|
||||||
|
|
||||||
type ObjFilter struct {
|
type ObjFilter struct {
|
||||||
Status string
|
Status string
|
||||||
Age int
|
Age int
|
||||||
|
@ -23,9 +20,6 @@ type ObjSelector struct {
|
||||||
boltDB *bbolt.DB
|
boltDB *bbolt.DB
|
||||||
filter *ObjFilter
|
filter *ObjFilter
|
||||||
cacheSize int
|
cacheSize int
|
||||||
kind SelectorKind
|
|
||||||
// Sync synchronizes VU used for deletion.
|
|
||||||
Sync sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// objectSelectCache is the default maximum size of a batch to select from DB.
|
// objectSelectCache is the default maximum size of a batch to select from DB.
|
||||||
|
@ -33,7 +27,7 @@ const objectSelectCache = 1000
|
||||||
|
|
||||||
// NewObjSelector creates a new instance of object selector that can iterate over
|
// NewObjSelector creates a new instance of object selector that can iterate over
|
||||||
// objects in the specified registry.
|
// objects in the specified registry.
|
||||||
func NewObjSelector(registry *ObjRegistry, selectionSize int, kind SelectorKind, filter *ObjFilter) *ObjSelector {
|
func NewObjSelector(registry *ObjRegistry, selectionSize int, filter *ObjFilter) *ObjSelector {
|
||||||
if selectionSize <= 0 {
|
if selectionSize <= 0 {
|
||||||
selectionSize = objectSelectCache
|
selectionSize = objectSelectCache
|
||||||
}
|
}
|
||||||
|
@ -46,7 +40,6 @@ func NewObjSelector(registry *ObjRegistry, selectionSize int, kind SelectorKind,
|
||||||
filter: filter,
|
filter: filter,
|
||||||
objChan: make(chan *ObjectInfo, selectionSize*2),
|
objChan: make(chan *ObjectInfo, selectionSize*2),
|
||||||
cacheSize: selectionSize,
|
cacheSize: selectionSize,
|
||||||
kind: kind,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go objSelector.selectLoop()
|
go objSelector.selectLoop()
|
||||||
|
@ -62,21 +55,12 @@ func NewObjSelector(registry *ObjRegistry, selectionSize int, kind SelectorKind,
|
||||||
// - underlying registry context is done, nil objects will be returned on the
|
// - underlying registry context is done, nil objects will be returned on the
|
||||||
// currently blocked and every further NextObject calls.
|
// currently blocked and every further NextObject calls.
|
||||||
func (o *ObjSelector) NextObject() *ObjectInfo {
|
func (o *ObjSelector) NextObject() *ObjectInfo {
|
||||||
if o.kind == SelectorOneshot {
|
return <-o.objChan
|
||||||
return <-o.objChan
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(nextObjectTimeout):
|
|
||||||
return nil
|
|
||||||
case obj := <-o.objChan:
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count returns total number of objects that match filter of the selector.
|
// Count returns total number of objects that match filter of the selector.
|
||||||
func (o *ObjSelector) Count() (int, error) {
|
func (o *ObjSelector) Count() (int, error) {
|
||||||
count := 0
|
var count = 0
|
||||||
err := o.boltDB.View(func(tx *bbolt.Tx) error {
|
err := o.boltDB.View(func(tx *bbolt.Tx) error {
|
||||||
b := tx.Bucket([]byte(o.filter.Status))
|
b := tx.Bucket([]byte(o.filter.Status))
|
||||||
if b == nil {
|
if b == nil {
|
||||||
|
@ -176,20 +160,13 @@ func (o *ObjSelector) selectLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.kind == SelectorOneshot && len(cache) != o.cacheSize {
|
if len(cache) != o.cacheSize {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.kind != SelectorLooped && len(cache) != o.cacheSize {
|
|
||||||
// no more objects, wait a little; the logic could be improved.
|
// no more objects, wait a little; the logic could be improved.
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
case <-o.ctx.Done():
|
case <-o.ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if o.kind == SelectorLooped && len(cache) != o.cacheSize {
|
|
||||||
lastID = 0
|
lastID = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,35 +74,7 @@ func (r *Registry) open(dbFilePath string) *ObjRegistry {
|
||||||
return registry
|
return registry
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectorKind represents selector behaviour when no items are available.
|
|
||||||
type SelectorKind byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SelectorAwaiting waits for a new item to arrive.
|
|
||||||
// This selector visits each item exactly once and can be used when items
|
|
||||||
// to select are being pushed into registry concurrently.
|
|
||||||
SelectorAwaiting = iota
|
|
||||||
// SelectorLooped rewinds cursor to the start after all items have been read.
|
|
||||||
// It can encounter duplicates and should be used mostly for read scenarious.
|
|
||||||
SelectorLooped
|
|
||||||
// SelectorOneshot visits each item exactly once and exits immediately afterwards.
|
|
||||||
// It may be used to artificially abort the test after all items were processed.
|
|
||||||
SelectorOneshot
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *Registry) GetSelector(dbFilePath string, name string, cacheSize int, filter map[string]string) *ObjSelector {
|
func (r *Registry) GetSelector(dbFilePath string, name string, cacheSize int, filter map[string]string) *ObjSelector {
|
||||||
return r.getSelectorInternal(dbFilePath, name, cacheSize, SelectorAwaiting, filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Registry) GetLoopedSelector(dbFilePath string, name string, cacheSize int, filter map[string]string) *ObjSelector {
|
|
||||||
return r.getSelectorInternal(dbFilePath, name, cacheSize, SelectorLooped, filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Registry) GetOneshotSelector(dbFilePath string, name string, cacheSize int, filter map[string]string) *ObjSelector {
|
|
||||||
return r.getSelectorInternal(dbFilePath, name, cacheSize, SelectorOneshot, filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Registry) getSelectorInternal(dbFilePath string, name string, cacheSize int, kind SelectorKind, filter map[string]string) *ObjSelector {
|
|
||||||
objFilter, err := parseFilter(filter)
|
objFilter, err := parseFilter(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -114,7 +86,7 @@ func (r *Registry) getSelectorInternal(dbFilePath string, name string, cacheSize
|
||||||
selector := r.root.selectors[name]
|
selector := r.root.selectors[name]
|
||||||
if selector == nil {
|
if selector == nil {
|
||||||
registry := r.open(dbFilePath)
|
registry := r.open(dbFilePath)
|
||||||
selector = NewObjSelector(registry, cacheSize, kind, objFilter)
|
selector = NewObjSelector(registry, cacheSize, objFilter)
|
||||||
r.root.selectors[name] = selector
|
r.root.selectors[name] = selector
|
||||||
} else if !reflect.DeepEqual(selector.filter, objFilter) {
|
} else if !reflect.DeepEqual(selector.filter, objFilter) {
|
||||||
panic(fmt.Sprintf("selector %s already has been created with a different filter", name))
|
panic(fmt.Sprintf("selector %s already has been created with a different filter", name))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package s3
|
package s3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
@ -8,12 +9,12 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/datagen"
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||||
|
"github.com/dop251/goja"
|
||||||
"go.k6.io/k6/js/modules"
|
"go.k6.io/k6/js/modules"
|
||||||
"go.k6.io/k6/metrics"
|
"go.k6.io/k6/metrics"
|
||||||
)
|
)
|
||||||
|
@ -50,9 +51,9 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) Put(bucket, key string, payload datagen.Payload) PutResponse {
|
func (c *Client) Put(bucket, key string, payload goja.ArrayBuffer) PutResponse {
|
||||||
rdr := payload.Reader()
|
rdr := bytes.NewReader(payload.Bytes())
|
||||||
sz := payload.Size()
|
sz := rdr.Size()
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
_, err := c.cli.PutObject(c.vu.Context(), &s3.PutObjectInput{
|
_, err := c.cli.PutObject(c.vu.Context(), &s3.PutObjectInput{
|
||||||
|
@ -65,16 +66,15 @@ func (c *Client) Put(bucket, key string, payload datagen.Payload) PutResponse {
|
||||||
return PutResponse{Success: false, Error: err.Error()}
|
return PutResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objPutSuccess, 1)
|
stats.Report(c.vu, objPutTotal, 1)
|
||||||
stats.ReportDataSent(c.vu, float64(sz))
|
stats.ReportDataSent(c.vu, float64(sz))
|
||||||
stats.Report(c.vu, objPutDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objPutDuration, metrics.D(time.Since(start)))
|
||||||
stats.Report(c.vu, objPutData, float64(sz))
|
|
||||||
return PutResponse{Success: true}
|
return PutResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
const multipartUploadMinPartSize = 5 * 1024 * 1024 // 5MB
|
const multipartUploadMinPartSize = 5 * 1024 * 1024 // 5MB
|
||||||
|
|
||||||
func (c *Client) Multipart(bucket, key string, objPartSize, concurrency int, payload datagen.Payload) PutResponse {
|
func (c *Client) Multipart(bucket, key string, objPartSize, concurrency int, payload goja.ArrayBuffer) PutResponse {
|
||||||
if objPartSize < multipartUploadMinPartSize {
|
if objPartSize < multipartUploadMinPartSize {
|
||||||
stats.Report(c.vu, objPutFails, 1)
|
stats.Report(c.vu, objPutFails, 1)
|
||||||
return PutResponse{Success: false, Error: fmt.Sprintf("part size '%d' must be greater than '%d'(5 MB)", objPartSize, multipartUploadMinPartSize)}
|
return PutResponse{Success: false, Error: fmt.Sprintf("part size '%d' must be greater than '%d'(5 MB)", objPartSize, multipartUploadMinPartSize)}
|
||||||
|
@ -86,8 +86,8 @@ func (c *Client) Multipart(bucket, key string, objPartSize, concurrency int, pay
|
||||||
u.Concurrency = concurrency
|
u.Concurrency = concurrency
|
||||||
})
|
})
|
||||||
|
|
||||||
payloadReader := payload.Reader()
|
payloadReader := bytes.NewReader(payload.Bytes())
|
||||||
sz := payload.Size()
|
sz := payloadReader.Len()
|
||||||
|
|
||||||
_, err := uploader.Upload(c.vu.Context(), &s3.PutObjectInput{
|
_, err := uploader.Upload(c.vu.Context(), &s3.PutObjectInput{
|
||||||
Bucket: aws.String(bucket),
|
Bucket: aws.String(bucket),
|
||||||
|
@ -99,10 +99,9 @@ func (c *Client) Multipart(bucket, key string, objPartSize, concurrency int, pay
|
||||||
return PutResponse{Success: false, Error: err.Error()}
|
return PutResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objPutSuccess, 1)
|
stats.Report(c.vu, objPutTotal, 1)
|
||||||
stats.ReportDataSent(c.vu, float64(sz))
|
stats.ReportDataSent(c.vu, float64(sz))
|
||||||
stats.Report(c.vu, objPutDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objPutDuration, metrics.D(time.Since(start)))
|
||||||
stats.Report(c.vu, objPutData, float64(sz))
|
|
||||||
return PutResponse{Success: true}
|
return PutResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +117,7 @@ func (c *Client) Delete(bucket, key string) DeleteResponse {
|
||||||
return DeleteResponse{Success: false, Error: err.Error()}
|
return DeleteResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objDeleteSuccess, 1)
|
stats.Report(c.vu, objDeleteTotal, 1)
|
||||||
stats.Report(c.vu, objDeleteDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objDeleteDuration, metrics.D(time.Since(start)))
|
||||||
return DeleteResponse{Success: true}
|
return DeleteResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +125,7 @@ func (c *Client) Delete(bucket, key string) DeleteResponse {
|
||||||
func (c *Client) Get(bucket, key string) GetResponse {
|
func (c *Client) Get(bucket, key string) GetResponse {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
objSize := 0
|
var objSize = 0
|
||||||
err := get(c.cli, bucket, key, func(chunk []byte) {
|
err := get(c.cli, bucket, key, func(chunk []byte) {
|
||||||
objSize += len(chunk)
|
objSize += len(chunk)
|
||||||
})
|
})
|
||||||
|
@ -135,77 +134,12 @@ func (c *Client) Get(bucket, key string) GetResponse {
|
||||||
return GetResponse{Success: false, Error: err.Error()}
|
return GetResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objGetSuccess, 1)
|
stats.Report(c.vu, objGetTotal, 1)
|
||||||
stats.Report(c.vu, objGetDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objGetDuration, metrics.D(time.Since(start)))
|
||||||
stats.ReportDataReceived(c.vu, float64(objSize))
|
stats.ReportDataReceived(c.vu, float64(objSize))
|
||||||
stats.Report(c.vu, objGetData, float64(objSize))
|
|
||||||
return GetResponse{Success: true}
|
return GetResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectVersion deletes object version with specified versionID.
|
|
||||||
// If version argument is empty, deletes all versions and delete-markers of specified object.
|
|
||||||
func (c *Client) DeleteObjectVersion(bucket, key, version string) DeleteResponse {
|
|
||||||
var toDelete []types.ObjectIdentifier
|
|
||||||
|
|
||||||
if version != "" {
|
|
||||||
toDelete = append(toDelete, types.ObjectIdentifier{
|
|
||||||
Key: aws.String(key),
|
|
||||||
VersionId: aws.String(version),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
versions, err := c.cli.ListObjectVersions(c.vu.Context(), &s3.ListObjectVersionsInput{
|
|
||||||
Bucket: aws.String(bucket),
|
|
||||||
Prefix: aws.String(key),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
stats.Report(c.vu, objDeleteFails, 1)
|
|
||||||
return DeleteResponse{Success: false, Error: err.Error()}
|
|
||||||
}
|
|
||||||
toDelete = filterObjectVersions(versions, key)
|
|
||||||
}
|
|
||||||
if len(toDelete) == 0 {
|
|
||||||
return c.Delete(bucket, key)
|
|
||||||
} else {
|
|
||||||
_, err := c.cli.DeleteObjects(c.vu.Context(), &s3.DeleteObjectsInput{
|
|
||||||
Bucket: aws.String(bucket),
|
|
||||||
Delete: &types.Delete{
|
|
||||||
Objects: toDelete,
|
|
||||||
Quiet: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
stats.Report(c.vu, objDeleteFails, 1)
|
|
||||||
return DeleteResponse{Success: false, Error: err.Error()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DeleteResponse{Success: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterObjectVersions(versions *s3.ListObjectVersionsOutput, key string) []types.ObjectIdentifier {
|
|
||||||
var result []types.ObjectIdentifier
|
|
||||||
|
|
||||||
for _, v := range versions.Versions {
|
|
||||||
if *v.Key == key {
|
|
||||||
result = append(result, types.ObjectIdentifier{
|
|
||||||
Key: v.Key,
|
|
||||||
VersionId: v.VersionId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, marker := range versions.DeleteMarkers {
|
|
||||||
if *marker.Key == key {
|
|
||||||
result = append(result, types.ObjectIdentifier{
|
|
||||||
Key: marker.Key,
|
|
||||||
VersionId: marker.VersionId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(
|
func get(
|
||||||
c *s3.Client,
|
c *s3.Client,
|
||||||
bucket string,
|
bucket string,
|
||||||
|
@ -244,7 +178,7 @@ func (c *Client) VerifyHash(bucket, key, expectedHash string) VerifyHashResponse
|
||||||
}
|
}
|
||||||
actualHash := hex.EncodeToString(hasher.Sum(nil))
|
actualHash := hex.EncodeToString(hasher.Sum(nil))
|
||||||
if actualHash != expectedHash {
|
if actualHash != expectedHash {
|
||||||
return VerifyHashResponse{Success: false, Error: "hash mismatch"}
|
return VerifyHashResponse{Success: true, Error: "hash mismatch"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return VerifyHashResponse{Success: true}
|
return VerifyHashResponse{Success: true}
|
||||||
|
@ -279,27 +213,7 @@ func (c *Client) CreateBucket(bucket string, params map[string]string) CreateBuc
|
||||||
return CreateBucketResponse{Success: false, Error: err.Error()}
|
return CreateBucketResponse{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
var versioning bool
|
stats.Report(c.vu, createBucketTotal, 1)
|
||||||
if strVersioned, ok := params["versioning"]; ok {
|
|
||||||
if versioning, err = strconv.ParseBool(strVersioned); err != nil {
|
|
||||||
stats.Report(c.vu, createBucketFails, 1)
|
|
||||||
return CreateBucketResponse{Success: false, Error: err.Error()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if versioning {
|
|
||||||
_, err = c.cli.PutBucketVersioning(context.TODO(), &s3.PutBucketVersioningInput{
|
|
||||||
Bucket: aws.String(bucket),
|
|
||||||
VersioningConfiguration: &types.VersioningConfiguration{
|
|
||||||
Status: types.BucketVersioningStatusEnabled,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
stats.Report(c.vu, createBucketFails, 1)
|
|
||||||
return CreateBucketResponse{Success: false, Error: err.Error()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.Report(c.vu, createBucketSuccess, 1)
|
|
||||||
stats.Report(c.vu, createBucketDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, createBucketDuration, metrics.D(time.Since(start)))
|
||||||
return CreateBucketResponse{Success: true}
|
return CreateBucketResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
|
@ -29,10 +28,10 @@ var (
|
||||||
_ modules.Instance = &S3{}
|
_ modules.Instance = &S3{}
|
||||||
_ modules.Module = &RootModule{}
|
_ modules.Module = &RootModule{}
|
||||||
|
|
||||||
objPutSuccess, objPutFails, objPutDuration, objPutData *metrics.Metric
|
objPutTotal, objPutFails, objPutDuration *metrics.Metric
|
||||||
objGetSuccess, objGetFails, objGetDuration, objGetData *metrics.Metric
|
objGetTotal, objGetFails, objGetDuration *metrics.Metric
|
||||||
objDeleteSuccess, objDeleteFails, objDeleteDuration *metrics.Metric
|
objDeleteTotal, objDeleteFails, objDeleteDuration *metrics.Metric
|
||||||
createBucketSuccess, createBucketFails, createBucketDuration *metrics.Metric
|
createBucketTotal, createBucketFails, createBucketDuration *metrics.Metric
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -95,23 +94,22 @@ func (s *S3) Connect(endpoint string, params map[string]string) (*Client, error)
|
||||||
})
|
})
|
||||||
|
|
||||||
// register metrics
|
// register metrics
|
||||||
objPutSuccess, _ = stats.Registry.NewMetric("aws_obj_put_success", metrics.Counter)
|
registry := metrics.NewRegistry()
|
||||||
objPutFails, _ = stats.Registry.NewMetric("aws_obj_put_fails", metrics.Counter)
|
objPutTotal, _ = registry.NewMetric("aws_obj_put_total", metrics.Counter)
|
||||||
objPutDuration, _ = stats.Registry.NewMetric("aws_obj_put_duration", metrics.Trend, metrics.Time)
|
objPutFails, _ = registry.NewMetric("aws_obj_put_fails", metrics.Counter)
|
||||||
objPutData, _ = stats.Registry.NewMetric("aws_obj_put_bytes", metrics.Counter, metrics.Data)
|
objPutDuration, _ = registry.NewMetric("aws_obj_put_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
objGetSuccess, _ = stats.Registry.NewMetric("aws_obj_get_success", metrics.Counter)
|
objGetTotal, _ = registry.NewMetric("aws_obj_get_total", metrics.Counter)
|
||||||
objGetFails, _ = stats.Registry.NewMetric("aws_obj_get_fails", metrics.Counter)
|
objGetFails, _ = registry.NewMetric("aws_obj_get_fails", metrics.Counter)
|
||||||
objGetDuration, _ = stats.Registry.NewMetric("aws_obj_get_duration", metrics.Trend, metrics.Time)
|
objGetDuration, _ = registry.NewMetric("aws_obj_get_duration", metrics.Trend, metrics.Time)
|
||||||
objGetData, _ = stats.Registry.NewMetric("aws_obj_get_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
|
||||||
objDeleteSuccess, _ = stats.Registry.NewMetric("aws_obj_delete_success", metrics.Counter)
|
objDeleteTotal, _ = registry.NewMetric("aws_obj_delete_total", metrics.Counter)
|
||||||
objDeleteFails, _ = stats.Registry.NewMetric("aws_obj_delete_fails", metrics.Counter)
|
objDeleteFails, _ = registry.NewMetric("aws_obj_delete_fails", metrics.Counter)
|
||||||
objDeleteDuration, _ = stats.Registry.NewMetric("aws_obj_delete_duration", metrics.Trend, metrics.Time)
|
objDeleteDuration, _ = registry.NewMetric("aws_obj_delete_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
createBucketSuccess, _ = stats.Registry.NewMetric("aws_create_bucket_success", metrics.Counter)
|
createBucketTotal, _ = registry.NewMetric("aws_create_bucket_total", metrics.Counter)
|
||||||
createBucketFails, _ = stats.Registry.NewMetric("aws_create_bucket_fails", metrics.Counter)
|
createBucketFails, _ = registry.NewMetric("aws_create_bucket_fails", metrics.Counter)
|
||||||
createBucketDuration, _ = stats.Registry.NewMetric("aws_create_bucket_duration", metrics.Trend, metrics.Time)
|
createBucketDuration, _ = registry.NewMetric("aws_create_bucket_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
vu: s.vu,
|
vu: s.vu,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package s3local
|
package s3local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/datagen"
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/local"
|
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/stats"
|
||||||
|
"github.com/dop251/goja"
|
||||||
"go.k6.io/k6/js/modules"
|
"go.k6.io/k6/js/modules"
|
||||||
"go.k6.io/k6/metrics"
|
"go.k6.io/k6/metrics"
|
||||||
)
|
)
|
||||||
|
@ -18,13 +18,11 @@ type Client struct {
|
||||||
l layer.Client
|
l layer.Client
|
||||||
ownerID *user.ID
|
ownerID *user.ID
|
||||||
resolver layer.BucketResolver
|
resolver layer.BucketResolver
|
||||||
limiter local.Limiter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
SuccessOrErrorResponse struct {
|
SuccessOrErrorResponse struct {
|
||||||
Success bool
|
Success bool
|
||||||
Abort bool
|
|
||||||
Error string
|
Error string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,14 +32,7 @@ type (
|
||||||
GetResponse SuccessOrErrorResponse
|
GetResponse SuccessOrErrorResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) Put(bucket, key string, payload datagen.Payload) PutResponse {
|
func (c *Client) Put(bucket, key string, payload goja.ArrayBuffer) PutResponse {
|
||||||
if c.limiter.IsFull() {
|
|
||||||
return PutResponse{
|
|
||||||
Success: false,
|
|
||||||
Abort: true,
|
|
||||||
Error: "engine size limit reached",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cid, err := c.resolver.Resolve(c.vu.Context(), bucket)
|
cid, err := c.resolver.Resolve(c.vu.Context(), bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stats.Report(c.vu, objPutFails, 1)
|
stats.Report(c.vu, objPutFails, 1)
|
||||||
|
@ -57,8 +48,8 @@ func (c *Client) Put(bucket, key string, payload datagen.Payload) PutResponse {
|
||||||
},
|
},
|
||||||
Header: map[string]string{},
|
Header: map[string]string{},
|
||||||
Object: key,
|
Object: key,
|
||||||
Size: uint64(payload.Size()),
|
Size: int64(len(payload.Bytes())),
|
||||||
Reader: payload.Reader(),
|
Reader: bytes.NewReader(payload.Bytes()),
|
||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
@ -68,9 +59,8 @@ func (c *Client) Put(bucket, key string, payload datagen.Payload) PutResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objPutDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objPutDuration, metrics.D(time.Since(start)))
|
||||||
stats.Report(c.vu, objPutSuccess, 1)
|
stats.Report(c.vu, objPutTotal, 1)
|
||||||
stats.ReportDataSent(c.vu, float64(prm.Size))
|
stats.ReportDataSent(c.vu, float64(prm.Size))
|
||||||
stats.Report(c.vu, objPutData, float64(prm.Size))
|
|
||||||
|
|
||||||
return PutResponse{Success: true}
|
return PutResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
@ -108,22 +98,16 @@ func (c *Client) Get(bucket, key string) GetResponse {
|
||||||
Start: 0,
|
Start: 0,
|
||||||
End: uint64(extInfo.ObjectInfo.Size),
|
End: uint64(extInfo.ObjectInfo.Size),
|
||||||
},
|
},
|
||||||
|
Writer: wr,
|
||||||
}
|
}
|
||||||
objPayload, err := c.l.GetObject(c.vu.Context(), getPrm)
|
if err := c.l.GetObject(c.vu.Context(), getPrm); err != nil {
|
||||||
if err != nil {
|
|
||||||
stats.Report(c.vu, objGetFails, 1)
|
|
||||||
return GetResponse{Error: err.Error()}
|
|
||||||
}
|
|
||||||
err = objPayload.StreamTo(wr)
|
|
||||||
if err != nil {
|
|
||||||
stats.Report(c.vu, objGetFails, 1)
|
stats.Report(c.vu, objGetFails, 1)
|
||||||
return GetResponse{Error: err.Error()}
|
return GetResponse{Error: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Report(c.vu, objGetDuration, metrics.D(time.Since(start)))
|
stats.Report(c.vu, objGetDuration, metrics.D(time.Since(start)))
|
||||||
stats.Report(c.vu, objGetSuccess, 1)
|
stats.Report(c.vu, objGetTotal, 1)
|
||||||
stats.ReportDataReceived(c.vu, wr.total)
|
stats.ReportDataReceived(c.vu, wr.total)
|
||||||
stats.Report(c.vu, objGetData, wr.total)
|
|
||||||
|
|
||||||
return GetResponse{Success: true}
|
return GetResponse{Success: true}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/local/rawclient"
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/local/rawclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,15 +30,15 @@ func unimplementedMessage(fname string) string {
|
||||||
"something other than filling a cluster (i.e. PUT or GET).", fname)
|
"something other than filling a cluster (i.e. PUT or GET).", fname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*frostfs) CreateContainer(context.Context, layer.PrmContainerCreate) (*layer.ContainerCreateResult, error) {
|
func (*frostfs) CreateContainer(context.Context, layer.PrmContainerCreate) (cid.ID, error) {
|
||||||
panic(unimplementedMessage("CreateContainer"))
|
panic(unimplementedMessage("CreateContainer"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*frostfs) Container(ctx context.Context, prmContainer layer.PrmContainer) (*container.Container, error) {
|
func (*frostfs) Container(context.Context, cid.ID) (*container.Container, error) {
|
||||||
panic(unimplementedMessage("Container"))
|
panic(unimplementedMessage("Container"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*frostfs) UserContainers(ctx context.Context, containers layer.PrmUserContainers) ([]cid.ID, error) {
|
func (*frostfs) UserContainers(context.Context, user.ID) ([]cid.ID, error) {
|
||||||
panic(unimplementedMessage("UserContainers"))
|
panic(unimplementedMessage("UserContainers"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ func (*frostfs) SetContainerEACL(context.Context, eacl.Table, *session.Container
|
||||||
panic(unimplementedMessage("SetContainerEACL"))
|
panic(unimplementedMessage("SetContainerEACL"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*frostfs) ContainerEACL(ctx context.Context, containerEACL layer.PrmContainerEACL) (*eacl.Table, error) {
|
func (*frostfs) ContainerEACL(context.Context, cid.ID) (*eacl.Table, error) {
|
||||||
panic(unimplementedMessage("ContainerEACL"))
|
panic(unimplementedMessage("ContainerEACL"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ func (f *frostfs) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (
|
||||||
for _, attr := range prm.Attributes {
|
for _, attr := range prm.Attributes {
|
||||||
hdrs[attr[0]] = attr[1]
|
hdrs[attr[0]] = attr[1]
|
||||||
}
|
}
|
||||||
return f.Put(ctx, prm.Container, nil, hdrs, payload)
|
return f.Put(ctx, prm.Container, &prm.Creator, hdrs, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *frostfs) DeleteObject(context.Context, layer.PrmObjectDelete) error {
|
func (f *frostfs) DeleteObject(context.Context, layer.PrmObjectDelete) error {
|
||||||
|
@ -87,7 +88,3 @@ func (f *frostfs) DeleteObject(context.Context, layer.PrmObjectDelete) error {
|
||||||
func (f *frostfs) TimeToEpoch(ctx context.Context, now time.Time, future time.Time) (uint64, uint64, error) {
|
func (f *frostfs) TimeToEpoch(ctx context.Context, now time.Time, future time.Time) (uint64, uint64, error) {
|
||||||
panic(unimplementedMessage("TimeToEpoch"))
|
panic(unimplementedMessage("TimeToEpoch"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *frostfs) SearchObjects(ctx context.Context, search layer.PrmObjectSearch) ([]oid.ID, error) {
|
|
||||||
panic(unimplementedMessage("SearchObjects"))
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,10 +32,10 @@ var (
|
||||||
_ modules.Module = &RootModule{}
|
_ modules.Module = &RootModule{}
|
||||||
_ modules.Instance = &Local{}
|
_ modules.Instance = &Local{}
|
||||||
|
|
||||||
internalObjPutSuccess, internalObjPutFails, internalObjPutDuration, internalObjPutData *metrics.Metric
|
internalObjPutTotal, internalObjPutFails, internalObjPutDuration *metrics.Metric
|
||||||
internalObjGetSuccess, internalObjGetFails, internalObjGetDuration, internalObjGetData *metrics.Metric
|
internalObjGetTotal, internalObjGetFails, internalObjGetDuration *metrics.Metric
|
||||||
objPutSuccess, objPutFails, objPutDuration, objPutData *metrics.Metric
|
objPutTotal, objPutFails, objPutDuration *metrics.Metric
|
||||||
objGetSuccess, objGetFails, objGetDuration, objGetData *metrics.Metric
|
objGetTotal, objGetFails, objGetDuration *metrics.Metric
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -56,7 +56,7 @@ func (s *Local) Exports() modules.Exports {
|
||||||
return modules.Exports{Default: s}
|
return modules.Exports{Default: s}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Local) Connect(configFile string, configDir string, params map[string]string, bucketMapping map[string]string, maxSizeGB int64) (*Client, error) {
|
func (s *Local) Connect(configFile string, params map[string]string, bucketMapping map[string]string) (*Client, error) {
|
||||||
// Parse configuration flags.
|
// Parse configuration flags.
|
||||||
fs := flag.NewFlagSet("s3local", flag.ContinueOnError)
|
fs := flag.NewFlagSet("s3local", flag.ContinueOnError)
|
||||||
|
|
||||||
|
@ -88,30 +88,28 @@ func (s *Local) Connect(configFile string, configDir string, params map[string]s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register metrics.
|
// Register metrics.
|
||||||
internalObjPutSuccess, _ = stats.Registry.NewMetric("s3local_internal_obj_put_success", metrics.Counter)
|
registry := metrics.NewRegistry()
|
||||||
internalObjPutFails, _ = stats.Registry.NewMetric("s3local_internal_obj_put_fails", metrics.Counter)
|
|
||||||
internalObjPutDuration, _ = stats.Registry.NewMetric("s3local_internal_obj_put_duration", metrics.Trend, metrics.Time)
|
|
||||||
internalObjPutData, _ = stats.Registry.NewMetric("s3local_internal_obj_put_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
|
||||||
internalObjGetSuccess, _ = stats.Registry.NewMetric("s3local_internal_obj_get_success", metrics.Counter)
|
internalObjPutTotal, _ = registry.NewMetric("s3local_internal_obj_put_total", metrics.Counter)
|
||||||
internalObjGetFails, _ = stats.Registry.NewMetric("s3local_internal_obj_get_fails", metrics.Counter)
|
internalObjPutFails, _ = registry.NewMetric("s3local_internal_obj_put_fails", metrics.Counter)
|
||||||
internalObjGetDuration, _ = stats.Registry.NewMetric("s3local_internal_obj_get_duration", metrics.Trend, metrics.Time)
|
internalObjPutDuration, _ = registry.NewMetric("s3local_internal_obj_put_duration", metrics.Trend, metrics.Time)
|
||||||
internalObjGetData, _ = stats.Registry.NewMetric("s3local_internal_obj_get_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
|
||||||
objPutSuccess, _ = stats.Registry.NewMetric("s3local_obj_put_success", metrics.Counter)
|
internalObjGetTotal, _ = registry.NewMetric("s3local_internal_obj_get_total", metrics.Counter)
|
||||||
objPutFails, _ = stats.Registry.NewMetric("s3local_obj_put_fails", metrics.Counter)
|
internalObjGetFails, _ = registry.NewMetric("s3local_internal_obj_get_fails", metrics.Counter)
|
||||||
objPutDuration, _ = stats.Registry.NewMetric("s3local_obj_put_duration", metrics.Trend, metrics.Time)
|
internalObjGetDuration, _ = registry.NewMetric("s3local_internal_obj_get_duration", metrics.Trend, metrics.Time)
|
||||||
objPutData, _ = stats.Registry.NewMetric("s3local_obj_put_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
|
||||||
objGetSuccess, _ = stats.Registry.NewMetric("s3local_obj_get_success", metrics.Counter)
|
objPutTotal, _ = registry.NewMetric("s3local_obj_put_total", metrics.Counter)
|
||||||
objGetFails, _ = stats.Registry.NewMetric("s3local_obj_get_fails", metrics.Counter)
|
objPutFails, _ = registry.NewMetric("s3local_obj_put_fails", metrics.Counter)
|
||||||
objGetDuration, _ = stats.Registry.NewMetric("s3local_obj_get_duration", metrics.Trend, metrics.Time)
|
objPutDuration, _ = registry.NewMetric("s3local_obj_put_duration", metrics.Trend, metrics.Time)
|
||||||
objGetData, _ = stats.Registry.NewMetric("s3local_obj_get_bytes", metrics.Counter, metrics.Data)
|
|
||||||
|
objGetTotal, _ = registry.NewMetric("s3local_obj_get_total", metrics.Counter)
|
||||||
|
objGetFails, _ = registry.NewMetric("s3local_obj_get_fails", metrics.Counter)
|
||||||
|
objGetDuration, _ = registry.NewMetric("s3local_obj_get_duration", metrics.Trend, metrics.Time)
|
||||||
|
|
||||||
// Create S3 layer backed by local storage engine and tree service.
|
// Create S3 layer backed by local storage engine and tree service.
|
||||||
ng, limiter, err := s.l.ResolveEngine(s.l.VU().Context(), configFile, configDir, *debugLogger, maxSizeGB)
|
ng, err := s.l.ResolveEngine(s.l.VU().Context(), configFile, *debugLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connecting to engine for config - file %q dir %q: %v", configFile, configDir, err)
|
return nil, fmt.Errorf("connecting to engine for config %q: %v", configFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
treeSvc := tree.NewTree(treeServiceEngineWrapper{
|
treeSvc := tree.NewTree(treeServiceEngineWrapper{
|
||||||
|
@ -126,18 +124,16 @@ func (s *Local) Connect(configFile string, configDir string, params map[string]s
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stats.Report(s.l.VU(), internalObjPutFails, 1)
|
stats.Report(s.l.VU(), internalObjPutFails, 1)
|
||||||
} else {
|
} else {
|
||||||
stats.Report(s.l.VU(), internalObjPutSuccess, 1)
|
stats.Report(s.l.VU(), internalObjPutTotal, 1)
|
||||||
stats.Report(s.l.VU(), internalObjPutDuration, metrics.D(dt))
|
stats.Report(s.l.VU(), internalObjPutDuration, metrics.D(dt))
|
||||||
stats.Report(s.l.VU(), internalObjPutData, float64(sz))
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
rawclient.WithGetHandler(func(sz uint64, err error, dt time.Duration) {
|
rawclient.WithGetHandler(func(sz uint64, err error, dt time.Duration) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stats.Report(s.l.VU(), internalObjGetFails, 1)
|
stats.Report(s.l.VU(), internalObjGetFails, 1)
|
||||||
} else {
|
} else {
|
||||||
stats.Report(s.l.VU(), internalObjGetSuccess, 1)
|
stats.Report(s.l.VU(), internalObjGetTotal, 1)
|
||||||
stats.Report(s.l.VU(), internalObjGetDuration, metrics.D(dt))
|
stats.Report(s.l.VU(), internalObjGetDuration, metrics.D(dt))
|
||||||
stats.Report(s.l.VU(), internalObjGetData, float64(sz))
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -148,24 +144,20 @@ func (s *Local) Connect(configFile string, configDir string, params map[string]s
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &layer.Config{
|
cfg := &layer.Config{
|
||||||
Cache: layer.NewCache(layer.DefaultCachesConfigs(zap.L())),
|
Caches: layer.DefaultCachesConfigs(zap.L()),
|
||||||
AnonKey: layer.AnonymousKey{Key: key},
|
AnonKey: layer.AnonymousKey{Key: key},
|
||||||
Resolver: resolver,
|
Resolver: resolver,
|
||||||
TreeService: treeSvc,
|
TreeService: treeSvc,
|
||||||
}
|
}
|
||||||
|
|
||||||
l := layer.NewLayer(zap.L(), &frostfs{rc}, cfg)
|
l := layer.NewLayer(zap.L(), &frostfs{rc}, cfg)
|
||||||
err = l.Initialize(s.l.VU().Context(), nopEventListener{})
|
l.Initialize(s.l.VU().Context(), nopEventListener{})
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("initialize: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
vu: s.l.VU(),
|
vu: s.l.VU(),
|
||||||
l: l,
|
l: l,
|
||||||
ownerID: rc.OwnerID(),
|
ownerID: rc.OwnerID(),
|
||||||
resolver: resolver,
|
resolver: resolver,
|
||||||
limiter: limiter,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ func (s treeServiceEngineWrapper) GetSubTree(ctx context.Context, bktInfo *data.
|
||||||
return fmt.Errorf("getting children: %v", err)
|
return fmt.Errorf("getting children: %v", err)
|
||||||
}
|
}
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
if err := traverse(child.ID, curDepth+1); err != nil {
|
if err := traverse(child, curDepth+1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,12 +191,6 @@ func (s treeServiceEngineWrapper) RemoveNode(ctx context.Context, bktInfo *data.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s treeServiceEngineWrapper) GetSubTreeStream(
|
|
||||||
ctx context.Context, bktInfo *data.BucketInfo, treeID string, rootID uint64, depth uint32,
|
|
||||||
) (tree.SubTreeStream, error) {
|
|
||||||
panic(unimplementedMessage("TreeService.GetSubTreeStream"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapToKV(m map[string]string) []pilorama.KeyValue {
|
func mapToKV(m map[string]string) []pilorama.KeyValue {
|
||||||
var kvs []pilorama.KeyValue
|
var kvs []pilorama.KeyValue
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
|
|
|
@ -1,54 +1,16 @@
|
||||||
package stats
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.k6.io/k6/js/modules"
|
"go.k6.io/k6/js/modules"
|
||||||
"go.k6.io/k6/metrics"
|
"go.k6.io/k6/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RootModule is the global module object type. It is instantiated once per test
|
|
||||||
// run and will be used to create k6/x/frostfs/stats module instances for each VU.
|
|
||||||
type RootModule struct {
|
|
||||||
Instance string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tagSet *metrics.TagSet
|
|
||||||
|
|
||||||
Registry *metrics.Registry
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Registry = metrics.NewRegistry()
|
|
||||||
tagSet = Registry.RootTagSet()
|
|
||||||
modules.Register("k6/x/frostfs/stats", &RootModule{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTags sets additional tags to custom metrics.
|
|
||||||
// Format: "key1:value1;key2:value2".
|
|
||||||
// Panics if input has invalid format.
|
|
||||||
func (m *RootModule) SetTags(labels string) {
|
|
||||||
kv := make(map[string]string)
|
|
||||||
pairs := strings.Split(labels, ";")
|
|
||||||
for _, pair := range pairs {
|
|
||||||
items := strings.Split(pair, ":")
|
|
||||||
if len(items) != 2 {
|
|
||||||
panic("invalid labels format")
|
|
||||||
}
|
|
||||||
kv[strings.TrimSpace(items[0])] = strings.TrimSpace(items[1])
|
|
||||||
}
|
|
||||||
for k, v := range kv {
|
|
||||||
tagSet = tagSet.With(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Report(vu modules.VU, metric *metrics.Metric, value float64) {
|
func Report(vu modules.VU, metric *metrics.Metric, value float64) {
|
||||||
metrics.PushIfNotDone(vu.Context(), vu.State().Samples, metrics.Sample{
|
metrics.PushIfNotDone(vu.Context(), vu.State().Samples, metrics.Sample{
|
||||||
TimeSeries: metrics.TimeSeries{
|
TimeSeries: metrics.TimeSeries{
|
||||||
Metric: metric,
|
Metric: metric,
|
||||||
Tags: tagSet,
|
|
||||||
},
|
},
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Value: value,
|
Value: value,
|
||||||
|
@ -60,11 +22,9 @@ func ReportDataReceived(vu modules.VU, value float64) {
|
||||||
metrics.Sample{
|
metrics.Sample{
|
||||||
TimeSeries: metrics.TimeSeries{
|
TimeSeries: metrics.TimeSeries{
|
||||||
Metric: &metrics.Metric{},
|
Metric: &metrics.Metric{},
|
||||||
Tags: tagSet,
|
|
||||||
},
|
},
|
||||||
Value: value,
|
Value: value,
|
||||||
Time: time.Now(),
|
Time: time.Now()},
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +34,8 @@ func ReportDataSent(vu modules.VU, value float64) {
|
||||||
metrics.Sample{
|
metrics.Sample{
|
||||||
TimeSeries: metrics.TimeSeries{
|
TimeSeries: metrics.TimeSeries{
|
||||||
Metric: &metrics.Metric{},
|
Metric: &metrics.Metric{},
|
||||||
Tags: tagSet,
|
|
||||||
},
|
},
|
||||||
Value: value,
|
Value: value,
|
||||||
Time: time.Now(),
|
Time: time.Now()},
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,222 +1,206 @@
|
||||||
import { sleep } from 'k6';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import { SharedArray } from 'k6/data';
|
|
||||||
import exec from 'k6/execution';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
|
||||||
import native from 'k6/x/frostfs/native';
|
import native from 'k6/x/frostfs/native';
|
||||||
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import { SharedArray } from 'k6/data';
|
||||||
|
import { sleep } from 'k6';
|
||||||
import { newGenerator } from './libs/datagen.js';
|
|
||||||
import { parseEnv } from './libs/env-parser.js';
|
|
||||||
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_list = new SharedArray(
|
const obj_list = new SharedArray('obj_list', function () {
|
||||||
'obj_list',
|
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
||||||
function () { return JSON.parse(open(__ENV.PREGEN_JSON)).objects; });
|
});
|
||||||
|
|
||||||
const container_list = new SharedArray(
|
const container_list = new SharedArray('container_list', function () {
|
||||||
'container_list',
|
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
||||||
function () { return JSON.parse(open(__ENV.PREGEN_JSON)).containers; });
|
});
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
// Select random gRPC endpoint for current VU
|
// Select random gRPC endpoint for current VU
|
||||||
const grpc_endpoints = __ENV.GRPC_ENDPOINTS.split(',');
|
const grpc_endpoints = __ENV.GRPC_ENDPOINTS.split(',');
|
||||||
const grpc_endpoint =
|
const grpc_endpoint = grpc_endpoints[Math.floor(Math.random() * grpc_endpoints.length)];
|
||||||
grpc_endpoints[Math.floor(Math.random() * grpc_endpoints.length)];
|
const grpc_client = native.connect(grpc_endpoint, '',
|
||||||
const grpc_client = native.connect(
|
__ENV.DIAL_TIMEOUT ? parseInt(__ENV.DIAL_TIMEOUT) : 5,
|
||||||
grpc_endpoint, '', __ENV.DIAL_TIMEOUT ? parseInt(__ENV.DIAL_TIMEOUT) : 5,
|
__ENV.STREAM_TIMEOUT ? parseInt(__ENV.STREAM_TIMEOUT) : 60,
|
||||||
__ENV.STREAM_TIMEOUT ? parseInt(__ENV.STREAM_TIMEOUT) : 60,
|
__ENV.PREPARE_LOCALLY ? __ENV.PREPARE_LOCALLY.toLowerCase() === "true" : false);
|
||||||
__ENV.PREPARE_LOCALLY ? __ENV.PREPARE_LOCALLY.toLowerCase() === 'true' : false,
|
const log = logging.new().withField("endpoint", grpc_endpoint);
|
||||||
1024 * parseInt(__ENV.MAX_OBJECT_SIZE || '0'));
|
|
||||||
const log = logging.new().withField('endpoint', grpc_endpoint);
|
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
let obj_to_delete_selector = undefined;
|
||||||
|
if (registry_enabled && delete_age) {
|
||||||
|
obj_to_delete_selector = registry.getSelector(
|
||||||
|
__ENV.REGISTRY_FILE,
|
||||||
|
"obj_to_delete",
|
||||||
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
|
{
|
||||||
|
status: "created",
|
||||||
|
age: delete_age,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_age = __ENV.READ_AGE ? parseInt(__ENV.READ_AGE) : 10;
|
|
||||||
let obj_to_read_selector = undefined;
|
let obj_to_read_selector = undefined;
|
||||||
if (registry_enabled) {
|
if (registry_enabled) {
|
||||||
obj_to_read_selector = registry.getLoopedSelector(
|
obj_to_read_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_read',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_read",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
age: read_age,
|
{
|
||||||
})
|
status: "created",
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE), __ENV.PAYLOAD_TYPE || "");
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
||||||
const write_grpc_chunk_size = 1024 * parseInt(__ENV.GRPC_CHUNK_SIZE || '0')
|
const write_grpc_chunk_size = 1024 * parseInt(__ENV.GRPC_CHUNK_SIZE || '0')
|
||||||
const generator = newGenerator(write_vu_count > 0);
|
|
||||||
if (write_vu_count > 0) {
|
if (write_vu_count > 0) {
|
||||||
scenarios.write = {
|
scenarios.write = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: write_vu_count,
|
vus: write_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_write',
|
exec: 'obj_write',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
|
||||||
let obj_to_delete_selector = undefined;
|
|
||||||
let obj_to_delete_exit_on_null = undefined;
|
|
||||||
if (registry_enabled && delete_age) {
|
|
||||||
obj_to_delete_exit_on_null = write_vu_count == 0;
|
|
||||||
|
|
||||||
let constructor = obj_to_delete_exit_on_null ? registry.getOneshotSelector
|
|
||||||
: registry.getSelector;
|
|
||||||
|
|
||||||
obj_to_delete_selector =
|
|
||||||
constructor(__ENV.REGISTRY_FILE, 'obj_to_delete',
|
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
|
||||||
status: 'created',
|
|
||||||
age: delete_age,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_vu_count = parseInt(__ENV.READERS || '0');
|
const read_vu_count = parseInt(__ENV.READERS || '0');
|
||||||
if (read_vu_count > 0) {
|
if (read_vu_count > 0) {
|
||||||
scenarios.read = {
|
scenarios.read = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: read_vu_count,
|
vus: read_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_read',
|
exec: 'obj_read',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const delete_vu_count = parseInt(__ENV.DELETERS || '0');
|
const delete_vu_count = parseInt(__ENV.DELETERS || '0');
|
||||||
if (delete_vu_count > 0) {
|
if (delete_vu_count > 0) {
|
||||||
if (!obj_to_delete_selector) {
|
if (!obj_to_delete_selector) {
|
||||||
throw new Error(
|
throw new Error('Positive DELETE worker number without a proper object selector');
|
||||||
'Positive DELETE worker number without a proper object selector');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scenarios.delete = {
|
scenarios.delete = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: delete_vu_count,
|
vus: delete_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_delete',
|
exec: 'obj_delete',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_vu_count = write_vu_count + read_vu_count + delete_vu_count;
|
const total_vu_count = write_vu_count + read_vu_count + delete_vu_count;
|
||||||
|
|
||||||
console.log(`Pregenerated containers: ${container_list.length}`);
|
console.log(`Pregenerated containers: ${container_list.length}`);
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
console.log(`Pregenerated read object size: ${read_size}`);
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
||||||
console.log(`Reading VUs: ${read_vu_count}`);
|
console.log(`Reading VUs: ${read_vu_count}`);
|
||||||
console.log(`Writing VUs: ${write_vu_count}`);
|
console.log(`Writing VUs: ${write_vu_count}`);
|
||||||
console.log(`Deleting VUs: ${delete_vu_count}`);
|
console.log(`Deleting VUs: ${delete_vu_count}`);
|
||||||
console.log(`Total VUs: ${total_vu_count}`);
|
console.log(`Total VUs: ${total_vu_count}`);
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
const start_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load started at: ${Date(start_timestamp).toString()}`)
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
const end_timestamp = Date.now()
|
const end_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load finished at: ${Date(end_timestamp).toString()}`)
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write() {
|
export function obj_write() {
|
||||||
if (__ENV.SLEEP_WRITE) {
|
if (__ENV.SLEEP_WRITE) {
|
||||||
sleep(__ENV.SLEEP_WRITE);
|
sleep(__ENV.SLEEP_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = { unique_header: uuidv4() };
|
const headers = {
|
||||||
const container =
|
unique_header: uuidv4()
|
||||||
container_list[Math.floor(Math.random() * container_list.length)];
|
};
|
||||||
|
const container = container_list[Math.floor(Math.random() * container_list.length)];
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const { payload, hash } = generator.genPayload(registry_enabled);
|
||||||
const resp =
|
const resp = grpc_client.put(container, headers, payload, write_grpc_chunk_size);
|
||||||
grpc_client.put(container, headers, payload, write_grpc_chunk_size);
|
if (!resp.success) {
|
||||||
if (!resp.success) {
|
log.withField("cid", container).error(resp.error);
|
||||||
log.withField('cid', container).error(resp.error);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject(container, resp.object_id, '', '', payload.hash());
|
obj_registry.addObject(container, resp.object_id, "", "", hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_read() {
|
export function obj_read() {
|
||||||
if (__ENV.SLEEP_READ) {
|
if (__ENV.SLEEP_READ) {
|
||||||
sleep(__ENV.SLEEP_READ);
|
sleep(__ENV.SLEEP_READ);
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_to_read_selector) {
|
|
||||||
const obj = obj_to_read_selector.nextObject();
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const resp = grpc_client.get(obj.c_id, obj.o_id)
|
|
||||||
|
if(obj_to_read_selector) {
|
||||||
|
const obj = obj_to_read_selector.nextObject();
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resp = grpc_client.get(obj.c_id, obj.o_id)
|
||||||
|
if (!resp.success) {
|
||||||
|
log.withFields({cid: obj.c_id, oid: obj.o_id}).error(resp.error);
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
||||||
|
const resp = grpc_client.get(obj.container, obj.object)
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({ cid: obj.c_id, oid: obj.o_id }).error(resp.error);
|
log.withFields({cid: obj.container, oid: obj.object}).error(resp.error);
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
|
||||||
const resp = grpc_client.get(obj.container, obj.object)
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({ cid: obj.container, oid: obj.object }).error(resp.error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_delete() {
|
export function obj_delete() {
|
||||||
if (__ENV.SLEEP_DELETE) {
|
if (__ENV.SLEEP_DELETE) {
|
||||||
sleep(__ENV.SLEEP_DELETE);
|
sleep(__ENV.SLEEP_DELETE);
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_to_delete_selector.nextObject();
|
|
||||||
if (!obj) {
|
|
||||||
if (obj_to_delete_exit_on_null) {
|
|
||||||
exec.test.abort("No more objects to select");
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resp = grpc_client.delete(obj.c_id, obj.o_id);
|
const obj = obj_to_delete_selector.nextObject();
|
||||||
if (!resp.success) {
|
if (!obj) {
|
||||||
// Log errors except (2052 - object already deleted)
|
return;
|
||||||
log.withFields({ cid: obj.c_id, oid: obj.o_id }).error(resp.error);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj_registry.deleteObject(obj.id);
|
const resp = grpc_client.delete(obj.c_id, obj.o_id);
|
||||||
|
if (!resp.success) {
|
||||||
|
// Log errors except (2052 - object already deleted)
|
||||||
|
log.withFields({cid: obj.c_id, oid: obj.o_id}).error(resp.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_registry.deleteObject(obj.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +1,68 @@
|
||||||
import { sleep } from 'k6';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import { SharedArray } from 'k6/data';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
|
||||||
import native from 'k6/x/frostfs/native';
|
import native from 'k6/x/frostfs/native';
|
||||||
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import { SharedArray } from 'k6/data';
|
||||||
|
import { sleep } from 'k6';
|
||||||
import { newGenerator } from './libs/datagen.js';
|
|
||||||
import { parseEnv } from './libs/env-parser.js';
|
|
||||||
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_list = new SharedArray('obj_list', function () {
|
const obj_list = new SharedArray('obj_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
||||||
});
|
});
|
||||||
|
|
||||||
const container_list = new SharedArray('container_list', function () {
|
const container_list = new SharedArray('container_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
||||||
});
|
});
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
// Select random gRPC endpoint for current VU
|
// Select random gRPC endpoint for current VU
|
||||||
const grpc_endpoints = __ENV.GRPC_ENDPOINTS.split(',');
|
const grpc_endpoints = __ENV.GRPC_ENDPOINTS.split(',');
|
||||||
const grpc_endpoint =
|
const grpc_endpoint = grpc_endpoints[Math.floor(Math.random() * grpc_endpoints.length)];
|
||||||
grpc_endpoints[Math.floor(Math.random() * grpc_endpoints.length)];
|
const grpc_client = native.connect(grpc_endpoint, '',
|
||||||
const grpc_client = native.connect(
|
__ENV.DIAL_TIMEOUT ? parseInt(__ENV.DIAL_TIMEOUT) : 5,
|
||||||
grpc_endpoint, '', __ENV.DIAL_TIMEOUT ? parseInt(__ENV.DIAL_TIMEOUT) : 5,
|
__ENV.STREAM_TIMEOUT ? parseInt(__ENV.STREAM_TIMEOUT) : 60,
|
||||||
__ENV.STREAM_TIMEOUT ? parseInt(__ENV.STREAM_TIMEOUT) : 60,
|
__ENV.PREPARE_LOCALLY ? __ENV.PREPARE_LOCALLY.toLowerCase() === "true" : false);
|
||||||
__ENV.PREPARE_LOCALLY ? __ENV.PREPARE_LOCALLY.toLowerCase() === 'true' : false,
|
const log = logging.new().withField("endpoint", grpc_endpoint);
|
||||||
1024 * parseInt(__ENV.MAX_OBJECT_SIZE || '0'));
|
|
||||||
const log = logging.new().withField('endpoint', grpc_endpoint);
|
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
||||||
let obj_to_delete_selector = undefined;
|
let obj_to_delete_selector = undefined;
|
||||||
if (registry_enabled && delete_age) {
|
if (registry_enabled && delete_age) {
|
||||||
obj_to_delete_selector = registry.getSelector(
|
obj_to_delete_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_delete',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_delete",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
age: delete_age,
|
{
|
||||||
});
|
status: "created",
|
||||||
|
age: delete_age,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_age = __ENV.READ_AGE ? parseInt(__ENV.READ_AGE) : 10;
|
|
||||||
let obj_to_read_selector = undefined;
|
let obj_to_read_selector = undefined;
|
||||||
if (registry_enabled) {
|
if (registry_enabled) {
|
||||||
obj_to_read_selector = registry.getLoopedSelector(
|
obj_to_read_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_read',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_read",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
age: read_age,
|
{
|
||||||
})
|
status: "created",
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE));
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const time_unit = __ENV.TIME_UNIT || '1s';
|
const time_unit = __ENV.TIME_UNIT || '1s';
|
||||||
|
@ -73,166 +70,161 @@ const pre_alloc_write_vus = parseInt(__ENV.PRE_ALLOC_WRITERS || '0');
|
||||||
const max_write_vus = parseInt(__ENV.MAX_WRITERS || pre_alloc_write_vus);
|
const max_write_vus = parseInt(__ENV.MAX_WRITERS || pre_alloc_write_vus);
|
||||||
const write_rate = parseInt(__ENV.WRITE_RATE || '0');
|
const write_rate = parseInt(__ENV.WRITE_RATE || '0');
|
||||||
const write_grpc_chunk_size = 1024 * parseInt(__ENV.GRPC_CHUNK_SIZE || '0')
|
const write_grpc_chunk_size = 1024 * parseInt(__ENV.GRPC_CHUNK_SIZE || '0')
|
||||||
const generator = newGenerator(write_rate > 0);
|
|
||||||
if (write_rate > 0) {
|
if (write_rate > 0) {
|
||||||
scenarios.write = {
|
scenarios.write = {
|
||||||
executor: 'constant-arrival-rate',
|
executor: 'constant-arrival-rate',
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
preAllocatedVUs: pre_alloc_write_vus,
|
preAllocatedVUs: pre_alloc_write_vus,
|
||||||
maxVUs: max_write_vus,
|
maxVUs: max_write_vus,
|
||||||
rate: write_rate,
|
rate: write_rate,
|
||||||
timeUnit: time_unit,
|
timeUnit: time_unit,
|
||||||
exec: 'obj_write',
|
exec: 'obj_write',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const pre_alloc_read_vus = parseInt(__ENV.PRE_ALLOC_READERS || '0');
|
const pre_alloc_read_vus = parseInt(__ENV.PRE_ALLOC_READERS || '0');
|
||||||
const max_read_vus = parseInt(__ENV.MAX_READERS || pre_alloc_read_vus);
|
const max_read_vus = parseInt(__ENV.MAX_READERS || pre_alloc_read_vus);
|
||||||
const read_rate = parseInt(__ENV.READ_RATE || '0');
|
const read_rate = parseInt(__ENV.READ_RATE || '0');
|
||||||
if (read_rate > 0) {
|
if (read_rate > 0) {
|
||||||
scenarios.read = {
|
scenarios.read = {
|
||||||
executor: 'constant-arrival-rate',
|
executor: 'constant-arrival-rate',
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
preAllocatedVUs: pre_alloc_write_vus,
|
preAllocatedVUs: pre_alloc_write_vus,
|
||||||
maxVUs: max_read_vus,
|
maxVUs: max_read_vus,
|
||||||
rate: read_rate,
|
rate: read_rate,
|
||||||
timeUnit: time_unit,
|
timeUnit: time_unit,
|
||||||
exec: 'obj_read',
|
exec: 'obj_read',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const pre_alloc_delete_vus = parseInt(__ENV.PRE_ALLOC_DELETERS || '0');
|
const pre_alloc_delete_vus = parseInt(__ENV.PRE_ALLOC_DELETERS || '0');
|
||||||
const max_delete_vus = parseInt(__ENV.MAX_DELETERS || pre_alloc_write_vus);
|
const max_delete_vus = parseInt(__ENV.MAX_DELETERS || pre_alloc_write_vus);
|
||||||
const delete_rate = parseInt(__ENV.DELETE_RATE || '0');
|
const delete_rate = parseInt(__ENV.DELETE_RATE || '0');
|
||||||
if (delete_rate > 0) {
|
if (delete_rate > 0) {
|
||||||
if (!obj_to_delete_selector) {
|
if (!obj_to_delete_selector) {
|
||||||
throw new Error(
|
throw new Error('Positive DELETE worker number without a proper object selector');
|
||||||
'Positive DELETE worker number without a proper object selector');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scenarios.delete = {
|
scenarios.delete = {
|
||||||
executor: 'constant-arrival-rate',
|
executor: 'constant-arrival-rate',
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
preAllocatedVUs: pre_alloc_delete_vus,
|
preAllocatedVUs: pre_alloc_delete_vus,
|
||||||
maxVUs: max_delete_vus,
|
maxVUs: max_delete_vus,
|
||||||
rate: delete_rate,
|
rate: delete_rate,
|
||||||
timeUnit: time_unit,
|
timeUnit: time_unit,
|
||||||
exec: 'obj_delete',
|
exec: 'obj_delete',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_pre_allocated_vu_count =
|
const total_pre_allocated_vu_count = pre_alloc_write_vus + pre_alloc_read_vus + pre_alloc_delete_vus;
|
||||||
pre_alloc_write_vus + pre_alloc_read_vus + pre_alloc_delete_vus;
|
const total_max_vu_count = max_read_vus + max_write_vus + max_delete_vus
|
||||||
const total_max_vu_count = max_read_vus + max_write_vus + max_delete_vus
|
|
||||||
|
|
||||||
console.log(`Pregenerated containers: ${container_list.length}`);
|
console.log(`Pregenerated containers: ${container_list.length}`);
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
console.log(`Pregenerated read object size: ${read_size}`);
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
||||||
console.log(`Pre allocated reading VUs: ${pre_alloc_read_vus}`);
|
console.log(`Pre allocated reading VUs: ${pre_alloc_read_vus}`);
|
||||||
console.log(`Pre allocated writing VUs: ${pre_alloc_write_vus}`);
|
console.log(`Pre allocated writing VUs: ${pre_alloc_write_vus}`);
|
||||||
console.log(`Pre allocated deleting VUs: ${pre_alloc_delete_vus}`);
|
console.log(`Pre allocated deleting VUs: ${pre_alloc_delete_vus}`);
|
||||||
console.log(`Total pre allocated VUs: ${total_pre_allocated_vu_count}`);
|
console.log(`Total pre allocated VUs: ${total_pre_allocated_vu_count}`);
|
||||||
console.log(`Max reading VUs: ${max_read_vus}`);
|
console.log(`Max reading VUs: ${max_read_vus}`);
|
||||||
console.log(`Max writing VUs: ${max_write_vus}`);
|
console.log(`Max writing VUs: ${max_write_vus}`);
|
||||||
console.log(`Max deleting VUs: ${max_delete_vus}`);
|
console.log(`Max deleting VUs: ${max_delete_vus}`);
|
||||||
console.log(`Total max VUs: ${total_max_vu_count}`);
|
console.log(`Total max VUs: ${total_max_vu_count}`);
|
||||||
console.log(`Time unit: ${time_unit}`);
|
console.log(`Time unit: ${time_unit}`);
|
||||||
console.log(`Read rate: ${read_rate}`);
|
console.log(`Read rate: ${read_rate}`);
|
||||||
console.log(`Writing rate: ${write_rate}`);
|
console.log(`Writing rate: ${write_rate}`);
|
||||||
console.log(`Delete rate: ${delete_rate}`);
|
console.log(`Delete rate: ${delete_rate}`);
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
const start_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load started at: ${Date(start_timestamp).toString()}`)
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
const end_timestamp = Date.now()
|
const end_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load finished at: ${Date(end_timestamp).toString()}`)
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write() {
|
export function obj_write() {
|
||||||
if (__ENV.SLEEP_WRITE) {
|
if (__ENV.SLEEP_WRITE) {
|
||||||
sleep(__ENV.SLEEP_WRITE);
|
sleep(__ENV.SLEEP_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = { unique_header: uuidv4() };
|
const headers = {
|
||||||
const container =
|
unique_header: uuidv4()
|
||||||
container_list[Math.floor(Math.random() * container_list.length)];
|
};
|
||||||
|
const container = container_list[Math.floor(Math.random() * container_list.length)];
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const { payload, hash } = generator.genPayload(registry_enabled);
|
||||||
const resp =
|
const resp = grpc_client.put(container, headers, payload, write_grpc_chunk_size);
|
||||||
grpc_client.put(container, headers, payload, write_grpc_chunk_size);
|
if (!resp.success) {
|
||||||
if (!resp.success) {
|
log.withField("cid", container).error(resp.error);
|
||||||
log.withField('cid', container).error(resp.error);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject(container, resp.object_id, '', '', payload.hash());
|
obj_registry.addObject(container, resp.object_id, "", "", hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_read() {
|
export function obj_read() {
|
||||||
if (__ENV.SLEEP_READ) {
|
if (__ENV.SLEEP_READ) {
|
||||||
sleep(__ENV.SLEEP_READ);
|
sleep(__ENV.SLEEP_READ);
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_to_read_selector) {
|
|
||||||
const obj = obj_to_read_selector.nextObject();
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const resp = grpc_client.get(obj.c_id, obj.o_id)
|
|
||||||
|
if(obj_to_read_selector) {
|
||||||
|
const obj = obj_to_read_selector.nextObject();
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resp = grpc_client.get(obj.c_id, obj.o_id)
|
||||||
|
if (!resp.success) {
|
||||||
|
log.withFields({cid: obj.c_id, oid: obj.o_id}).error(resp.error);
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
||||||
|
const resp = grpc_client.get(obj.container, obj.object)
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({ cid: obj.c_id, oid: obj.o_id }).error(resp.error);
|
log.withFields({cid: obj.container, oid: obj.object}).error(resp.error);
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
|
||||||
const resp = grpc_client.get(obj.container, obj.object)
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({ cid: obj.container, oid: obj.object }).error(resp.error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_delete() {
|
export function obj_delete() {
|
||||||
if (__ENV.SLEEP_DELETE) {
|
if (__ENV.SLEEP_DELETE) {
|
||||||
sleep(__ENV.SLEEP_DELETE);
|
sleep(__ENV.SLEEP_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj = obj_to_delete_selector.nextObject();
|
const obj = obj_to_delete_selector.nextObject();
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = grpc_client.delete(obj.c_id, obj.o_id);
|
const resp = grpc_client.delete(obj.c_id, obj.o_id);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
// Log errors except (2052 - object already deleted)
|
// Log errors except (2052 - object already deleted)
|
||||||
log.withFields({ cid: obj.c_id, oid: obj.o_id }).error(resp.error);
|
log.withFields({cid: obj.c_id, oid: obj.o_id}).error(resp.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj_registry.deleteObject(obj.id);
|
obj_registry.deleteObject(obj.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,143 +1,128 @@
|
||||||
import {sleep} from 'k6';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import {SharedArray} from 'k6/data';
|
|
||||||
import http from 'k6/http';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import http from 'k6/http';
|
||||||
|
import { SharedArray } from 'k6/data';
|
||||||
import {newGenerator} from './libs/datagen.js';
|
import { sleep } from 'k6';
|
||||||
import {parseEnv} from './libs/env-parser.js';
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
import {uuidv4} from './libs/k6-utils-1.4.0.js';
|
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_list = new SharedArray('obj_list', function() {
|
const obj_list = new SharedArray('obj_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
||||||
});
|
});
|
||||||
|
|
||||||
const container_list = new SharedArray('container_list', function() {
|
const container_list = new SharedArray('container_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
||||||
});
|
});
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
// Select random HTTP endpoint for current VU
|
// Select random HTTP endpoint for current VU
|
||||||
const http_endpoints = __ENV.HTTP_ENDPOINTS.split(',');
|
const http_endpoints = __ENV.HTTP_ENDPOINTS.split(',');
|
||||||
const http_endpoint =
|
const http_endpoint = http_endpoints[Math.floor(Math.random() * http_endpoints.length)];
|
||||||
http_endpoints[Math.floor(Math.random() * http_endpoints.length)];
|
const log = logging.new().withField("endpoint", http_endpoint);
|
||||||
const log = logging.new().withField('endpoint', http_endpoint);
|
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE), __ENV.PAYLOAD_TYPE || "");
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
||||||
const generator = newGenerator(write_vu_count > 0);
|
|
||||||
if (write_vu_count > 0) {
|
if (write_vu_count > 0) {
|
||||||
scenarios.write = {
|
scenarios.write = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: write_vu_count,
|
vus: write_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_write',
|
exec: 'obj_write',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_vu_count = parseInt(__ENV.READERS || '0');
|
const read_vu_count = parseInt(__ENV.READERS || '0');
|
||||||
if (read_vu_count > 0) {
|
if (read_vu_count > 0) {
|
||||||
scenarios.read = {
|
scenarios.read = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: read_vu_count,
|
vus: read_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_read',
|
exec: 'obj_read',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_vu_count = write_vu_count + read_vu_count;
|
const total_vu_count = write_vu_count + read_vu_count;
|
||||||
|
|
||||||
console.log(`Pregenerated containers: ${container_list.length}`);
|
console.log(`Pregenerated containers: ${container_list.length}`);
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
console.log(`Pregenerated read object size: ${read_size}`);
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
||||||
console.log(`Reading VUs: ${read_vu_count}`);
|
console.log(`Reading VUs: ${read_vu_count}`);
|
||||||
console.log(`Writing VUs: ${write_vu_count}`);
|
console.log(`Writing VUs: ${write_vu_count}`);
|
||||||
console.log(`Total VUs: ${total_vu_count}`);
|
console.log(`Total VUs: ${total_vu_count}`);
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
const start_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load started at: ${Date(start_timestamp).toString()}`)
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
const end_timestamp = Date.now()
|
const end_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load finished at: ${Date(end_timestamp).toString()}`)
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, {indent: ' ', enableColors: false}),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write() {
|
export function obj_write() {
|
||||||
if (__ENV.SLEEP_WRITE) {
|
if (__ENV.SLEEP_WRITE) {
|
||||||
sleep(__ENV.SLEEP_WRITE);
|
sleep(__ENV.SLEEP_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const container =
|
const container = container_list[Math.floor(Math.random() * container_list.length)];
|
||||||
container_list[Math.floor(Math.random() * container_list.length)];
|
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const { payload, hash } = generator.genPayload(registry_enabled);
|
||||||
const data = {
|
const data = {
|
||||||
field: uuidv4(),
|
field: uuidv4(),
|
||||||
// Because we use `file` wrapping and it is not straightforward to use
|
file: http.file(payload, "random.data"),
|
||||||
// streams here,
|
};
|
||||||
// `-e STREAMING=1` has no effect for this scenario.
|
|
||||||
file: http.file(payload.bytes(), 'random.data'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const resp = http.post(`http://${http_endpoint}/upload/${container}`, data);
|
const resp = http.post(`http://${http_endpoint}/upload/${container}`, data);
|
||||||
if (resp.status != 200) {
|
if (resp.status != 200) {
|
||||||
log.withFields({status: resp.status, cid: container}).error(resp.error);
|
log.withFields({status: resp.status, cid: container}).error(resp.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const object_id = JSON.parse(resp.body).object_id;
|
const object_id = JSON.parse(resp.body).object_id;
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject(container, object_id, '', '', payload.hash());
|
obj_registry.addObject(container, object_id, "", "", hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_read() {
|
export function obj_read() {
|
||||||
if (__ENV.SLEEP_READ) {
|
if (__ENV.SLEEP_READ) {
|
||||||
sleep(__ENV.SLEEP_READ);
|
sleep(__ENV.SLEEP_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
||||||
const resp =
|
const resp = http.get(`http://${http_endpoint}/get/${obj.container}/${obj.object}`);
|
||||||
http.get(`http://${http_endpoint}/get/${obj.container}/${obj.object}`);
|
if (resp.status != 200) {
|
||||||
if (resp.status != 200) {
|
log.withFields({status: resp.status, cid: obj.container, oid: obj.object}).error(resp.error);
|
||||||
log.withFields({status: resp.status, cid: obj.container, oid: obj.object})
|
}
|
||||||
.error(resp.error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import datagen from 'k6/x/frostfs/datagen';
|
|
||||||
|
|
||||||
export function newGenerator(condition) {
|
|
||||||
if (condition) {
|
|
||||||
return datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE), __ENV.PAYLOAD_TYPE || "", !!__ENV.STREAMING);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { uuidv4 } from './k6-utils-1.4.0.js';
|
|
||||||
|
|
||||||
export function generateS3Key() {
|
|
||||||
let width = parseInt(__ENV.DIR_WIDTH || '0');
|
|
||||||
let height = parseInt(__ENV.DIR_HEIGHT || '0');
|
|
||||||
|
|
||||||
let key = ''
|
|
||||||
if (width > 0 && height > 0) {
|
|
||||||
for (let index = 0; index < height; index++) {
|
|
||||||
const w = Math.floor(Math.random() * width) + 1;
|
|
||||||
key = key + 'dir' + w + '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
key += objName();
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
const asciiLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
|
||||||
|
|
||||||
function objName() {
|
|
||||||
if (__ENV.OBJ_NAME) {
|
|
||||||
return __ENV.OBJ_NAME;
|
|
||||||
}
|
|
||||||
const length = parseInt(__ENV.OBJ_NAME_LENGTH || '0');
|
|
||||||
if (length > 0) {
|
|
||||||
let name = "";
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
name += asciiLetters.charAt(Math.floor(Math.random() * asciiLetters.length));
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return uuidv4();
|
|
||||||
}
|
|
|
@ -1,177 +1,163 @@
|
||||||
import {SharedArray} from 'k6/data';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import exec from 'k6/execution';
|
|
||||||
import local from 'k6/x/frostfs/local';
|
import local from 'k6/x/frostfs/local';
|
||||||
import logging from 'k6/x/frostfs/logging';
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import { SharedArray } from 'k6/data';
|
||||||
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
import {newGenerator} from './libs/datagen.js';
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
import {parseEnv} from './libs/env-parser.js';
|
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
||||||
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
|
||||||
import {uuidv4} from './libs/k6-utils-1.4.0.js';
|
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_list = new SharedArray('obj_list', function() {
|
const obj_list = new SharedArray('obj_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
||||||
});
|
});
|
||||||
|
|
||||||
const container_list = new SharedArray('container_list', function() {
|
const container_list = new SharedArray('container_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
||||||
});
|
});
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
const config_file = __ENV.CONFIG_FILE;
|
const config_file = __ENV.CONFIG_FILE;
|
||||||
const config_dir = __ENV.CONFIG_DIR;
|
|
||||||
const debug_logger = (__ENV.DEBUG_LOGGER || 'false') == 'true';
|
const debug_logger = (__ENV.DEBUG_LOGGER || 'false') == 'true';
|
||||||
const max_total_size_gb =
|
const local_client = local.connect(config_file, '', debug_logger);
|
||||||
__ENV.MAX_TOTAL_SIZE_GB ? parseInt(__ENV.MAX_TOTAL_SIZE_GB) : 0;
|
const log = logging.new().withField("config", config_file);
|
||||||
const local_client =
|
|
||||||
local.connect(config_file, config_dir, '', debug_logger, max_total_size_gb);
|
|
||||||
const log = logging.new().withFields(
|
|
||||||
{'config_file': config_file, 'config_dir': config_dir});
|
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
||||||
let obj_to_delete_selector = undefined;
|
let obj_to_delete_selector = undefined;
|
||||||
if (registry_enabled && delete_age) {
|
if (registry_enabled && delete_age) {
|
||||||
obj_to_delete_selector = registry.getSelector(
|
obj_to_delete_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_delete',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_delete",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
age: delete_age,
|
{
|
||||||
});
|
status: "created",
|
||||||
|
age: delete_age,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE));
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
||||||
const generator = newGenerator(write_vu_count > 0);
|
|
||||||
if (write_vu_count > 0) {
|
if (write_vu_count > 0) {
|
||||||
scenarios.write = {
|
scenarios.write = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: write_vu_count,
|
vus: write_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_write',
|
exec: 'obj_write',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_vu_count = parseInt(__ENV.READERS || '0');
|
const read_vu_count = parseInt(__ENV.READERS || '0');
|
||||||
if (read_vu_count > 0) {
|
if (read_vu_count > 0) {
|
||||||
scenarios.read = {
|
scenarios.read = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: read_vu_count,
|
vus: read_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_read',
|
exec: 'obj_read',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const delete_vu_count = parseInt(__ENV.DELETERS || '0');
|
const delete_vu_count = parseInt(__ENV.DELETERS || '0');
|
||||||
if (delete_vu_count > 0) {
|
if (delete_vu_count > 0) {
|
||||||
if (!obj_to_delete_selector) {
|
if (!obj_to_delete_selector) {
|
||||||
throw new Error(
|
throw new Error('Positive DELETE worker number without a proper object selector');
|
||||||
'Positive DELETE worker number without a proper object selector');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scenarios.delete = {
|
scenarios.delete = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: delete_vu_count,
|
vus: delete_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_delete',
|
exec: 'obj_delete',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_vu_count = write_vu_count + read_vu_count + delete_vu_count;
|
const total_vu_count = write_vu_count + read_vu_count + delete_vu_count;
|
||||||
|
|
||||||
console.log(`Pregenerated containers: ${container_list.length}`);
|
console.log(`Pregenerated containers: ${container_list.length}`);
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
console.log(`Pregenerated read object size: ${read_size}`);
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
||||||
console.log(`Reading VUs: ${read_vu_count}`);
|
console.log(`Reading VUs: ${read_vu_count}`);
|
||||||
console.log(`Writing VUs: ${write_vu_count}`);
|
console.log(`Writing VUs: ${write_vu_count}`);
|
||||||
console.log(`Deleting VUs: ${delete_vu_count}`);
|
console.log(`Deleting VUs: ${delete_vu_count}`);
|
||||||
console.log(`Total VUs: ${total_vu_count}`);
|
console.log(`Total VUs: ${total_vu_count}`);
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
const start_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load started at: ${Date(start_timestamp).toString()}`)
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
const end_timestamp = Date.now()
|
const end_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load finished at: ${Date(end_timestamp).toString()}`)
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, {indent: ' ', enableColors: false}),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write() {
|
export function obj_write() {
|
||||||
const headers = {unique_header: uuidv4()};
|
const headers = {
|
||||||
const container =
|
unique_header: uuidv4()
|
||||||
container_list[Math.floor(Math.random() * container_list.length)];
|
};
|
||||||
|
const container = container_list[Math.floor(Math.random() * container_list.length)];
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const { payload, hash } = generator.genPayload(registry_enabled);
|
||||||
const resp = local_client.put(container, headers, payload);
|
const resp = local_client.put(container, headers, payload);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
if (resp.abort) {
|
log.withField("cid", container).error(resp.error);
|
||||||
exec.test.abort(resp.error);
|
return;
|
||||||
}
|
}
|
||||||
log.withField('cid', container).error(resp.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject(container, resp.object_id, '', '', payload.hash());
|
obj_registry.addObject(container, resp.object_id, "", "", hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_read() {
|
export function obj_read() {
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
||||||
const resp = local_client.get(obj.container, obj.object)
|
const resp = local_client.get(obj.container, obj.object)
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({cid: obj.container, oid: obj.object}).error(resp.error);
|
log.withFields({cid: obj.container, oid: obj.object}).error(resp.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_delete() {
|
export function obj_delete() {
|
||||||
const obj = obj_to_delete_selector.nextObject();
|
const obj = obj_to_delete_selector.nextObject();
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = local_client.delete(obj.c_id, obj.o_id);
|
const resp = local_client.delete(obj.c_id, obj.o_id);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
// Log errors except (2052 - object already deleted)
|
// Log errors except (2052 - object already deleted)
|
||||||
log.withFields({cid: obj.c_id, oid: obj.o_id}).error(resp.error);
|
log.withFields({cid: obj.c_id, oid: obj.o_id}).error(resp.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj_registry.deleteObject(obj.id);
|
obj_registry.deleteObject(obj.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,15 @@ import uuid
|
||||||
from helpers.cmd import execute_cmd, log
|
from helpers.cmd import execute_cmd, log
|
||||||
|
|
||||||
|
|
||||||
def create_bucket(endpoint, versioning, location, acl, no_verify_ssl):
|
def create_bucket(endpoint, versioning, location, no_verify_ssl):
|
||||||
configuration = ""
|
|
||||||
if location:
|
if location:
|
||||||
configuration = f"--create-bucket-configuration 'LocationConstraint={location}'"
|
location = f"--create-bucket-configuration 'LocationConstraint={location}'"
|
||||||
if acl:
|
|
||||||
acl = f"--acl {acl}"
|
|
||||||
|
|
||||||
bucket_name = str(uuid.uuid4())
|
bucket_name = str(uuid.uuid4())
|
||||||
no_verify_ssl_str = "--no-verify-ssl" if no_verify_ssl else ""
|
no_verify_ssl_str = "--no-verify-ssl" if no_verify_ssl else ""
|
||||||
cmd_line = f"aws {no_verify_ssl_str} s3api create-bucket --bucket {bucket_name} " \
|
cmd_line = f"aws {no_verify_ssl_str} s3api create-bucket --bucket {bucket_name} " \
|
||||||
f"--endpoint {endpoint} {configuration} {acl} "
|
f"--endpoint {endpoint} {location}"
|
||||||
cmd_line_ver = f"aws {no_verify_ssl_str} s3api put-bucket-versioning --bucket {bucket_name} " \
|
cmd_line_ver = f"aws {no_verify_ssl_str} s3api put-bucket-versioning --bucket {bucket_name} " \
|
||||||
f"--versioning-configuration Status=Enabled --endpoint {endpoint}"
|
f"--versioning-configuration Status=Enabled --endpoint {endpoint} "
|
||||||
|
|
||||||
output, success = execute_cmd(cmd_line)
|
output, success = execute_cmd(cmd_line)
|
||||||
|
|
||||||
|
@ -25,7 +21,7 @@ def create_bucket(endpoint, versioning, location, acl, no_verify_ssl):
|
||||||
f"Error: {output}", endpoint)
|
f"Error: {output}", endpoint)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if versioning:
|
if versioning == "True":
|
||||||
output, success = execute_cmd(cmd_line_ver)
|
output, success = execute_cmd(cmd_line_ver)
|
||||||
if not success:
|
if not success:
|
||||||
log(f"{cmd_line_ver}\n"
|
log(f"{cmd_line_ver}\n"
|
||||||
|
@ -34,7 +30,7 @@ def create_bucket(endpoint, versioning, location, acl, no_verify_ssl):
|
||||||
else:
|
else:
|
||||||
log(f"Bucket versioning has been applied for bucket {bucket_name}", endpoint)
|
log(f"Bucket versioning has been applied for bucket {bucket_name}", endpoint)
|
||||||
|
|
||||||
log(f"Created bucket: {bucket_name} ({location})", endpoint)
|
log(f"Created bucket: {bucket_name}", endpoint)
|
||||||
return bucket_name
|
return bucket_name
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
import re
|
import re
|
||||||
from helpers.cmd import execute_cmd, log
|
from helpers.cmd import execute_cmd, log
|
||||||
|
|
||||||
def create_container(endpoint, policy, container_creation_retry, wallet_path, config, rules, local=False, retry=0):
|
def create_container(endpoint, policy, wallet_file, wallet_config):
|
||||||
if retry > int(container_creation_retry):
|
if wallet_file:
|
||||||
raise ValueError(f"unable to create container: too many unsuccessful attempts")
|
wallet_file = "--wallet " + wallet_file
|
||||||
|
if wallet_config:
|
||||||
if wallet_path:
|
wallet_config = "--config " + wallet_config
|
||||||
wallet_file = f"--wallet {wallet_path}"
|
|
||||||
if config:
|
|
||||||
wallet_config = f"--config {config}"
|
|
||||||
cmd_line = f"frostfs-cli --rpc-endpoint {endpoint} container create {wallet_file} {wallet_config} " \
|
cmd_line = f"frostfs-cli --rpc-endpoint {endpoint} container create {wallet_file} {wallet_config} " \
|
||||||
f" --policy '{policy}' --await"
|
f" --policy '{policy}' --basic-acl public-read-write --await"
|
||||||
|
|
||||||
output, success = execute_cmd(cmd_line)
|
output, success = execute_cmd(cmd_line)
|
||||||
|
|
||||||
|
@ -30,77 +27,10 @@ def create_container(endpoint, policy, container_creation_retry, wallet_path, co
|
||||||
splitted = fst_str.split(": ")
|
splitted = fst_str.split(": ")
|
||||||
if len(splitted) != 2:
|
if len(splitted) != 2:
|
||||||
raise ValueError(f"no CID was parsed from command output:\t{fst_str}")
|
raise ValueError(f"no CID was parsed from command output:\t{fst_str}")
|
||||||
cid = splitted[1]
|
|
||||||
|
|
||||||
log(f"Created container: {cid} ({policy})", endpoint)
|
log(f"Created container {splitted[1]}", endpoint)
|
||||||
|
|
||||||
# Add rule for container
|
return splitted[1]
|
||||||
if rules:
|
|
||||||
r = ""
|
|
||||||
for rule in rules:
|
|
||||||
r += f" --rule '{rule}' "
|
|
||||||
cmd_line = f"frostfs-cli --rpc-endpoint {endpoint} ape-manager add {wallet_file} {wallet_config} " \
|
|
||||||
f" --chain-id 'chain-id' {r} --target-name '{cid}' --target-type 'container'"
|
|
||||||
output, success = execute_cmd(cmd_line)
|
|
||||||
if not success:
|
|
||||||
log(f"{cmd_line}\n"
|
|
||||||
f"Rule has not been added\n"
|
|
||||||
f"{output}", endpoint)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not local:
|
|
||||||
return cid
|
|
||||||
|
|
||||||
cmd_line = f"frostfs-cli netmap nodeinfo --rpc-endpoint {endpoint} {wallet_file} {wallet_config}"
|
|
||||||
output, success = execute_cmd(cmd_line)
|
|
||||||
|
|
||||||
if not success:
|
|
||||||
log(f"{cmd_line}\n"
|
|
||||||
f"Failed to get nodeinfo\n"
|
|
||||||
f"{output}", endpoint)
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
fst_str = output.split('\n')[0]
|
|
||||||
except Exception:
|
|
||||||
log(f"{cmd_line}\n"
|
|
||||||
f"Incorrect output\n"
|
|
||||||
f"Output: {output or '<empty>'}", endpoint)
|
|
||||||
return False
|
|
||||||
splitted = fst_str.split(": ")
|
|
||||||
if len(splitted) != 2 or len(splitted[1]) == 0:
|
|
||||||
raise ValueError(f"no node key was parsed from command output:\t{fst_str}")
|
|
||||||
|
|
||||||
node_key = splitted[1]
|
|
||||||
|
|
||||||
cmd_line = f"frostfs-cli container nodes --rpc-endpoint {endpoint} {wallet_file} {wallet_config} --cid {cid}"
|
|
||||||
output, success = execute_cmd(cmd_line)
|
|
||||||
|
|
||||||
if not success:
|
|
||||||
log(f"{cmd_line}\n"
|
|
||||||
f"Failed to get container nodes\n"
|
|
||||||
f"{output}", endpoint)
|
|
||||||
return False
|
|
||||||
|
|
||||||
for output_str in output.split('\n'):
|
|
||||||
output_str = output_str.lstrip().rstrip()
|
|
||||||
if not output_str.startswith("Node "):
|
|
||||||
continue
|
|
||||||
splitted = output_str.split(": ")
|
|
||||||
if len(splitted) != 2 or len(splitted[1]) == 0:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
k = splitted[1].split(" ")[0]
|
|
||||||
except Exception:
|
|
||||||
log(f"{cmd_line}\n"
|
|
||||||
f"Incorrect output\n"
|
|
||||||
f"Output: {output or '<empty>'}", endpoint)
|
|
||||||
continue
|
|
||||||
if k == node_key:
|
|
||||||
return cid
|
|
||||||
|
|
||||||
log(f"Created container {cid} is not stored on {endpoint}, creating another one...", endpoint)
|
|
||||||
return create_container(endpoint, policy, container_creation_retry, wallet_path, config, rules, local, retry + 1)
|
|
||||||
|
|
||||||
|
|
||||||
def upload_object(container, payload_filepath, endpoint, wallet_file, wallet_config):
|
def upload_object(container, payload_filepath, endpoint, wallet_file, wallet_config):
|
||||||
|
|
|
@ -15,21 +15,18 @@ from helpers.frostfs_cli import create_container, upload_object
|
||||||
ERROR_WRONG_CONTAINERS_COUNT = 1
|
ERROR_WRONG_CONTAINERS_COUNT = 1
|
||||||
ERROR_WRONG_OBJECTS_COUNT = 2
|
ERROR_WRONG_OBJECTS_COUNT = 2
|
||||||
MAX_WORKERS = 50
|
MAX_WORKERS = 50
|
||||||
DEFAULT_POLICY = "REP 2 IN X CBF 2 SELECT 2 FROM * AS X"
|
|
||||||
DEFAULT_RULES = ["allow Object.* *"]
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--size', help='Upload objects size in kb')
|
parser.add_argument('--size', help='Upload objects size in kb')
|
||||||
parser.add_argument('--containers', help='Number of containers to create')
|
parser.add_argument('--containers', help='Number of containers to create')
|
||||||
parser.add_argument('--retry', default=20, help='Maximum number of retries to create a container')
|
|
||||||
parser.add_argument('--out', help='JSON file with output')
|
parser.add_argument('--out', help='JSON file with output')
|
||||||
parser.add_argument('--preload_obj', help='Number of pre-loaded objects')
|
parser.add_argument('--preload_obj', help='Number of pre-loaded objects')
|
||||||
parser.add_argument('--wallet', help='Wallet file path')
|
parser.add_argument('--wallet', help='Wallet file path')
|
||||||
parser.add_argument('--config', help='Wallet config file path')
|
parser.add_argument('--config', help='Wallet config file path')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--policy",
|
"--policy",
|
||||||
help=f"Container placement policy. Default is {DEFAULT_POLICY}",
|
help="Container placement policy",
|
||||||
action="append"
|
default="REP 2 IN X CBF 2 SELECT 2 FROM * AS X"
|
||||||
)
|
)
|
||||||
parser.add_argument('--endpoint', help='Nodes addresses separated by comma.')
|
parser.add_argument('--endpoint', help='Nodes addresses separated by comma.')
|
||||||
parser.add_argument('--update', help='Save existed containers')
|
parser.add_argument('--update', help='Save existed containers')
|
||||||
|
@ -37,11 +34,6 @@ parser.add_argument('--ignore-errors', help='Ignore preset errors', action='stor
|
||||||
parser.add_argument('--workers', help='Count of workers in preset. Max = 50, Default = 50', default=50)
|
parser.add_argument('--workers', help='Count of workers in preset. Max = 50, Default = 50', default=50)
|
||||||
parser.add_argument('--sleep', help='Time to sleep between containers creation and objects upload (in seconds), '
|
parser.add_argument('--sleep', help='Time to sleep between containers creation and objects upload (in seconds), '
|
||||||
'Default = 8', default=8)
|
'Default = 8', default=8)
|
||||||
parser.add_argument('--local', help='Create containers that store data on provided endpoints. Warning: additional empty containers may be created.', action='store_true')
|
|
||||||
parser.add_argument(
|
|
||||||
'--rule',
|
|
||||||
help='Rule attached to created containers. All entries of CONTAINER_ID will be replaced with id of created container.',
|
|
||||||
action="append")
|
|
||||||
|
|
||||||
args: Namespace = parser.parse_args()
|
args: Namespace = parser.parse_args()
|
||||||
print(args)
|
print(args)
|
||||||
|
@ -52,18 +44,12 @@ def main():
|
||||||
objects_list = []
|
objects_list = []
|
||||||
|
|
||||||
endpoints = args.endpoint.split(',')
|
endpoints = args.endpoint.split(',')
|
||||||
if not args.policy:
|
|
||||||
args.policy = [DEFAULT_POLICY]
|
|
||||||
|
|
||||||
container_creation_retry = args.retry
|
|
||||||
wallet = args.wallet
|
wallet = args.wallet
|
||||||
wallet_config = args.config
|
wallet_config = args.config
|
||||||
workers = int(args.workers)
|
workers = int(args.workers)
|
||||||
objects_per_container = int(args.preload_obj)
|
objects_per_container = int(args.preload_obj)
|
||||||
rules = args.rule
|
|
||||||
if not rules:
|
|
||||||
rules = DEFAULT_RULES
|
|
||||||
|
|
||||||
ignore_errors = args.ignore_errors
|
ignore_errors = args.ignore_errors
|
||||||
if args.update:
|
if args.update:
|
||||||
# Open file
|
# Open file
|
||||||
|
@ -75,9 +61,9 @@ def main():
|
||||||
containers_count = int(args.containers)
|
containers_count = int(args.containers)
|
||||||
print(f"Create containers: {containers_count}")
|
print(f"Create containers: {containers_count}")
|
||||||
with ProcessPoolExecutor(max_workers=min(MAX_WORKERS, workers)) as executor:
|
with ProcessPoolExecutor(max_workers=min(MAX_WORKERS, workers)) as executor:
|
||||||
containers_runs = [executor.submit(create_container, endpoint, policy, container_creation_retry, wallet, wallet_config, rules, args.local)
|
containers_runs = [executor.submit(create_container, endpoint, args.policy, wallet, wallet_config)
|
||||||
for _, endpoint, policy in
|
for _, endpoint in
|
||||||
zip(range(containers_count), cycle(endpoints), cycle(args.policy))]
|
zip(range(containers_count), cycle(endpoints))]
|
||||||
|
|
||||||
for run in containers_runs:
|
for run in containers_runs:
|
||||||
container_id = run.result()
|
container_id = run.result()
|
||||||
|
@ -87,7 +73,7 @@ def main():
|
||||||
print("Create containers: Completed")
|
print("Create containers: Completed")
|
||||||
|
|
||||||
print(f" > Containers: {containers}")
|
print(f" > Containers: {containers}")
|
||||||
if containers_count > 0 and len(containers) != containers_count:
|
if containers_count == 0 or len(containers) != containers_count:
|
||||||
print(f"Containers mismatch in preset: expected {containers_count}, created {len(containers)}")
|
print(f"Containers mismatch in preset: expected {containers_count}, created {len(containers)}")
|
||||||
if not ignore_errors:
|
if not ignore_errors:
|
||||||
sys.exit(ERROR_WRONG_CONTAINERS_COUNT)
|
sys.exit(ERROR_WRONG_CONTAINERS_COUNT)
|
||||||
|
|
|
@ -11,12 +11,6 @@ from concurrent.futures import ProcessPoolExecutor
|
||||||
from helpers.cmd import random_payload
|
from helpers.cmd import random_payload
|
||||||
from helpers.aws_cli import create_bucket, upload_object
|
from helpers.aws_cli import create_bucket, upload_object
|
||||||
|
|
||||||
ERROR_WRONG_CONTAINERS_COUNT = 1
|
|
||||||
ERROR_WRONG_OBJECTS_COUNT = 2
|
|
||||||
ERROR_WRONG_PERCENTAGE = 3
|
|
||||||
MAX_WORKERS = 50
|
|
||||||
DEFAULT_LOCATION = ""
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
parser.add_argument('--size', help='Upload objects size in kb.')
|
parser.add_argument('--size', help='Upload objects size in kb.')
|
||||||
|
@ -26,19 +20,21 @@ parser.add_argument('--preload_obj', help='Number of pre-loaded objects.')
|
||||||
parser.add_argument('--endpoint', help='S3 Gateways addresses separated by comma.')
|
parser.add_argument('--endpoint', help='S3 Gateways addresses separated by comma.')
|
||||||
parser.add_argument('--update', help='True/False, False by default. Save existed buckets from target file (--out). '
|
parser.add_argument('--update', help='True/False, False by default. Save existed buckets from target file (--out). '
|
||||||
'New buckets will not be created.')
|
'New buckets will not be created.')
|
||||||
parser.add_argument('--location', help=f'AWS location constraint. Default is "{DEFAULT_LOCATION}"', action="append")
|
parser.add_argument('--location', help='AWS location. Will be empty, if has not be declared.', default="")
|
||||||
parser.add_argument('--versioning', help='True/False, False by default. Alias of --buckets_versioned=100')
|
parser.add_argument('--versioning', help='True/False, False by default.')
|
||||||
parser.add_argument('--buckets_versioned', help='Percent of versioned buckets. Default is 0', default=0)
|
|
||||||
parser.add_argument('--ignore-errors', help='Ignore preset errors', action='store_true')
|
parser.add_argument('--ignore-errors', help='Ignore preset errors', action='store_true')
|
||||||
parser.add_argument('--no-verify-ssl', help='Ignore SSL verifications', action='store_true')
|
parser.add_argument('--no-verify-ssl', help='Ignore SSL verifications', action='store_true')
|
||||||
parser.add_argument('--workers', help='Count of workers in preset. Max = 50, Default = 50', default=50)
|
parser.add_argument('--workers', help='Count of workers in preset. Max = 50, Default = 50', default=50)
|
||||||
parser.add_argument('--sleep', help='Time to sleep between buckets creation and objects upload (in seconds), '
|
parser.add_argument('--sleep', help='Time to sleep between buckets creation and objects upload (in seconds), '
|
||||||
'Default = 8', default=8)
|
'Default = 8', default=8)
|
||||||
parser.add_argument('--acl', help='Bucket ACL. Default is private. Expected values are: private, public-read or public-read-write.', default="private")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
print(args)
|
print(args)
|
||||||
|
|
||||||
|
ERROR_WRONG_CONTAINERS_COUNT = 1
|
||||||
|
ERROR_WRONG_OBJECTS_COUNT = 2
|
||||||
|
MAX_WORKERS = 50
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
buckets = []
|
buckets = []
|
||||||
objects_list = []
|
objects_list = []
|
||||||
|
@ -46,8 +42,6 @@ def main():
|
||||||
no_verify_ssl = args.no_verify_ssl
|
no_verify_ssl = args.no_verify_ssl
|
||||||
|
|
||||||
endpoints = args.endpoint.split(',')
|
endpoints = args.endpoint.split(',')
|
||||||
if not args.location:
|
|
||||||
args.location = [DEFAULT_LOCATION]
|
|
||||||
|
|
||||||
workers = int(args.workers)
|
workers = int(args.workers)
|
||||||
objects_per_bucket = int(args.preload_obj)
|
objects_per_bucket = int(args.preload_obj)
|
||||||
|
@ -64,18 +58,9 @@ def main():
|
||||||
print(f"Create buckets: {buckets_count}")
|
print(f"Create buckets: {buckets_count}")
|
||||||
|
|
||||||
with ProcessPoolExecutor(max_workers=min(MAX_WORKERS, workers)) as executor:
|
with ProcessPoolExecutor(max_workers=min(MAX_WORKERS, workers)) as executor:
|
||||||
if not 0 <= int(args.buckets_versioned) <= 100:
|
buckets_runs = [executor.submit(create_bucket, endpoint, args.versioning, args.location, no_verify_ssl)
|
||||||
print(f"Percent of versioned buckets must be between 0 and 100: got {args.buckets_versioned}")
|
for _, endpoint in
|
||||||
if not ignore_errors:
|
zip(range(buckets_count), cycle(endpoints))]
|
||||||
sys.exit(ERROR_WRONG_PERCENTAGE)
|
|
||||||
if args.versioning == "True":
|
|
||||||
versioning_per_bucket = [True] * buckets_count
|
|
||||||
else:
|
|
||||||
num_versioned_buckets = int((int(args.buckets_versioned) / 100) * buckets_count)
|
|
||||||
versioning_per_bucket = [True] * num_versioned_buckets + [False] * (buckets_count - num_versioned_buckets)
|
|
||||||
buckets_runs = [executor.submit(create_bucket, endpoint, versioning_per_bucket[i], location, args.acl, no_verify_ssl)
|
|
||||||
for i, endpoint, location in
|
|
||||||
zip(range(buckets_count), cycle(endpoints), cycle(args.location))]
|
|
||||||
|
|
||||||
for run in buckets_runs:
|
for run in buckets_runs:
|
||||||
bucket_name = run.result()
|
bucket_name = run.result()
|
||||||
|
@ -85,7 +70,7 @@ def main():
|
||||||
print("Create buckets: Completed")
|
print("Create buckets: Completed")
|
||||||
|
|
||||||
print(f" > Buckets: {buckets}")
|
print(f" > Buckets: {buckets}")
|
||||||
if buckets_count > 0 and len(buckets) != buckets_count:
|
if buckets_count == 0 or len(buckets) != buckets_count:
|
||||||
print(f"Buckets mismatch in preset: expected {buckets_count}, created {len(buckets)}")
|
print(f"Buckets mismatch in preset: expected {buckets_count}, created {len(buckets)}")
|
||||||
if not ignore_errors:
|
if not ignore_errors:
|
||||||
sys.exit(ERROR_WRONG_CONTAINERS_COUNT)
|
sys.exit(ERROR_WRONG_CONTAINERS_COUNT)
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import http.client
|
import requests
|
||||||
import ssl
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--endpoint', help='Endpoint of the S3 gateway')
|
parser.add_argument('--endpoint', help='Endpoint of the S3 gateway')
|
||||||
|
@ -17,13 +16,10 @@ def main():
|
||||||
|
|
||||||
preset = json.loads(preset_text)
|
preset = json.loads(preset_text)
|
||||||
|
|
||||||
conn = http.client.HTTPSConnection(args.endpoint, context = ssl._create_unverified_context())
|
|
||||||
containers = []
|
containers = []
|
||||||
for bucket in preset.get('buckets'):
|
for bucket in preset.get('buckets'):
|
||||||
conn.request("HEAD", f'/{bucket}')
|
resp = requests.head(f'{args.endpoint}/{bucket}', verify=False)
|
||||||
response = conn.getresponse()
|
containers.append(resp.headers['X-Container-Id'])
|
||||||
containers.append(response.getheader('X-Container-Id'))
|
|
||||||
response.read()
|
|
||||||
|
|
||||||
preset['containers'] = containers
|
preset['containers'] = containers
|
||||||
with open(args.preset_file, 'w+') as f:
|
with open(args.preset_file, 'w+') as f:
|
||||||
|
|
|
@ -19,8 +19,6 @@ Scenarios `grpc.js`, `local.js`, `http.js` and `s3.js` support the following opt
|
||||||
* `SLEEP_READ` - time interval (in seconds) between reading VU iterations.
|
* `SLEEP_READ` - time interval (in seconds) between reading VU iterations.
|
||||||
* `SELECTION_SIZE` - size of batch to select for deletion (default: 1000).
|
* `SELECTION_SIZE` - size of batch to select for deletion (default: 1000).
|
||||||
* `PAYLOAD_TYPE` - type of an object payload ("random" or "text", default: "random").
|
* `PAYLOAD_TYPE` - type of an object payload ("random" or "text", default: "random").
|
||||||
* `STREAMING` - if set, the payload is generated on the fly and is not read into memory fully.
|
|
||||||
* `METRIC_TAGS` - custom metrics tags (format `tag1:value1;tag2:value2`).
|
|
||||||
|
|
||||||
Additionally, the profiling extension can be enabled to generate CPU and memory profiles which can be inspected with `go tool pprof file.prof`:
|
Additionally, the profiling extension can be enabled to generate CPU and memory profiles which can be inspected with `go tool pprof file.prof`:
|
||||||
```shell
|
```shell
|
||||||
|
@ -71,15 +69,13 @@ $ ./scenarios/preset/preset_grpc.py --size 1024 --containers 1 --out grpc.json -
|
||||||
2. Execute scenario with options:
|
2. Execute scenario with options:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ./k6 run -e DURATION=60 -e WRITE_OBJ_SIZE=8192 -e READERS=20 -e WRITERS=20 -e DELETERS=30 -e DELETE_AGE=10 -e REGISTRY_FILE=registry.bolt -e CONFIG_FILE=/path/to/config.yaml -e CONFIG_DIR=/path/to/dir/ -e PREGEN_JSON=./grpc.json scenarios/local.js
|
$ ./k6 run -e DURATION=60 -e WRITE_OBJ_SIZE=8192 -e READERS=20 -e WRITERS=20 -e DELETERS=30 -e DELETE_AGE=10 -e REGISTRY_FILE=registry.bolt -e CONFIG_FILE=/path/to/config.yaml -e PREGEN_JSON=./grpc.json scenarios/local.js
|
||||||
```
|
```
|
||||||
|
|
||||||
Options (in addition to the common options):
|
Options (in addition to the common options):
|
||||||
* `CONFIG_FILE` - path to the local configuration file used for the storage node. Only the storage configuration section is used.
|
* `CONFIG_FILE` - path to the local configuration file used for the storage node. Only the storage configuration section is used.
|
||||||
* `CONFIG_DIR` - path to the folder with local configuration files used for the storage node.
|
|
||||||
* `DELETERS` - number of VUs performing delete operations (using deleters requires that options `DELETE_AGE` and `REGISTRY_FILE` are specified as well).
|
* `DELETERS` - number of VUs performing delete operations (using deleters requires that options `DELETE_AGE` and `REGISTRY_FILE` are specified as well).
|
||||||
* `DELETE_AGE` - age of object in seconds before which it can not be deleted. This parameter can be used to control how many objects we have in the system under load.
|
* `DELETE_AGE` - age of object in seconds before which it can not be deleted. This parameter can be used to control how many objects we have in the system under load.
|
||||||
* `MAX_TOTAL_SIZE_GB` - if specified, max payload size in GB of the storage engine. If the storage engine is already full, no new objects will be saved.
|
|
||||||
|
|
||||||
## HTTP
|
## HTTP
|
||||||
|
|
||||||
|
@ -125,7 +121,7 @@ The tests will use all pre-created buckets for PUT operations and all pre-create
|
||||||
$ ./scenarios/preset/preset_s3.py --size 1024 --buckets 1 --out s3_1024kb.json --endpoint host1:8084 --preload_obj 500 --location load-1-4
|
$ ./scenarios/preset/preset_s3.py --size 1024 --buckets 1 --out s3_1024kb.json --endpoint host1:8084 --preload_obj 500 --location load-1-4
|
||||||
```
|
```
|
||||||
* '--location' - specify the name of container policy (from policy.json file). It's important to run 'aws configure' each time when the policy file has been changed to pick up the latest policies.
|
* '--location' - specify the name of container policy (from policy.json file). It's important to run 'aws configure' each time when the policy file has been changed to pick up the latest policies.
|
||||||
* '--buckets_versioned' - specify the percentage of versioned buckets from the total number of created buckets. Default is 0
|
|
||||||
3. Execute scenario with options:
|
3. Execute scenario with options:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -138,8 +134,6 @@ Options (in addition to the common options):
|
||||||
* `DELETE_AGE` - age of object in seconds before which it can not be deleted. This parameter can be used to control how many objects we have in the system under load.
|
* `DELETE_AGE` - age of object in seconds before which it can not be deleted. This parameter can be used to control how many objects we have in the system under load.
|
||||||
* `SLEEP_DELETE` - time interval (in seconds) between deleting VU iterations.
|
* `SLEEP_DELETE` - time interval (in seconds) between deleting VU iterations.
|
||||||
* `OBJ_NAME` - if specified, this name will be used for all write operations instead of random generation.
|
* `OBJ_NAME` - if specified, this name will be used for all write operations instead of random generation.
|
||||||
* `OBJ_NAME_LENGTH` - if specified, then name of the object will be generated with the specified length of ASCII characters.
|
|
||||||
* `DIR_HEIGHT`, `DIR_WIDTH` - if both specified, object name will consist of `DIR_HEIGHT` directories, each of which can have `DIR_WIDTH` subdirectories, for example for `DIR_HEIGHT = 3, DIR_WIDTH = 100`, object names will be `/dir{1...100}/dir{1...100}/dir{1...100}/{uuid || OBJ_NAME}`
|
|
||||||
|
|
||||||
## S3 Multipart
|
## S3 Multipart
|
||||||
|
|
||||||
|
@ -181,50 +175,13 @@ After this, the `pregen.json` file will contain a `containers` list field the sa
|
||||||
|
|
||||||
3. Execute the scenario with the desired options. For example:
|
3. Execute the scenario with the desired options. For example:
|
||||||
```shell
|
```shell
|
||||||
$ ./k6 run -e DURATION=60 -e WRITE_OBJ_SIZE=8192 -e READERS=20 -e WRITERS=20 -e CONFIG_FILE=/path/to/node/config.yml -e CONFIG_DIR=/path/to/dir/ -e PREGEN_JSON=pregen.json scenarios/s3local.js
|
$ ./k6 run -e DURATION=60 -e WRITE_OBJ_SIZE=8192 -e READERS=20 -e WRITERS=20 -e CONFIG_FILE=/path/to/node/config.yml -e PREGEN_JSON=pregen.json scenarios/s3local.js
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the `s3local` scenario currently does not support deleters.
|
Note that the `s3local` scenario currently does not support deleters.
|
||||||
|
|
||||||
Options (in addition to the common options):
|
Options (in addition to the common options):
|
||||||
* `OBJ_NAME` - if specified, this name will be used for all write operations instead of random generation.
|
* `OBJ_NAME` - if specified, this name will be used for all write operations instead of random generation.
|
||||||
* `MAX_TOTAL_SIZE_GB` - if specified, max payload size in GB of the storage engine. If the storage engine is already full, no new objects will be saved.
|
|
||||||
|
|
||||||
## Export metrics
|
|
||||||
|
|
||||||
To export metrics to Prometheus (also Grafana and Victoria Metrics support Prometheus format), you need to run `k6` with an option `-o experimental-prometheus-rw` and
|
|
||||||
an environment variable `K6_PROMETHEUS_RW_SERVER_URL` whose value corresponds to the URL for the remote write endpoint.
|
|
||||||
To specify percentiles for trend metrics, use an environment variable `K6_PROMETHEUS_RW_TREND_STATS`.
|
|
||||||
See [k6 docs](https://k6.io/docs/results-output/real-time/prometheus-remote-write/) for a list of all possible options.
|
|
||||||
To distinct metrics from different loaders, use an option `METRIC_TAGS`. These tags does not apply to builtin `k6` metrics.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```bash
|
|
||||||
K6_PROMETHEUS_RW_SERVER_URL=http://host:8428/api/v1/write \
|
|
||||||
K6_PROMETHEUS_RW_TREND_STATS="p(95),p(99),min,max" \
|
|
||||||
./k6 run ... -o experimental-prometheus-rw -e METRIC_TAGS="instance:server1;run:run1" scenario.js
|
|
||||||
```
|
|
||||||
|
|
||||||
## Grafana annotations
|
|
||||||
|
|
||||||
There is no option to export Grafana annotaions, but it can be easily done with `curl` and Grafana's annotations API.
|
|
||||||
Example:
|
|
||||||
```shell
|
|
||||||
curl --request POST \
|
|
||||||
--url https://user:password@grafana.host/api/annotations \
|
|
||||||
--header 'Content-Type: application/json' \
|
|
||||||
--data '{
|
|
||||||
"dashboardUID": "YsVWNpMIk",
|
|
||||||
"time": 1706533045014,
|
|
||||||
"timeEnd": 1706533085100,
|
|
||||||
"tags": [
|
|
||||||
"tag1",
|
|
||||||
"tag2"
|
|
||||||
],
|
|
||||||
"text": "Test annotation"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
See [Grafana docs](https://grafana.com/docs/grafana/latest/developers/http_api/annotations/) for details.
|
|
||||||
|
|
||||||
## Verify
|
## Verify
|
||||||
|
|
||||||
|
|
293
scenarios/s3.js
293
scenarios/s3.js
|
@ -1,226 +1,201 @@
|
||||||
import {sleep} from 'k6';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import {SharedArray} from 'k6/data';
|
|
||||||
import exec from 'k6/execution';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import s3 from 'k6/x/frostfs/s3';
|
import s3 from 'k6/x/frostfs/s3';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import { SharedArray } from 'k6/data';
|
||||||
|
import { sleep } from 'k6';
|
||||||
import {generateS3Key} from './libs/keygen.js';
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
import {parseEnv} from './libs/env-parser.js';
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
||||||
import {newGenerator} from './libs/datagen.js';
|
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_list = new SharedArray(
|
const obj_list = new SharedArray('obj_list', function () {
|
||||||
'obj_list',
|
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
||||||
function() { return JSON.parse(open(__ENV.PREGEN_JSON)).objects; });
|
});
|
||||||
|
|
||||||
const bucket_list = new SharedArray(
|
const bucket_list = new SharedArray('bucket_list', function () {
|
||||||
'bucket_list',
|
return JSON.parse(open(__ENV.PREGEN_JSON)).buckets;
|
||||||
function() { return JSON.parse(open(__ENV.PREGEN_JSON)).buckets; });
|
});
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
const no_verify_ssl = __ENV.NO_VERIFY_SSL || 'true';
|
const no_verify_ssl = __ENV.NO_VERIFY_SSL || "true";
|
||||||
const connection_args = {
|
const connection_args = {no_verify_ssl: no_verify_ssl}
|
||||||
no_verify_ssl : no_verify_ssl
|
|
||||||
}
|
|
||||||
// Select random S3 endpoint for current VU
|
// Select random S3 endpoint for current VU
|
||||||
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
||||||
const s3_endpoint =
|
const s3_endpoint = s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
||||||
s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
|
||||||
const s3_client = s3.connect(s3_endpoint, connection_args);
|
const s3_client = s3.connect(s3_endpoint, connection_args);
|
||||||
const log = logging.new().withField('endpoint', s3_endpoint);
|
const log = logging.new().withField("endpoint", s3_endpoint);
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
let obj_to_delete_selector = undefined;
|
||||||
|
if (registry_enabled && delete_age) {
|
||||||
|
obj_to_delete_selector = registry.getSelector(
|
||||||
|
__ENV.REGISTRY_FILE,
|
||||||
|
"obj_to_delete",
|
||||||
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
|
{
|
||||||
|
status: "created",
|
||||||
|
age: delete_age,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_age = __ENV.READ_AGE ? parseInt(__ENV.READ_AGE) : 10;
|
|
||||||
let obj_to_read_selector = undefined;
|
let obj_to_read_selector = undefined;
|
||||||
if (registry_enabled) {
|
if (registry_enabled) {
|
||||||
obj_to_read_selector = registry.getLoopedSelector(
|
obj_to_read_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_read',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_read",
|
||||||
status : 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
age : read_age,
|
{
|
||||||
})
|
status: "created",
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE), __ENV.PAYLOAD_TYPE || "");
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
||||||
const generator = newGenerator(write_vu_count > 0);
|
|
||||||
if (write_vu_count > 0) {
|
if (write_vu_count > 0) {
|
||||||
scenarios.write = {
|
scenarios.write = {
|
||||||
executor : 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus : write_vu_count,
|
vus: write_vu_count,
|
||||||
duration : `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec : 'obj_write',
|
exec: 'obj_write',
|
||||||
gracefulStop : '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
|
||||||
let obj_to_delete_selector = undefined;
|
|
||||||
let obj_to_delete_exit_on_null = undefined;
|
|
||||||
if (registry_enabled && delete_age) {
|
|
||||||
obj_to_delete_exit_on_null = write_vu_count == 0;
|
|
||||||
|
|
||||||
let constructor = obj_to_delete_exit_on_null ? registry.getOneshotSelector
|
|
||||||
: registry.getSelector;
|
|
||||||
|
|
||||||
obj_to_delete_selector =
|
|
||||||
constructor(__ENV.REGISTRY_FILE, 'obj_to_delete',
|
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
|
||||||
status : 'created',
|
|
||||||
age : delete_age,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_vu_count = parseInt(__ENV.READERS || '0');
|
const read_vu_count = parseInt(__ENV.READERS || '0');
|
||||||
if (read_vu_count > 0) {
|
if (read_vu_count > 0) {
|
||||||
scenarios.read = {
|
scenarios.read = {
|
||||||
executor : 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus : read_vu_count,
|
vus: read_vu_count,
|
||||||
duration : `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec : 'obj_read',
|
exec: 'obj_read',
|
||||||
gracefulStop : '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const delete_vu_count = parseInt(__ENV.DELETERS || '0');
|
const delete_vu_count = parseInt(__ENV.DELETERS || '0');
|
||||||
if (delete_vu_count > 0) {
|
if (delete_vu_count > 0) {
|
||||||
if (!obj_to_delete_selector) {
|
if (!obj_to_delete_selector) {
|
||||||
throw 'Positive DELETE worker number without a proper object selector';
|
throw 'Positive DELETE worker number without a proper object selector';
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios.delete = {
|
scenarios.delete = {
|
||||||
executor : 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus : delete_vu_count,
|
vus: delete_vu_count,
|
||||||
duration : `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec : 'obj_delete',
|
exec: 'obj_delete',
|
||||||
gracefulStop : '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout : '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_vu_count = write_vu_count + read_vu_count + delete_vu_count;
|
const total_vu_count = write_vu_count + read_vu_count + delete_vu_count;
|
||||||
|
|
||||||
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
console.log(`Pregenerated read object size: ${read_size}`);
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
||||||
console.log(`Reading VUs: ${read_vu_count}`);
|
console.log(`Reading VUs: ${read_vu_count}`);
|
||||||
console.log(`Writing VUs: ${write_vu_count}`);
|
console.log(`Writing VUs: ${write_vu_count}`);
|
||||||
console.log(`Deleting VUs: ${delete_vu_count}`);
|
console.log(`Deleting VUs: ${delete_vu_count}`);
|
||||||
console.log(`Total VUs: ${total_vu_count}`);
|
console.log(`Total VUs: ${total_vu_count}`);
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
const start_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load started at: ${Date(start_timestamp).toString()}`)
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
|
|
||||||
if (delete_vu_count > 0){
|
|
||||||
obj_to_delete_selector.sync.add(delete_vu_count)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
const end_timestamp = Date.now()
|
const end_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load finished at: ${Date(end_timestamp).toString()}`)
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout' : textSummary(data, {indent : ' ', enableColors : false}),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json] : JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write() {
|
export function obj_write() {
|
||||||
if (__ENV.SLEEP_WRITE) {
|
if (__ENV.SLEEP_WRITE) {
|
||||||
sleep(__ENV.SLEEP_WRITE);
|
sleep(__ENV.SLEEP_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = generateS3Key();
|
const key = __ENV.OBJ_NAME || uuidv4();
|
||||||
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const { payload, hash } = generator.genPayload(registry_enabled);
|
||||||
const resp = s3_client.put(bucket, key, payload);
|
const resp = s3_client.put(bucket, key, payload);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({bucket : bucket, key : key}).error(resp.error);
|
log.withFields({bucket: bucket, key: key}).error(resp.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject('', '', bucket, key, payload.hash());
|
obj_registry.addObject("", "", bucket, key, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_read() {
|
export function obj_read() {
|
||||||
if (__ENV.SLEEP_READ) {
|
if (__ENV.SLEEP_READ) {
|
||||||
sleep(__ENV.SLEEP_READ);
|
sleep(__ENV.SLEEP_READ);
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_to_read_selector) {
|
|
||||||
const obj = obj_to_read_selector.nextObject();
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const resp = s3_client.get(obj.s3_bucket, obj.s3_key)
|
|
||||||
|
if(obj_to_read_selector) {
|
||||||
|
const obj = obj_to_read_selector.nextObject();
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resp = s3_client.get(obj.s3_bucket, obj.s3_key)
|
||||||
|
if (!resp.success) {
|
||||||
|
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key}).error(resp.error);
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
||||||
|
|
||||||
|
const resp = s3_client.get(obj.bucket, obj.object);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({bucket : obj.s3_bucket, key : obj.s3_key})
|
log.withFields({bucket: obj.bucket, key: obj.object}).error(resp.error);
|
||||||
.error(resp.error);
|
}
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
|
||||||
|
|
||||||
const resp = s3_client.get(obj.bucket, obj.object);
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({bucket : obj.bucket, key : obj.object}).error(resp.error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_delete() {
|
export function obj_delete() {
|
||||||
if (__ENV.SLEEP_DELETE) {
|
if (__ENV.SLEEP_DELETE) {
|
||||||
sleep(__ENV.SLEEP_DELETE);
|
sleep(__ENV.SLEEP_DELETE);
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_to_delete_selector.nextObject();
|
|
||||||
if (!obj) {
|
|
||||||
if (obj_to_delete_exit_on_null) {
|
|
||||||
obj_to_delete_selector.sync.done()
|
|
||||||
obj_to_delete_selector.sync.wait()
|
|
||||||
exec.test.abort("No more objects to select");
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resp = s3_client.delete(obj.s3_bucket, obj.s3_key);
|
const obj = obj_to_delete_selector.nextObject();
|
||||||
if (!resp.success) {
|
if (!obj) {
|
||||||
log.withFields({bucket : obj.s3_bucket, key : obj.s3_key, op : 'DELETE'})
|
return;
|
||||||
.error(resp.error);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj_registry.deleteObject(obj.id);
|
const resp = s3_client.delete(obj.s3_bucket, obj.s3_key);
|
||||||
|
if (!resp.success) {
|
||||||
|
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key, op: "DELETE"}).error(resp.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_registry.deleteObject(obj.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,89 +1,84 @@
|
||||||
import {sleep} from 'k6';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import {SharedArray} from 'k6/data';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import s3 from 'k6/x/frostfs/s3';
|
import s3 from 'k6/x/frostfs/s3';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import { SharedArray } from 'k6/data';
|
||||||
|
import { sleep } from 'k6';
|
||||||
import {generateS3Key} from './libs/keygen.js';
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
import {newGenerator} from './libs/datagen.js';
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
import {parseEnv} from './libs/env-parser.js';
|
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
||||||
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_list = new SharedArray('obj_list', function() {
|
const obj_list = new SharedArray('obj_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
||||||
});
|
});
|
||||||
|
|
||||||
const bucket_list = new SharedArray('bucket_list', function() {
|
const bucket_list = new SharedArray('bucket_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).buckets;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).buckets;
|
||||||
});
|
});
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
// Select random S3 endpoint for current VU
|
// Select random S3 endpoint for current VU
|
||||||
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
||||||
const s3_endpoint =
|
const s3_endpoint = s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
||||||
s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
const no_verify_ssl = __ENV.NO_VERIFY_SSL || "true";
|
||||||
const no_verify_ssl = __ENV.NO_VERIFY_SSL || 'true';
|
const connection_args = {no_verify_ssl: no_verify_ssl}
|
||||||
const connection_args = {
|
|
||||||
no_verify_ssl: no_verify_ssl
|
|
||||||
};
|
|
||||||
const s3_client = s3.connect(s3_endpoint, connection_args);
|
const s3_client = s3.connect(s3_endpoint, connection_args);
|
||||||
const log = logging.new().withField('endpoint', s3_endpoint);
|
const log = logging.new().withField("endpoint", s3_endpoint);
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
||||||
let obj_to_delete_selector = undefined;
|
let obj_to_delete_selector = undefined;
|
||||||
if (registry_enabled && delete_age) {
|
if (registry_enabled && delete_age) {
|
||||||
obj_to_delete_selector = registry.getSelector(
|
obj_to_delete_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_delete',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_delete",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
age: delete_age,
|
{
|
||||||
});
|
status: "created",
|
||||||
|
age: delete_age,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_age = __ENV.READ_AGE ? parseInt(__ENV.READ_AGE) : 10;
|
|
||||||
let obj_to_read_selector = undefined;
|
let obj_to_read_selector = undefined;
|
||||||
if (registry_enabled) {
|
if (registry_enabled) {
|
||||||
obj_to_read_selector = registry.getLoopedSelector(
|
obj_to_read_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_read',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_read",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
age: read_age,
|
{
|
||||||
})
|
status: "created",
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE));
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const time_unit = __ENV.TIME_UNIT || '1s';
|
const time_unit = __ENV.TIME_UNIT || '1s';
|
||||||
const pre_alloc_write_vus = parseInt(__ENV.PRE_ALLOC_WRITERS || '0');
|
const pre_alloc_write_vus = parseInt(__ENV.PRE_ALLOC_WRITERS || '0');
|
||||||
const max_write_vus = parseInt(__ENV.MAX_WRITERS || pre_alloc_write_vus);
|
const max_write_vus = parseInt(__ENV.MAX_WRITERS || pre_alloc_write_vus);
|
||||||
const write_rate = parseInt(__ENV.WRITE_RATE || '0');
|
const write_rate = parseInt(__ENV.WRITE_RATE || '0');
|
||||||
const generator = newGenerator(write_rate > 0);
|
|
||||||
if (write_rate > 0) {
|
if (write_rate > 0) {
|
||||||
scenarios.write = {
|
scenarios.write = {
|
||||||
executor: 'constant-arrival-rate',
|
executor: 'constant-arrival-rate',
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
preAllocatedVUs: pre_alloc_write_vus,
|
preAllocatedVUs: pre_alloc_write_vus,
|
||||||
maxVUs: max_write_vus,
|
maxVUs: max_write_vus,
|
||||||
rate: write_rate,
|
rate: write_rate,
|
||||||
timeUnit: time_unit,
|
timeUnit: time_unit,
|
||||||
exec: 'obj_write',
|
exec: 'obj_write',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,16 +86,16 @@ const pre_alloc_read_vus = parseInt(__ENV.PRE_ALLOC_READERS || '0');
|
||||||
const max_read_vus = parseInt(__ENV.MAX_READERS || pre_alloc_read_vus);
|
const max_read_vus = parseInt(__ENV.MAX_READERS || pre_alloc_read_vus);
|
||||||
const read_rate = parseInt(__ENV.READ_RATE || '0');
|
const read_rate = parseInt(__ENV.READ_RATE || '0');
|
||||||
if (read_rate > 0) {
|
if (read_rate > 0) {
|
||||||
scenarios.read = {
|
scenarios.read = {
|
||||||
executor: 'constant-arrival-rate',
|
executor: 'constant-arrival-rate',
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
preAllocatedVUs: pre_alloc_write_vus,
|
preAllocatedVUs: pre_alloc_write_vus,
|
||||||
maxVUs: max_read_vus,
|
maxVUs: max_read_vus,
|
||||||
rate: read_rate,
|
rate: read_rate,
|
||||||
timeUnit: time_unit,
|
timeUnit: time_unit,
|
||||||
exec: 'obj_read',
|
exec: 'obj_read',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,132 +103,126 @@ const pre_alloc_delete_vus = parseInt(__ENV.PRE_ALLOC_DELETERS || '0');
|
||||||
const max_delete_vus = parseInt(__ENV.MAX_DELETERS || pre_alloc_write_vus);
|
const max_delete_vus = parseInt(__ENV.MAX_DELETERS || pre_alloc_write_vus);
|
||||||
const delete_rate = parseInt(__ENV.DELETE_RATE || '0');
|
const delete_rate = parseInt(__ENV.DELETE_RATE || '0');
|
||||||
if (delete_rate > 0) {
|
if (delete_rate > 0) {
|
||||||
if (!obj_to_delete_selector) {
|
if (!obj_to_delete_selector) {
|
||||||
throw new Error(
|
throw new Error('Positive DELETE worker number without a proper object selector');
|
||||||
'Positive DELETE worker number without a proper object selector');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scenarios.delete = {
|
scenarios.delete = {
|
||||||
executor: 'constant-arrival-rate',
|
executor: 'constant-arrival-rate',
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
preAllocatedVUs: pre_alloc_delete_vus,
|
preAllocatedVUs: pre_alloc_delete_vus,
|
||||||
maxVUs: max_delete_vus,
|
maxVUs: max_delete_vus,
|
||||||
rate: delete_rate,
|
rate: delete_rate,
|
||||||
timeUnit: time_unit,
|
timeUnit: time_unit,
|
||||||
exec: 'obj_delete',
|
exec: 'obj_delete',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_pre_allocated_vu_count =
|
const total_pre_allocated_vu_count = pre_alloc_write_vus + pre_alloc_read_vus + pre_alloc_delete_vus;
|
||||||
pre_alloc_write_vus + pre_alloc_read_vus + pre_alloc_delete_vus;
|
const total_max_vu_count = max_read_vus + max_write_vus + max_delete_vus
|
||||||
const total_max_vu_count = max_read_vus + max_write_vus + max_delete_vus
|
|
||||||
|
|
||||||
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
console.log(`Pregenerated read object size: ${read_size}`);
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
||||||
console.log(`Pre allocated reading VUs: ${pre_alloc_read_vus}`);
|
console.log(`Pre allocated reading VUs: ${pre_alloc_read_vus}`);
|
||||||
console.log(`Pre allocated writing VUs: ${pre_alloc_write_vus}`);
|
console.log(`Pre allocated writing VUs: ${pre_alloc_write_vus}`);
|
||||||
console.log(`Pre allocated deleting VUs: ${pre_alloc_delete_vus}`);
|
console.log(`Pre allocated deleting VUs: ${pre_alloc_delete_vus}`);
|
||||||
console.log(`Total pre allocated VUs: ${total_pre_allocated_vu_count}`);
|
console.log(`Total pre allocated VUs: ${total_pre_allocated_vu_count}`);
|
||||||
console.log(`Max reading VUs: ${max_read_vus}`);
|
console.log(`Max reading VUs: ${max_read_vus}`);
|
||||||
console.log(`Max writing VUs: ${max_write_vus}`);
|
console.log(`Max writing VUs: ${max_write_vus}`);
|
||||||
console.log(`Max deleting VUs: ${max_delete_vus}`);
|
console.log(`Max deleting VUs: ${max_delete_vus}`);
|
||||||
console.log(`Total max VUs: ${total_max_vu_count}`);
|
console.log(`Total max VUs: ${total_max_vu_count}`);
|
||||||
console.log(`Time unit: ${time_unit}`);
|
console.log(`Time unit: ${time_unit}`);
|
||||||
console.log(`Read rate: ${read_rate}`);
|
console.log(`Read rate: ${read_rate}`);
|
||||||
console.log(`Writing rate: ${write_rate}`);
|
console.log(`Writing rate: ${write_rate}`);
|
||||||
console.log(`Delete rate: ${delete_rate}`);
|
console.log(`Delete rate: ${delete_rate}`);
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
const start_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load started at: ${Date(start_timestamp).toString()}`)
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
const end_timestamp = Date.now()
|
const end_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load finished at: ${Date(end_timestamp).toString()}`)
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, {indent: ' ', enableColors: false}),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write() {
|
export function obj_write() {
|
||||||
if (__ENV.SLEEP_WRITE) {
|
if (__ENV.SLEEP_WRITE) {
|
||||||
sleep(__ENV.SLEEP_WRITE);
|
sleep(__ENV.SLEEP_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = generateS3Key();
|
const key = __ENV.OBJ_NAME || uuidv4();
|
||||||
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const { payload, hash } = generator.genPayload(registry_enabled);
|
||||||
const resp = s3_client.put(bucket, key, payload);
|
const resp = s3_client.put(bucket, key, payload);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({bucket: bucket, key: key}).error(resp.error);
|
log.withFields({bucket: bucket, key: key}).error(resp.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject('', '', bucket, key, payload.hash());
|
obj_registry.addObject("", "", bucket, key, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_read() {
|
export function obj_read() {
|
||||||
if (__ENV.SLEEP_READ) {
|
if (__ENV.SLEEP_READ) {
|
||||||
sleep(__ENV.SLEEP_READ);
|
sleep(__ENV.SLEEP_READ);
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_to_read_selector) {
|
|
||||||
const obj = obj_to_read_selector.nextObject();
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const resp = s3_client.get(obj.s3_bucket, obj.s3_key)
|
|
||||||
|
if(obj_to_read_selector) {
|
||||||
|
const obj = obj_to_read_selector.nextObject();
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resp = s3_client.get(obj.s3_bucket, obj.s3_key)
|
||||||
|
if (!resp.success) {
|
||||||
|
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key}).error(resp.error);
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
||||||
|
|
||||||
|
const resp = s3_client.get(obj.bucket, obj.object);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key})
|
log.withFields({bucket: obj.bucket, key: obj.object}).error(resp.error);
|
||||||
.error(resp.error);
|
}
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
|
||||||
|
|
||||||
const resp = s3_client.get(obj.bucket, obj.object);
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({bucket: obj.bucket, key: obj.object}).error(resp.error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_delete() {
|
export function obj_delete() {
|
||||||
if (__ENV.SLEEP_DELETE) {
|
if (__ENV.SLEEP_DELETE) {
|
||||||
sleep(__ENV.SLEEP_DELETE);
|
sleep(__ENV.SLEEP_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj = obj_to_delete_selector.nextObject();
|
const obj = obj_to_delete_selector.nextObject();
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = s3_client.delete(obj.s3_bucket, obj.s3_key);
|
const resp = s3_client.delete(obj.s3_bucket, obj.s3_key);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key, op: 'DELETE'})
|
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key, op: "DELETE"}).error(resp.error);
|
||||||
.error(resp.error);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
obj_registry.deleteObject(obj.id);
|
obj_registry.deleteObject(obj.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,233 +0,0 @@
|
||||||
import {sleep} from 'k6';
|
|
||||||
import {SharedArray} from 'k6/data';
|
|
||||||
import exec from 'k6/execution';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
|
||||||
import registry from 'k6/x/frostfs/registry';
|
|
||||||
import s3 from 'k6/x/frostfs/s3';
|
|
||||||
import stats from 'k6/x/frostfs/stats';
|
|
||||||
|
|
||||||
import {generateS3Key} from './libs/keygen.js';
|
|
||||||
import {newGenerator} from './libs/datagen.js';
|
|
||||||
import {parseEnv} from './libs/env-parser.js';
|
|
||||||
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
|
||||||
|
|
||||||
parseEnv();
|
|
||||||
|
|
||||||
const obj_list = new SharedArray(
|
|
||||||
'obj_list',
|
|
||||||
function() { return JSON.parse(open(__ENV.PREGEN_JSON)).objects; });
|
|
||||||
|
|
||||||
const bucket_list = new SharedArray(
|
|
||||||
'bucket_list',
|
|
||||||
function() { return JSON.parse(open(__ENV.PREGEN_JSON)).buckets; });
|
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
|
||||||
|
|
||||||
const no_verify_ssl = __ENV.NO_VERIFY_SSL || 'true';
|
|
||||||
|
|
||||||
const connection_args = {
|
|
||||||
no_verify_ssl : no_verify_ssl
|
|
||||||
}
|
|
||||||
// Select random S3 endpoint for current VU
|
|
||||||
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
|
||||||
const s3_endpoint =
|
|
||||||
s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
|
||||||
const s3_client = s3.connect(s3_endpoint, connection_args);
|
|
||||||
const log = logging.new().withField('endpoint', s3_endpoint);
|
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
|
||||||
const obj_registry =
|
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
const read_age = __ENV.READ_AGE ? parseInt(__ENV.READ_AGE) : 10;
|
|
||||||
let obj_to_read_selector = undefined;
|
|
||||||
if (registry_enabled) {
|
|
||||||
obj_to_read_selector = registry.getSelector(
|
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_read',
|
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
|
||||||
status : 'created',
|
|
||||||
age : read_age,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const scenarios = {};
|
|
||||||
|
|
||||||
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
|
||||||
const generator = newGenerator(write_vu_count > 0);
|
|
||||||
if (write_vu_count > 0) {
|
|
||||||
scenarios.write = {
|
|
||||||
executor : 'constant-vus',
|
|
||||||
vus : write_vu_count,
|
|
||||||
duration : `${duration}s`,
|
|
||||||
exec : 'obj_write',
|
|
||||||
gracefulStop : '5s',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const read_vu_count = parseInt(__ENV.READERS || '0');
|
|
||||||
if (read_vu_count > 0) {
|
|
||||||
scenarios.read = {
|
|
||||||
executor : 'constant-vus',
|
|
||||||
vus : read_vu_count,
|
|
||||||
duration : `${duration}s`,
|
|
||||||
exec : 'obj_read',
|
|
||||||
gracefulStop : '5s',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const delete_age = __ENV.DELETE_AGE ? parseInt(__ENV.DELETE_AGE) : undefined;
|
|
||||||
let obj_to_delete_selector = undefined;
|
|
||||||
let obj_to_delete_exit_on_null = undefined;
|
|
||||||
|
|
||||||
if (registry_enabled ) {
|
|
||||||
obj_to_delete_exit_on_null = (write_vu_count == 0) && (read_vu_count == 0)
|
|
||||||
|
|
||||||
let constructor = obj_to_delete_exit_on_null ? registry.getOneshotSelector
|
|
||||||
: registry.getSelector;
|
|
||||||
|
|
||||||
obj_to_delete_selector =
|
|
||||||
constructor(__ENV.REGISTRY_FILE, 'obj_to_delete',
|
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
|
||||||
status : 'read',
|
|
||||||
age : delete_age,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const delete_vu_count = parseInt(__ENV.DELETERS || '0');
|
|
||||||
if (delete_vu_count > 0) {
|
|
||||||
if (!obj_to_delete_selector) {
|
|
||||||
throw 'Positive DELETE worker number without a proper object selector';
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios.delete = {
|
|
||||||
executor : 'constant-vus',
|
|
||||||
vus : delete_vu_count,
|
|
||||||
duration : `${duration}s`,
|
|
||||||
exec : 'obj_delete',
|
|
||||||
gracefulStop : '5s',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const options = {
|
|
||||||
scenarios,
|
|
||||||
setupTimeout : '5s',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function setup() {
|
|
||||||
const total_vu_count = write_vu_count + read_vu_count + delete_vu_count;
|
|
||||||
|
|
||||||
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
|
||||||
console.log(`Reading VUs: ${read_vu_count}`);
|
|
||||||
console.log(`Writing VUs: ${write_vu_count}`);
|
|
||||||
console.log(`Deleting VUs: ${delete_vu_count}`);
|
|
||||||
console.log(`Total VUs: ${total_vu_count}`);
|
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
|
||||||
console.log(
|
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function teardown(data) {
|
|
||||||
if (obj_registry) {
|
|
||||||
obj_registry.close();
|
|
||||||
}
|
|
||||||
const end_timestamp = Date.now()
|
|
||||||
console.log(
|
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleSummary(data) {
|
|
||||||
return {
|
|
||||||
'stdout' : textSummary(data, {indent : ' ', enableColors : false}),
|
|
||||||
[summary_json] : JSON.stringify(data),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function obj_write() {
|
|
||||||
if (__ENV.SLEEP_WRITE) {
|
|
||||||
sleep(__ENV.SLEEP_WRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = generateS3Key();
|
|
||||||
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
|
||||||
const resp = s3_client.put(bucket, key, payload);
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({bucket : bucket, key : key}).error(resp.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_registry) {
|
|
||||||
obj_registry.addObject('', '', bucket, key, payload.hash());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function obj_read() {
|
|
||||||
if (__ENV.SLEEP_READ) {
|
|
||||||
sleep(__ENV.SLEEP_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_to_read_selector) {
|
|
||||||
const obj = obj_to_read_selector.nextObject();
|
|
||||||
if (!obj ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const resp = s3_client.get(obj.s3_bucket, obj.s3_key)
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({bucket : obj.s3_bucket, key : obj.s3_key, status: obj.status, op: `READ`})
|
|
||||||
.error(resp.error);
|
|
||||||
} else {
|
|
||||||
obj_registry.setObjectStatus(obj.id, obj.status, 'read');
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
|
||||||
|
|
||||||
const resp = s3_client.get(obj.bucket, obj.object);
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({bucket : obj.bucket, key : obj.object}).error(resp.error);
|
|
||||||
} else {
|
|
||||||
obj_registry.setObjectStatus(obj.id, obj.status, 'read');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function obj_delete() {
|
|
||||||
if (__ENV.SLEEP_DELETE) {
|
|
||||||
sleep(__ENV.SLEEP_DELETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_to_delete_selector.nextObject();
|
|
||||||
delete_object(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function delete_object(obj) {
|
|
||||||
if (!obj) {
|
|
||||||
if (obj_to_delete_exit_on_null) {
|
|
||||||
exec.test.abort("No more objects to select");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resp = s3_client.delete(obj.s3_bucket, obj.s3_key);
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({bucket : obj.s3_bucket, key : obj.s3_key, op : 'DELETE'})
|
|
||||||
.error(resp.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj_registry.deleteObject(obj.id);
|
|
||||||
}
|
|
|
@ -1,119 +1,104 @@
|
||||||
import {sleep} from 'k6';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import {SharedArray} from 'k6/data';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import s3 from 'k6/x/frostfs/s3';
|
import s3 from 'k6/x/frostfs/s3';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import {SharedArray} from 'k6/data';
|
||||||
|
import {sleep} from 'k6';
|
||||||
import {generateS3Key} from './libs/keygen.js';
|
|
||||||
import {newGenerator} from './libs/datagen.js';
|
|
||||||
import {parseEnv} from './libs/env-parser.js';
|
|
||||||
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
||||||
|
import {parseEnv} from './libs/env-parser.js';
|
||||||
|
import {uuidv4} from './libs/k6-utils-1.4.0.js';
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const bucket_list = new SharedArray('bucket_list', function() {
|
const bucket_list = new SharedArray('bucket_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).buckets;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).buckets;
|
||||||
});
|
});
|
||||||
|
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
// Select random S3 endpoint for current VU
|
// Select random S3 endpoint for current VU
|
||||||
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
||||||
const s3_endpoint =
|
const s3_endpoint = s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
||||||
s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
const s3_client = s3.connect(`http://${s3_endpoint}`);
|
||||||
const no_verify_ssl = __ENV.NO_VERIFY_SSL || 'true';
|
const log = logging.new().withField("endpoint", s3_endpoint);
|
||||||
const connection_args = {
|
|
||||||
no_verify_ssl: no_verify_ssl
|
|
||||||
};
|
|
||||||
const s3_client = s3.connect(s3_endpoint, connection_args);
|
|
||||||
const log = logging.new().withField('endpoint', s3_endpoint);
|
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE), __ENV.PAYLOAD_TYPE || "");
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
||||||
if (write_vu_count < 1) {
|
if (write_vu_count < 1) {
|
||||||
throw 'number of VUs (env WRITERS) performing write operations should be greater than 0';
|
throw 'number of VUs (env WRITERS) performing write operations should be greater than 0';
|
||||||
}
|
}
|
||||||
|
|
||||||
const write_multipart_vu_count = parseInt(__ENV.WRITERS_MULTIPART || '0');
|
const write_multipart_vu_count = parseInt(__ENV.WRITERS_MULTIPART || '0');
|
||||||
if (write_multipart_vu_count < 1) {
|
if (write_multipart_vu_count < 1) {
|
||||||
throw 'number of parts (env WRITERS_MULTIPART) to upload in parallel should be greater than 0';
|
throw 'number of parts (env WRITERS_MULTIPART) to upload in parallel should be greater than 0';
|
||||||
}
|
}
|
||||||
|
|
||||||
const generator =
|
|
||||||
newGenerator(write_vu_count > 0 || write_multipart_vu_count > 0);
|
|
||||||
if (write_vu_count > 0) {
|
if (write_vu_count > 0) {
|
||||||
scenarios.write_multipart = {
|
scenarios.write_multipart = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: write_vu_count,
|
vus: write_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_write_multipart',
|
exec: 'obj_write_multipart',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_vu_count = write_vu_count * write_multipart_vu_count;
|
const total_vu_count = write_vu_count * write_multipart_vu_count;
|
||||||
|
|
||||||
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
||||||
console.log(`Writing VUs: ${write_vu_count}`);
|
console.log(`Writing VUs: ${write_vu_count}`);
|
||||||
console.log(`Writing multipart VUs: ${write_multipart_vu_count}`);
|
console.log(`Writing multipart VUs: ${write_multipart_vu_count}`);
|
||||||
console.log(`Total VUs: ${total_vu_count}`);
|
console.log(`Total VUs: ${total_vu_count}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, {indent: ' ', enableColors: false}),
|
'stdout': textSummary(data, {indent: ' ', enableColors: false}),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const write_multipart_part_size =
|
const write_multipart_part_size = 1024 * parseInt(__ENV.WRITE_OBJ_PART_SIZE || '0')
|
||||||
1024 * parseInt(__ENV.WRITE_OBJ_PART_SIZE || '0')
|
|
||||||
if (write_multipart_part_size < 5 * 1024 * 1024) {
|
if (write_multipart_part_size < 5 * 1024 * 1024) {
|
||||||
throw 'part size (env WRITE_OBJ_PART_SIZE * 1024) must be greater than (5 MB)';
|
throw 'part size (env WRITE_OBJ_PART_SIZE * 1024) must be greater than (5 MB)';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write_multipart() {
|
export function obj_write_multipart() {
|
||||||
if (__ENV.SLEEP_WRITE) {
|
if (__ENV.SLEEP_WRITE) {
|
||||||
sleep(__ENV.SLEEP_WRITE);
|
sleep(__ENV.SLEEP_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = generateS3Key();
|
const key = __ENV.OBJ_NAME || uuidv4();
|
||||||
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const {payload, hash} = generator.genPayload(registry_enabled);
|
||||||
const resp = s3_client.multipart(
|
const resp = s3_client.multipart(bucket, key, write_multipart_part_size, write_multipart_vu_count, payload);
|
||||||
bucket, key, write_multipart_part_size, write_multipart_vu_count,
|
if (!resp.success) {
|
||||||
payload);
|
log.withFields({bucket: bucket, key: key}).error(resp.error);
|
||||||
if (!resp.success) {
|
return;
|
||||||
log.withFields({bucket: bucket, key: key}).error(resp.error);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject('', '', bucket, key, payload.hash());
|
obj_registry.addObject("", "", bucket, key, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,173 +1,156 @@
|
||||||
import {SharedArray} from 'k6/data';
|
import datagen from 'k6/x/frostfs/datagen';
|
||||||
import exec from 'k6/execution';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
import logging from 'k6/x/frostfs/logging';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import s3local from 'k6/x/frostfs/s3local';
|
import s3local from 'k6/x/frostfs/s3local';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import { SharedArray } from 'k6/data';
|
||||||
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
import {generateS3Key} from './libs/keygen.js';
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
import {newGenerator} from './libs/datagen.js';
|
import { uuidv4 } from './libs/k6-utils-1.4.0.js';
|
||||||
import {parseEnv} from './libs/env-parser.js';
|
|
||||||
import {textSummary} from './libs/k6-summary-0.0.2.js';
|
|
||||||
import {uuidv4} from './libs/k6-utils-1.4.0.js';
|
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_list = new SharedArray('obj_list', function() {
|
const obj_list = new SharedArray('obj_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).objects;
|
||||||
});
|
});
|
||||||
|
|
||||||
const container_list = new SharedArray('container_list', function() {
|
const container_list = new SharedArray('container_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).containers;
|
||||||
});
|
});
|
||||||
|
|
||||||
const bucket_list = new SharedArray('bucket_list', function() {
|
const bucket_list = new SharedArray('bucket_list', function () {
|
||||||
return JSON.parse(open(__ENV.PREGEN_JSON)).buckets;
|
return JSON.parse(open(__ENV.PREGEN_JSON)).buckets;
|
||||||
});
|
});
|
||||||
|
|
||||||
function bucket_mapping() {
|
function bucket_mapping() {
|
||||||
if (container_list.length != bucket_list.length) {
|
if (container_list.length != bucket_list.length) {
|
||||||
throw 'The number of containers and buckets in the preset file must be the same.';
|
throw 'The number of containers and buckets in the preset file must be the same.';
|
||||||
}
|
}
|
||||||
let mapping = {};
|
let mapping = {};
|
||||||
for (let i = 0; i < container_list.length; ++i) {
|
for (let i = 0; i < container_list.length; ++i) {
|
||||||
mapping[bucket_list[i]] = container_list[i];
|
mapping[bucket_list[i]] = container_list[i];
|
||||||
}
|
}
|
||||||
return mapping;
|
return mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
const read_size = JSON.parse(open(__ENV.PREGEN_JSON)).obj_size;
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
const config_file = __ENV.CONFIG_FILE;
|
const config_file = __ENV.CONFIG_FILE;
|
||||||
const config_dir = __ENV.CONFIG_DIR;
|
const s3_client = s3local.connect(config_file, {
|
||||||
const max_total_size_gb =
|
'debug_logger': __ENV.DEBUG_LOGGER || 'false',
|
||||||
__ENV.MAX_TOTAL_SIZE_GB ? parseInt(__ENV.MAX_TOTAL_SIZE_GB) : 0;
|
}, bucket_mapping());
|
||||||
const s3_client = s3local.connect(
|
const log = logging.new().withField("config", config_file);
|
||||||
config_file, config_dir, {
|
|
||||||
'debug_logger': __ENV.DEBUG_LOGGER || 'false',
|
|
||||||
},
|
|
||||||
bucket_mapping(), max_total_size_gb);
|
|
||||||
const log = logging.new().withFields(
|
|
||||||
{'config_file': config_file, 'config_dir': config_dir});
|
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
const registry_enabled = !!__ENV.REGISTRY_FILE;
|
||||||
const obj_registry =
|
const obj_registry = registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
||||||
registry_enabled ? registry.open(__ENV.REGISTRY_FILE) : undefined;
|
|
||||||
|
|
||||||
let obj_to_read_selector = undefined;
|
let obj_to_read_selector = undefined;
|
||||||
if (registry_enabled) {
|
if (registry_enabled) {
|
||||||
obj_to_read_selector = registry.getLoopedSelector(
|
obj_to_read_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_read',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_read",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
})
|
{
|
||||||
|
status: "created",
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const duration = __ENV.DURATION;
|
const duration = __ENV.DURATION;
|
||||||
|
|
||||||
|
const generator = datagen.generator(1024 * parseInt(__ENV.WRITE_OBJ_SIZE));
|
||||||
|
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
|
|
||||||
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
const write_vu_count = parseInt(__ENV.WRITERS || '0');
|
||||||
const generator = newGenerator(write_vu_count > 0);
|
|
||||||
if (write_vu_count > 0) {
|
if (write_vu_count > 0) {
|
||||||
scenarios.write = {
|
scenarios.write = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: write_vu_count,
|
vus: write_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_write',
|
exec: 'obj_write',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const read_vu_count = parseInt(__ENV.READERS || '0');
|
const read_vu_count = parseInt(__ENV.READERS || '0');
|
||||||
if (read_vu_count > 0) {
|
if (read_vu_count > 0) {
|
||||||
scenarios.read = {
|
scenarios.read = {
|
||||||
executor: 'constant-vus',
|
executor: 'constant-vus',
|
||||||
vus: read_vu_count,
|
vus: read_vu_count,
|
||||||
duration: `${duration}s`,
|
duration: `${duration}s`,
|
||||||
exec: 'obj_read',
|
exec: 'obj_read',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
const total_vu_count = write_vu_count + read_vu_count;
|
const total_vu_count = write_vu_count + read_vu_count;
|
||||||
|
|
||||||
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
console.log(`Pregenerated buckets: ${bucket_list.length}`);
|
||||||
console.log(`Pregenerated read object size: ${read_size}`);
|
console.log(`Pregenerated read object size: ${read_size}`);
|
||||||
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
console.log(`Pregenerated total objects: ${obj_list.length}`);
|
||||||
console.log(`Reading VUs: ${read_vu_count}`);
|
console.log(`Reading VUs: ${read_vu_count}`);
|
||||||
console.log(`Writing VUs: ${write_vu_count}`);
|
console.log(`Writing VUs: ${write_vu_count}`);
|
||||||
console.log(`Total VUs: ${total_vu_count}`);
|
console.log(`Total VUs: ${total_vu_count}`);
|
||||||
|
|
||||||
const start_timestamp = Date.now()
|
const start_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load started at: ${Date(start_timestamp).toString()}`)
|
||||||
`Load started at: ${Date(start_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function teardown(data) {
|
export function teardown(data) {
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.close();
|
obj_registry.close();
|
||||||
}
|
}
|
||||||
const end_timestamp = Date.now()
|
const end_timestamp = Date.now()
|
||||||
console.log(
|
console.log(`Load finished at: ${Date(end_timestamp).toString()}`)
|
||||||
`Load finished at: ${Date(end_timestamp).toString()}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, {indent: ' ', enableColors: false}),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_write() {
|
export function obj_write() {
|
||||||
const key = generateS3Key();
|
const key = __ENV.OBJ_NAME || uuidv4();
|
||||||
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
const bucket = bucket_list[Math.floor(Math.random() * bucket_list.length)];
|
||||||
|
|
||||||
const payload = generator.genPayload();
|
const { payload, hash } = generator.genPayload(registry_enabled);
|
||||||
const resp = s3_client.put(bucket, key, payload);
|
const resp = s3_client.put(bucket, key, payload);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
if (resp.abort) {
|
log.withFields({bucket: bucket, key: key}).error(resp.error);
|
||||||
exec.test.abort(resp.error);
|
return;
|
||||||
}
|
}
|
||||||
log.withFields({bucket: bucket, key: key}).error(resp.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_registry) {
|
if (obj_registry) {
|
||||||
obj_registry.addObject('', '', bucket, key, payload.hash());
|
obj_registry.addObject("", "", bucket, key, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_read() {
|
export function obj_read() {
|
||||||
if (obj_to_read_selector) {
|
if(obj_to_read_selector) {
|
||||||
const obj = obj_to_read_selector.nextObject();
|
const obj = obj_to_read_selector.nextObject();
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
const resp = s3_client.get(obj.s3_bucket, obj.s3_key)
|
||||||
|
if (!resp.success) {
|
||||||
|
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key}).error(resp.error);
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
const resp = s3_client.get(obj.s3_bucket, obj.s3_key)
|
|
||||||
|
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
||||||
|
|
||||||
|
const resp = s3_client.get(obj.bucket, obj.object);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
log.withFields({bucket: obj.s3_bucket, key: obj.s3_key})
|
log.withFields({bucket: obj.bucket, key: obj.object}).error(resp.error);
|
||||||
.error(resp.error);
|
}
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = obj_list[Math.floor(Math.random() * obj_list.length)];
|
|
||||||
|
|
||||||
const resp = s3_client.get(obj.bucket, obj.object);
|
|
||||||
if (!resp.success) {
|
|
||||||
log.withFields({bucket: obj.bucket, key: obj.object}).error(resp.error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
import { sleep } from 'k6';
|
|
||||||
import { Counter } from 'k6/metrics';
|
|
||||||
import logging from 'k6/x/frostfs/logging';
|
|
||||||
import native from 'k6/x/frostfs/native';
|
import native from 'k6/x/frostfs/native';
|
||||||
import registry from 'k6/x/frostfs/registry';
|
import registry from 'k6/x/frostfs/registry';
|
||||||
import s3 from 'k6/x/frostfs/s3';
|
import s3 from 'k6/x/frostfs/s3';
|
||||||
import stats from 'k6/x/frostfs/stats';
|
import logging from 'k6/x/frostfs/logging';
|
||||||
|
import { sleep } from 'k6';
|
||||||
import { parseEnv } from './libs/env-parser.js';
|
import { Counter } from 'k6/metrics';
|
||||||
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
import { textSummary } from './libs/k6-summary-0.0.2.js';
|
||||||
|
import { parseEnv } from './libs/env-parser.js';
|
||||||
|
|
||||||
parseEnv();
|
parseEnv();
|
||||||
|
|
||||||
const obj_registry = registry.open(__ENV.REGISTRY_FILE);
|
const obj_registry = registry.open(__ENV.REGISTRY_FILE);
|
||||||
|
|
||||||
// Time limit (in seconds) for the run
|
// Time limit (in seconds) for the run
|
||||||
const time_limit = __ENV.TIME_LIMIT || '60';
|
const time_limit = __ENV.TIME_LIMIT || "60";
|
||||||
const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
const summary_json = __ENV.SUMMARY_JSON || "/tmp/summary.json";
|
||||||
|
|
||||||
// Number of objects in each status. These counters are cumulative in a
|
// Number of objects in each status. These counters are cumulative in a
|
||||||
// sense that they reflect total number of objects in the registry, not just
|
// sense that they reflect total number of objects in the registry, not just
|
||||||
|
@ -23,147 +21,140 @@ const summary_json = __ENV.SUMMARY_JSON || '/tmp/summary.json';
|
||||||
// This allows to run this scenario multiple times and collect overall
|
// This allows to run this scenario multiple times and collect overall
|
||||||
// statistics in the final run.
|
// statistics in the final run.
|
||||||
const obj_counters = {
|
const obj_counters = {
|
||||||
verified: new Counter('verified_obj'),
|
verified: new Counter('verified_obj'),
|
||||||
skipped: new Counter('skipped_obj'),
|
skipped: new Counter('skipped_obj'),
|
||||||
invalid: new Counter('invalid_obj'),
|
invalid: new Counter('invalid_obj'),
|
||||||
};
|
};
|
||||||
|
|
||||||
let log = logging.new();
|
let log = logging.new();
|
||||||
|
|
||||||
if (!!__ENV.METRIC_TAGS) {
|
|
||||||
stats.setTags(__ENV.METRIC_TAGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to random gRPC endpoint
|
// Connect to random gRPC endpoint
|
||||||
let grpc_client = undefined;
|
let grpc_client = undefined;
|
||||||
if (__ENV.GRPC_ENDPOINTS) {
|
if (__ENV.GRPC_ENDPOINTS) {
|
||||||
const grpcEndpoints = __ENV.GRPC_ENDPOINTS.split(',');
|
const grpcEndpoints = __ENV.GRPC_ENDPOINTS.split(',');
|
||||||
const grpcEndpoint =
|
const grpcEndpoint = grpcEndpoints[Math.floor(Math.random() * grpcEndpoints.length)];
|
||||||
grpcEndpoints[Math.floor(Math.random() * grpcEndpoints.length)];
|
log = log.withField("endpoint", grpcEndpoint);
|
||||||
log = log.withField('endpoint', grpcEndpoint);
|
grpc_client = native.connect(grpcEndpoint, '',
|
||||||
grpc_client = native.connect(
|
__ENV.DIAL_TIMEOUT ? parseInt(__ENV.DIAL_TIMEOUT) : 0,
|
||||||
grpcEndpoint, '', __ENV.DIAL_TIMEOUT ? parseInt(__ENV.DIAL_TIMEOUT) : 0,
|
__ENV.STREAM_TIMEOUT ? parseInt(__ENV.STREAM_TIMEOUT) : 0,
|
||||||
__ENV.STREAM_TIMEOUT ? parseInt(__ENV.STREAM_TIMEOUT) : 0,
|
__ENV.PREPARE_LOCALLY ? __ENV.PREPARE_LOCALLY.toLowerCase() === "true" : false, '');
|
||||||
__ENV.PREPARE_LOCALLY ? __ENV.PREPARE_LOCALLY.toLowerCase() === 'true' : false,
|
|
||||||
1024 * parseInt(__ENV.MAX_OBJECT_SIZE || '0'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to random S3 endpoint
|
// Connect to random S3 endpoint
|
||||||
let s3_client = undefined;
|
let s3_client = undefined;
|
||||||
if (__ENV.S3_ENDPOINTS) {
|
if (__ENV.S3_ENDPOINTS) {
|
||||||
const no_verify_ssl = __ENV.NO_VERIFY_SSL || 'true';
|
const no_verify_ssl = __ENV.NO_VERIFY_SSL || "true";
|
||||||
const connection_args = { no_verify_ssl: no_verify_ssl };
|
const connection_args = {no_verify_ssl: no_verify_ssl}
|
||||||
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
const s3_endpoints = __ENV.S3_ENDPOINTS.split(',');
|
||||||
const s3_endpoint =
|
const s3_endpoint = s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
||||||
s3_endpoints[Math.floor(Math.random() * s3_endpoints.length)];
|
log = log.withField("endpoint", s3_endpoint);
|
||||||
log = log.withField('endpoint', s3_endpoint);
|
s3_client = s3.connect(s3_endpoint, connection_args);
|
||||||
s3_client = s3.connect(s3_endpoint, connection_args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We will attempt to verify every object in "created" status. The scenario will
|
// We will attempt to verify every object in "created" status. The scenario will execute
|
||||||
// execute as many iterations as there are objects. Each object will have 3
|
// as many iterations as there are objects. Each object will have 3 retries to be verified
|
||||||
// retries to be verified
|
|
||||||
const obj_to_verify_selector = registry.getSelector(
|
const obj_to_verify_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, 'obj_to_verify',
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, {
|
"obj_to_verify",
|
||||||
status: 'created',
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
});
|
{
|
||||||
|
status: "created",
|
||||||
|
}
|
||||||
|
);
|
||||||
const obj_to_verify_count = obj_to_verify_selector.count();
|
const obj_to_verify_count = obj_to_verify_selector.count();
|
||||||
// Execute at least one iteration (executor shared-iterations can't run 0
|
// Execute at least one iteration (executor shared-iterations can't run 0 iterations)
|
||||||
// iterations)
|
|
||||||
const iterations = Math.max(1, obj_to_verify_count);
|
const iterations = Math.max(1, obj_to_verify_count);
|
||||||
// Executor shared-iterations requires number of iterations to be larger than
|
// Executor shared-iterations requires number of iterations to be larger than number of VUs
|
||||||
// number of VUs
|
|
||||||
const vus = Math.min(__ENV.CLIENTS, iterations);
|
const vus = Math.min(__ENV.CLIENTS, iterations);
|
||||||
|
|
||||||
const scenarios = {
|
const scenarios = {
|
||||||
verify: {
|
verify: {
|
||||||
executor: 'shared-iterations',
|
executor: 'shared-iterations',
|
||||||
vus,
|
vus,
|
||||||
iterations,
|
iterations,
|
||||||
maxDuration: `${time_limit}s`,
|
maxDuration: `${time_limit}s`,
|
||||||
exec: 'obj_verify',
|
exec: 'obj_verify',
|
||||||
gracefulStop: '5s',
|
gracefulStop: '5s',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const options = {
|
export const options = {
|
||||||
scenarios,
|
scenarios,
|
||||||
setupTimeout: '5s',
|
setupTimeout: '5s',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setup() {
|
export function setup() {
|
||||||
// Populate counters with initial values
|
// Populate counters with initial values
|
||||||
for (const [status, counter] of Object.entries(obj_counters)) {
|
for (const [status, counter] of Object.entries(obj_counters)) {
|
||||||
const obj_selector = registry.getSelector(
|
const obj_selector = registry.getSelector(
|
||||||
__ENV.REGISTRY_FILE, status,
|
__ENV.REGISTRY_FILE,
|
||||||
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0, { status });
|
status,
|
||||||
counter.add(obj_selector.count());
|
__ENV.SELECTION_SIZE ? parseInt(__ENV.SELECTION_SIZE) : 0,
|
||||||
}
|
{ status });
|
||||||
|
counter.add(obj_selector.count());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSummary(data) {
|
export function handleSummary(data) {
|
||||||
return {
|
return {
|
||||||
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
'stdout': textSummary(data, { indent: ' ', enableColors: false }),
|
||||||
[summary_json]: JSON.stringify(data),
|
[summary_json]: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function obj_verify() {
|
export function obj_verify() {
|
||||||
if (obj_to_verify_count == 0) {
|
if (obj_to_verify_count == 0) {
|
||||||
log.info('Nothing to verify');
|
log.info("Nothing to verify");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__ENV.SLEEP) {
|
if (__ENV.SLEEP) {
|
||||||
sleep(__ENV.SLEEP);
|
sleep(__ENV.SLEEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj = obj_to_verify_selector.nextObject();
|
const obj = obj_to_verify_selector.nextObject();
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
log.info('All objects have been verified');
|
log.info("All objects have been verified");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj_status = verify_object_with_retries(obj, 3);
|
const obj_status = verify_object_with_retries(obj, 3);
|
||||||
obj_counters[obj_status].add(1);
|
obj_counters[obj_status].add(1);
|
||||||
obj_registry.setObjectStatus(obj.id, obj.status, obj_status);
|
obj_registry.setObjectStatus(obj.id, obj.status, obj_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
function verify_object_with_retries(obj, attempts) {
|
function verify_object_with_retries(obj, attempts) {
|
||||||
for (let i = 0; i < attempts; i++) {
|
for (let i = 0; i < attempts; i++) {
|
||||||
let result;
|
let result;
|
||||||
// Different name is required.
|
// Different name is required.
|
||||||
// ReferenceError: Cannot access a variable before initialization.
|
// ReferenceError: Cannot access a variable before initialization.
|
||||||
let lg = log;
|
let lg = log;
|
||||||
if (obj.c_id && obj.o_id) {
|
if (obj.c_id && obj.o_id) {
|
||||||
lg = lg.withFields({ cid: obj.c_id, oid: obj.o_id });
|
lg = lg.withFields({cid: obj.c_id, oid: obj.o_id});
|
||||||
result = grpc_client.verifyHash(obj.c_id, obj.o_id, obj.payload_hash);
|
result = grpc_client.verifyHash(obj.c_id, obj.o_id, obj.payload_hash);
|
||||||
} else if (obj.s3_bucket && obj.s3_key) {
|
} else if (obj.s3_bucket && obj.s3_key) {
|
||||||
lg = lg.withFields({ bucket: obj.s3_bucket, key: obj.s3_key });
|
lg = lg.withFields({bucket: obj.s3_bucket, key: obj.s3_key});
|
||||||
result =
|
result = s3_client.verifyHash(obj.s3_bucket, obj.s3_key, obj.payload_hash);
|
||||||
s3_client.verifyHash(obj.s3_bucket, obj.s3_key, obj.payload_hash);
|
} else {
|
||||||
} else {
|
lg.withFields({
|
||||||
lg.withFields({
|
cid: obj.c_id,
|
||||||
cid: obj.c_id,
|
oid: obj.o_id,
|
||||||
oid: obj.o_id,
|
bucket: obj.s3_bucket,
|
||||||
bucket: obj.s3_bucket,
|
key: obj.s3_key
|
||||||
key: obj.s3_key
|
}).warn(`Object cannot be verified with supported protocols`);
|
||||||
}).warn(`Object cannot be verified with supported protocols`);
|
return "skipped";
|
||||||
return 'skipped';
|
}
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
return "verified";
|
||||||
|
} else if (result.error == "hash mismatch") {
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unless we explicitly saw that there was a hash mismatch, then we will retry after a delay
|
||||||
|
lg.error(`Verify error: ${result.error}. Object will be re-tried`);
|
||||||
|
sleep(__ENV.SLEEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.success) {
|
return "invalid";
|
||||||
return 'verified';
|
|
||||||
} else if (result.error == 'hash mismatch') {
|
|
||||||
return 'invalid';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unless we explicitly saw that there was a hash mismatch, then we will
|
|
||||||
// retry after a delay
|
|
||||||
lg.error(`Verify error: ${result.error}. Object will be re-tried`);
|
|
||||||
sleep(__ENV.SLEEP);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'invalid';
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue