Compare commits

..

1 commit

Author SHA1 Message Date
9ce6eed5ad [#121] client: Make PrmObjectPutSingle fields public
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2023-10-19 10:58:55 +03:00
192 changed files with 5351 additions and 11107 deletions

View file

@ -13,9 +13,9 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.23' go-version: '1.21'
- name: Run commit format checker - name: Run commit format checker
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3 uses: https://git.frostfs.info/TrueCloudLab/dco-go@v2
with: with:
from: 'origin/${{ github.event.pull_request.base.ref }}' from: 'origin/${{ github.event.pull_request.base.ref }}'

View file

@ -8,24 +8,17 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Go - name: golangci-lint
uses: actions/setup-go@v3 uses: https://github.com/golangci/golangci-lint-action@v2
with: with:
go-version: '1.23' version: latest
cache: true
- name: Install linters
run: make lint-install
- name: Run linters
run: make lint
tests: tests:
name: Tests name: Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
go_versions: [ '1.22', '1.23' ] go_versions: [ '1.19', '1.20' ]
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

6
.gitignore vendored
View file

@ -23,11 +23,7 @@ coverage.txt
coverage.html coverage.html
# antlr tool jar # antlr tool jar
antlr*.jar antlr-*.jar
# tempfiles # tempfiles
.cache .cache
# binary
bin/
release/

10
.gitlint Normal file
View file

@ -0,0 +1,10 @@
[general]
fail-without-commits=true
contrib=CC1
[title-match-regex]
regex=^\[\#[0-9Xx]+\]\s
[ignore-by-title]
regex=^Release(.*)
ignore=title-match-regex

View file

@ -12,8 +12,7 @@ run:
# output configuration options # output configuration options
output: output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
formats: format: tab
- format: tab
# all available settings of specific linters # all available settings of specific linters
linters-settings: linters-settings:
@ -26,7 +25,7 @@ linters-settings:
# report about shadowed variables # report about shadowed variables
check-shadowing: false check-shadowing: false
staticcheck: staticcheck:
checks: ["all"] checks: ["all", "-SA1019"] # TODO Enable SA1019 after deprecated warning are fixed.
funlen: funlen:
lines: 80 # default 60 lines: 80 # default 60
statements: 60 # default 40 statements: 60 # default 40
@ -63,6 +62,5 @@ linters:
- funlen - funlen
- gocognit - gocognit
- contextcheck - contextcheck
- protogetter
disable-all: true disable-all: true
fast: false fast: false

View file

@ -18,19 +18,13 @@ repos:
- id: end-of-file-fixer - id: end-of-file-fixer
exclude: "(.key|.interp|.tokens)$" exclude: "(.key|.interp|.tokens)$"
- repo: local - repo: https://github.com/golangci/golangci-lint
rev: v1.51.2
hooks: hooks:
- id: go-unit-tests - id: golangci-lint
name: go unit tests
entry: make test GOFLAGS=''
pass_filenames: false
types: [go]
language: system
- repo: local - repo: https://github.com/jorisroovers/gitlint
rev: v0.18.0
hooks: hooks:
- id: make-lint - id: gitlint
name: Run Make Lint stages: [commit-msg]
entry: make lint
language: system
pass_filenames: false

View file

@ -1,4 +1,4 @@
FROM golang:1.22 FROM golang:1.21
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install make openjdk-17-jre -y RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install make openjdk-11-jre -y
WORKDIR /work WORKDIR /work

View file

@ -1,16 +1,10 @@
#!/usr/bin/make -f #!/usr/bin/make -f
ANTLR_VERSION=4.13.1 ANTLR_VERSION="4.13.0"
TMP_DIR := .cache
LINT_VERSION ?= 1.60.1
TRUECLOUDLAB_LINT_VERSION ?= 0.0.6
OUTPUT_LINT_DIR ?= $(shell pwd)/bin
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
# Run tests # Run tests
test: GOFLAGS ?= "-cover -count=1"
test: test:
@GOFLAGS=$(GOFLAGS) go test ./... @go test ./... -cover
# Pull go dependencies # Pull go dependencies
dep: dep:
@ -21,23 +15,9 @@ dep:
@CGO_ENABLED=0 \ @CGO_ENABLED=0 \
go mod tidy -v && echo OK go mod tidy -v && echo OK
# Install linters
lint-install:
@mkdir -p $(TMP_DIR)
@rm -rf $(TMP_DIR)/linters
@git -c advice.detachedHead=false clone --branch v$(TRUECLOUDLAB_LINT_VERSION) https://git.frostfs.info/TrueCloudLab/linters.git $(TMP_DIR)/linters
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
@rm -rf $(TMP_DIR)/linters
@rmdir $(TMP_DIR) 2>/dev/null || true
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
# Run linters # Run linters
lint: lint:
@if [ ! -d "$(LINT_DIR)" ]; then \ @golangci-lint --timeout=5m run
echo "Run make lint-install"; \
exit 1; \
fi
$(LINT_DIR)/golangci-lint run
# Run tests with race detection and produce coverage output # Run tests with race detection and produce coverage output
cover: cover:
@ -53,8 +33,7 @@ format:
policy: policy:
@wget -q https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar -O antlr4-tool.jar @wget -q https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar -O antlr4-tool.jar
@java -Xmx500M -cp antlr4-tool.jar org.antlr.v4.Tool -Dlanguage=Go \ @java -Xmx500M -cp "`pwd`/antlr4-tool.jar" "org.antlr.v4.Tool" -o `pwd`/netmap/parser/ -Dlanguage=Go -no-listener -visitor `pwd`/netmap/parser/Query.g4 `pwd`/netmap/parser/QueryLexer.g4
-no-listener -visitor netmap/parser/Query.g4 netmap/parser/QueryLexer.g4
# Run `make %` in truecloudlab/frostfs-sdk-go container(Golang+Java) # Run `make %` in truecloudlab/frostfs-sdk-go container(Golang+Java)
docker/%: docker/%:
@ -78,15 +57,3 @@ help:
@echo ' Targets:' @echo ' Targets:'
@echo '' @echo ''
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u @awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u
# Activate pre-commit hooks
pre-commit:
pre-commit install --hook-type pre-commit
# Deactivate pre-commit hooks
unpre-commit:
pre-commit uninstall --hook-type pre-commit
# Run pre-commit hooks
pre-commit-run:
@pre-commit run --all-files --hook-stage manual

View file

@ -42,6 +42,7 @@ Contains client for working with FrostFS.
```go ```go
var prmInit client.PrmInit var prmInit client.PrmInit
prmInit.SetDefaultPrivateKey(key) // private key for request signing prmInit.SetDefaultPrivateKey(key) // private key for request signing
prmInit.ResolveFrostFSFailures() // enable erroneous status parsing
var c client.Client var c client.Client
c.Init(prmInit) c.Init(prmInit)
@ -76,7 +77,8 @@ if needed and perform any desired action. In the case above we may want to repor
these details to the user as well as retry an operation, possibly with different parameters. these details to the user as well as retry an operation, possibly with different parameters.
Status wire-format is extendable and each node can report any set of details it wants. Status wire-format is extendable and each node can report any set of details it wants.
The set of reserved status codes can be found in The set of reserved status codes can be found in
[FrostFS API](https://git.frostfs.info/TrueCloudLab/frostfs-api/src/branch/master/status/types.proto). [FrostFS API](https://git.frostfs.info/TrueCloudLab/frostfs-api/src/branch/master/status/types.proto). There is also
a `client.PrmInit.ResolveFrostFSFailures()` to seamlessly convert erroneous statuses into Go error type.
### policy ### policy
Contains helpers allowing conversion of placing policy from/to JSON representation Contains helpers allowing conversion of placing policy from/to JSON representation

View file

@ -1,52 +0,0 @@
package ape
import (
"errors"
"fmt"
apeV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/ape"
)
var (
ErrInvalidChainRepresentation = errors.New("invalid chain representation")
)
// ChainID is Chain's identifier.
type ChainID []byte
// Chain is an SDK representation for v2's Chain.
//
// Note that Chain (as well as v2's Chain) and all related entities
// are NOT operated by Access-Policy-Engine (APE). The client is responsible
// to convert these types to policy-engine entities.
type Chain struct {
// Raw is the encoded chain kind.
// It assumes that Raw's bytes are the result of encoding provided by
// policy-engine package.
Raw []byte
}
// ToV2 converts Chain to v2.
func (c *Chain) ToV2() *apeV2.Chain {
v2ct := new(apeV2.Chain)
if c.Raw != nil {
v2Raw := new(apeV2.ChainRaw)
v2Raw.SetRaw(c.Raw)
v2ct.SetKind(v2Raw)
}
return v2ct
}
// ReadFromV2 fills Chain from v2.
func (c *Chain) ReadFromV2(v2ct *apeV2.Chain) error {
switch v := v2ct.GetKind().(type) {
default:
return fmt.Errorf("unsupported chain kind: %T", v)
case *apeV2.ChainRaw:
raw := v.GetRaw()
c.Raw = raw
}
return nil
}

View file

@ -1,53 +0,0 @@
package ape
import (
apeV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/ape"
)
// TargetType is an SDK representation for v2's TargetType.
type TargetType apeV2.TargetType
const (
TargetTypeUndefined TargetType = iota
TargetTypeNamespace
TargetTypeContainer
TargetTypeUser
TargetTypeGroup
)
// ToV2 converts TargetType to v2.
func (targetType TargetType) ToV2() apeV2.TargetType {
return apeV2.TargetType(targetType)
}
// FromV2 reads TargetType to v2.
func (targetType *TargetType) FromV2(v2targetType apeV2.TargetType) {
*targetType = TargetType(v2targetType)
}
// ChainTarget is an SDK representation for v2's ChainTarget.
//
// Note that ChainTarget (as well as v2's ChainTarget) and all related entities
// are NOT operated by Access-Policy-Engine (APE). The client is responsible
// to convert these types to policy-engine entities.
type ChainTarget struct {
TargetType TargetType
Name string
}
// ToV2 converts ChainTarget to v2.
func (ct *ChainTarget) ToV2() *apeV2.ChainTarget {
v2ct := new(apeV2.ChainTarget)
v2ct.SetTargetType(ct.TargetType.ToV2())
v2ct.SetName(ct.Name)
return v2ct
}
// FromV2 reads ChainTarget frpm v2.
func (ct *ChainTarget) FromV2(v2ct *apeV2.ChainTarget) {
ct.TargetType.FromV2(v2ct.GetTargetType())
ct.Name = v2ct.GetName()
}

View file

@ -1,65 +0,0 @@
package ape_test
import (
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
"github.com/stretchr/testify/require"
apeV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/ape"
)
var (
m = map[ape.TargetType]apeV2.TargetType{
ape.TargetTypeUndefined: apeV2.TargetTypeUndefined,
ape.TargetTypeNamespace: apeV2.TargetTypeNamespace,
ape.TargetTypeContainer: apeV2.TargetTypeContainer,
ape.TargetTypeUser: apeV2.TargetTypeUser,
ape.TargetTypeGroup: apeV2.TargetTypeGroup,
}
)
func TestTargetType(t *testing.T) {
for typesdk, typev2 := range m {
t.Run("from sdk to v2 "+typev2.String(), func(t *testing.T) {
v2 := typesdk.ToV2()
require.Equal(t, v2, typev2)
})
t.Run("from v2 to sdk "+typev2.String(), func(t *testing.T) {
var typ ape.TargetType
typ.FromV2(typev2)
require.Equal(t, typesdk, typ)
})
}
}
func TestChainTarget(t *testing.T) {
var (
typ = ape.TargetTypeNamespace
name = "namespaceXXYYZZ"
)
t.Run("from sdk to v2", func(t *testing.T) {
ct := ape.ChainTarget{
TargetType: typ,
Name: name,
}
v2 := ct.ToV2()
require.Equal(t, m[typ], v2.GetTargetType())
require.Equal(t, name, v2.GetName())
})
t.Run("from v2 to sdk", func(t *testing.T) {
v2 := &apeV2.ChainTarget{}
v2.SetTargetType(m[typ])
v2.SetName(name)
var ct ape.ChainTarget
ct.FromV2(v2)
require.Equal(t, typ, ct.TargetType)
require.Equal(t, name, ct.Name)
})
}

View file

@ -1,43 +0,0 @@
package ape_test
import (
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
"github.com/stretchr/testify/require"
apeV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/ape"
)
const (
encoded = `{"ID":"","Rules":[{"Status":"Allow","Actions":{"Inverted":false,"Names":["GetObject"]},"Resources":{"Inverted":false,"Names":["native:object/*"]},"Any":false,"Condition":[{"Op":"StringEquals","Object":"Resource","Key":"Department","Value":"HR"}]}],"MatchType":"DenyPriority"}`
)
func TestChainData(t *testing.T) {
t.Run("raw chain", func(t *testing.T) {
var c ape.Chain
b := []byte(encoded)
c.Raw = b
v2, ok := c.ToV2().GetKind().(*apeV2.ChainRaw)
require.True(t, ok)
require.Equal(t, b, v2.Raw)
})
}
func TestChainMessageV2(t *testing.T) {
b := []byte(encoded)
v2Raw := &apeV2.ChainRaw{}
v2Raw.SetRaw(b)
v2 := &apeV2.Chain{}
v2.SetKind(v2Raw)
var c ape.Chain
c.ReadFromV2(v2)
require.NotNil(t, c.Raw)
require.Equal(t, b, c.Raw)
}

View file

@ -6,9 +6,7 @@ import (
"fmt" "fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
apeV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/ape"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto" frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
@ -35,85 +33,9 @@ type Token struct {
sigSet bool sigSet bool
sig refs.Signature sig refs.Signature
apeOverrideSet bool
apeOverride APEOverride
impersonate bool impersonate bool
} }
// APEOverride is the list of APE chains defined for a target.
// These chains are meant to serve as overrides to the already defined (or even undefined)
// APE chains for the target (see contract `Policy`).
//
// The server-side processing of the bearer token with set APE overrides must verify if a client is permitted
// to override chains for the target, preventing unauthorized access through the APE mechanism.
type APEOverride struct {
// Target for which chains are applied.
Target apeSDK.ChainTarget
// The list of APE chains.
Chains []apeSDK.Chain
}
// Marshal marshals APEOverride into a protobuf binary form.
func (c *APEOverride) Marshal() ([]byte, error) {
return c.ToV2().StableMarshal(nil), nil
}
// Unmarshal unmarshals protobuf binary representation of APEOverride.
func (c *APEOverride) Unmarshal(data []byte) error {
overrideV2 := new(acl.APEOverride)
if err := overrideV2.Unmarshal(data); err != nil {
return err
}
return c.FromV2(overrideV2)
}
// MarshalJSON encodes APEOverride to protobuf JSON format.
func (c *APEOverride) MarshalJSON() ([]byte, error) {
return c.ToV2().MarshalJSON()
}
// UnmarshalJSON decodes APEOverride from protobuf JSON format.
func (c *APEOverride) UnmarshalJSON(data []byte) error {
overrideV2 := new(acl.APEOverride)
if err := overrideV2.UnmarshalJSON(data); err != nil {
return err
}
return c.FromV2(overrideV2)
}
func (c *APEOverride) FromV2(tokenAPEChains *acl.APEOverride) error {
c.Target.FromV2(tokenAPEChains.GetTarget())
if chains := tokenAPEChains.GetChains(); len(chains) > 0 {
c.Chains = make([]apeSDK.Chain, len(chains))
for i := range chains {
if err := c.Chains[i].ReadFromV2(chains[i]); err != nil {
return fmt.Errorf("invalid APE chain: %w", err)
}
}
}
return nil
}
func (c *APEOverride) ToV2() *acl.APEOverride {
if c == nil {
return nil
}
apeOverride := new(acl.APEOverride)
apeOverride.SetTarget(c.Target.ToV2())
chains := make([]*apeV2.Chain, len(c.Chains))
for i := range c.Chains {
chains[i] = c.Chains[i].ToV2()
}
apeOverride.SetChains(chains)
return apeOverride
}
// reads Token from the acl.BearerToken message. If checkFieldPresence is set, // reads Token from the acl.BearerToken message. If checkFieldPresence is set,
// returns an error on absence of any protocol-required field. // returns an error on absence of any protocol-required field.
func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error { func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {
@ -126,11 +48,10 @@ func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {
b.impersonate = body.GetImpersonate() b.impersonate = body.GetImpersonate()
apeOverrides := body.GetAPEOverride()
eaclTable := body.GetEACL() eaclTable := body.GetEACL()
if b.eaclTableSet = eaclTable != nil; b.eaclTableSet { if b.eaclTableSet = eaclTable != nil; b.eaclTableSet {
b.eaclTable = *eacl.NewTableFromV2(eaclTable) b.eaclTable = *eacl.NewTableFromV2(eaclTable)
} else if checkFieldPresence && !b.impersonate && apeOverrides == nil { } else if checkFieldPresence && !b.impersonate {
return errors.New("missing eACL table") return errors.New("missing eACL table")
} }
@ -151,14 +72,6 @@ func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {
return errors.New("missing token lifetime") return errors.New("missing token lifetime")
} }
if b.apeOverrideSet = apeOverrides != nil; b.apeOverrideSet {
if err = b.apeOverride.FromV2(apeOverrides); err != nil {
return err
}
} else if checkFieldPresence && !b.impersonate && !b.eaclTableSet {
return errors.New("missing APE override")
}
sig := m.GetSignature() sig := m.GetSignature()
if b.sigSet = sig != nil; sig != nil { if b.sigSet = sig != nil; sig != nil {
b.sig = *sig b.sig = *sig
@ -177,7 +90,7 @@ func (b *Token) ReadFromV2(m acl.BearerToken) error {
} }
func (b Token) fillBody() *acl.BearerTokenBody { func (b Token) fillBody() *acl.BearerTokenBody {
if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet && !b.impersonate && !b.apeOverrideSet { if !b.eaclTableSet && !b.targetUserSet && !b.lifetimeSet && !b.impersonate {
return nil return nil
} }
@ -203,10 +116,6 @@ func (b Token) fillBody() *acl.BearerTokenBody {
body.SetLifetime(&lifetime) body.SetLifetime(&lifetime)
} }
if b.apeOverrideSet {
body.SetAPEOverride(b.apeOverride.ToV2())
}
body.SetImpersonate(b.impersonate) body.SetImpersonate(b.impersonate)
return &body return &body
@ -305,25 +214,6 @@ func (b Token) EACLTable() eacl.Table {
return eacl.Table{} return eacl.Table{}
} }
// SetAPEOverride sets APE override to the bearer token.
//
// See also: APEOverride.
func (b *Token) SetAPEOverride(v APEOverride) {
b.apeOverride = v
b.apeOverrideSet = true
}
// APEOverride returns APE override set by SetAPEOverride.
//
// Zero Token has zero APEOverride.
func (b *Token) APEOverride() APEOverride {
if b.apeOverrideSet {
return b.apeOverride
}
return APEOverride{}
}
// SetImpersonate mark token as impersonate to consider token signer as request owner. // SetImpersonate mark token as impersonate to consider token signer as request owner.
// If this field is true extended EACLTable in token body isn't processed. // If this field is true extended EACLTable in token body isn't processed.
func (b *Token) SetImpersonate(v bool) { func (b *Token) SetImpersonate(v bool) {

View file

@ -3,7 +3,6 @@ package bearer_test
import ( import (
"bytes" "bytes"
"math/rand" "math/rand"
"reflect"
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
@ -82,58 +81,6 @@ func TestToken_SetEACLTable(t *testing.T) {
require.True(t, isEqualEACLTables(eaclTable, val.EACLTable())) require.True(t, isEqualEACLTables(eaclTable, val.EACLTable()))
} }
func TestToken_SetAPEOverrides(t *testing.T) {
var val bearer.Token
var m acl.BearerToken
filled := bearertest.Token()
val.WriteToV2(&m)
require.Zero(t, m.GetBody())
val2 := filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
require.Zero(t, val2.APEOverride())
val2 = filled
jd, err := val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
require.Zero(t, val2.APEOverride())
// set value
tApe := bearertest.APEOverride()
val.SetAPEOverride(tApe)
require.Equal(t, tApe, val.APEOverride())
val.WriteToV2(&m)
require.NotNil(t, m.GetBody().GetAPEOverride())
require.True(t, tokenAPEOverridesEqual(tApe.ToV2(), m.GetBody().GetAPEOverride()))
val2 = filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
apeOverride := val2.APEOverride()
require.True(t, tokenAPEOverridesEqual(tApe.ToV2(), apeOverride.ToV2()))
val2 = filled
jd, err = val.MarshalJSON()
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
apeOverride = val.APEOverride()
require.True(t, tokenAPEOverridesEqual(tApe.ToV2(), apeOverride.ToV2()))
}
func tokenAPEOverridesEqual(lhs, rhs *acl.APEOverride) bool {
return reflect.DeepEqual(lhs, rhs)
}
func TestToken_ForUser(t *testing.T) { func TestToken_ForUser(t *testing.T) {
var val bearer.Token var val bearer.Token
var m acl.BearerToken var m acl.BearerToken
@ -160,7 +107,7 @@ func TestToken_ForUser(t *testing.T) {
require.Zero(t, m.GetBody()) require.Zero(t, m.GetBody())
// set value // set value
usr := usertest.ID() usr := *usertest.ID()
var usrV2 refs.OwnerID var usrV2 refs.OwnerID
usr.WriteToV2(&usrV2) usr.WriteToV2(&usrV2)
@ -296,11 +243,11 @@ func TestToken_AssertContainer(t *testing.T) {
func TestToken_AssertUser(t *testing.T) { func TestToken_AssertUser(t *testing.T) {
var val bearer.Token var val bearer.Token
usr := usertest.ID() usr := *usertest.ID()
require.True(t, val.AssertUser(usr)) require.True(t, val.AssertUser(usr))
val.ForUser(usertest.ID()) val.ForUser(*usertest.ID())
require.False(t, val.AssertUser(usr)) require.False(t, val.AssertUser(usr))
val.ForUser(usr) val.ForUser(usr)
@ -385,7 +332,7 @@ func TestToken_ReadFromV2(t *testing.T) {
val.WriteToV2(&m2) val.WriteToV2(&m2)
require.Equal(t, m, m2) require.Equal(t, m, m2)
usr, usr2 := usertest.ID(), usertest.ID() usr, usr2 := *usertest.ID(), *usertest.ID()
require.True(t, val.AssertUser(usr)) require.True(t, val.AssertUser(usr))
require.True(t, val.AssertUser(usr2)) require.True(t, val.AssertUser(usr2))

View file

@ -1,7 +1,6 @@
package bearertest package bearertest
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test" eacltest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl/test"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test" usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
@ -14,19 +13,8 @@ func Token() (t bearer.Token) {
t.SetExp(3) t.SetExp(3)
t.SetNbf(2) t.SetNbf(2)
t.SetIat(1) t.SetIat(1)
t.ForUser(usertest.ID()) t.ForUser(*usertest.ID())
t.SetEACLTable(*eacltest.Table()) t.SetEACLTable(*eacltest.Table())
t.SetAPEOverride(APEOverride())
return t return t
} }
func APEOverride() bearer.APEOverride {
return bearer.APEOverride{
Target: ape.ChainTarget{
TargetType: ape.TargetTypeContainer,
Name: "F8JsMnChywiPvbDvpxMbjTjx5KhWHHp6gCDt8BhzL9kF",
},
Chains: []ape.Chain{{Raw: []byte("{}")}},
}
}

View file

@ -24,17 +24,17 @@ type Checksum refs.Checksum
// Type represents the enumeration // Type represents the enumeration
// of checksum types. // of checksum types.
type Type refs.ChecksumType type Type uint8
const ( const (
// Unknown is an undefined checksum type. // Unknown is an undefined checksum type.
Unknown Type = Type(refs.UnknownChecksum) Unknown Type = iota
// SHA256 is a SHA256 checksum type. // SHA256 is a SHA256 checksum type.
SHA256 = Type(refs.SHA256) SHA256
// TZ is a Tillich-Zémor checksum type. // TZ is a Tillich-Zémor checksum type.
TZ = Type(refs.TillichZemor) TZ
) )
// ReadFromV2 reads Checksum from the refs.Checksum message. Checks if the // ReadFromV2 reads Checksum from the refs.Checksum message. Checks if the

View file

@ -2,9 +2,9 @@ package checksum
import ( import (
"bytes" "bytes"
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"math/rand"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
) )

View file

@ -1,8 +1,8 @@
package checksumtest package checksumtest
import ( import (
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"math/rand"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
) )
@ -11,7 +11,7 @@ import (
func Checksum() checksum.Checksum { func Checksum() checksum.Checksum {
var cs [sha256.Size]byte var cs [sha256.Size]byte
_, _ = rand.Read(cs[:]) rand.Read(cs[:])
var x checksum.Checksum var x checksum.Checksum

View file

@ -17,26 +17,26 @@ import (
// PrmBalanceGet groups parameters of BalanceGet operation. // PrmBalanceGet groups parameters of BalanceGet operation.
type PrmBalanceGet struct { type PrmBalanceGet struct {
XHeaders []string prmCommonMeta
Account user.ID accountSet bool
account user.ID
} }
// SetAccount sets identifier of the FrostFS account for which the balance is requested. // SetAccount sets identifier of the FrostFS account for which the balance is requested.
// Required parameter. // Required parameter.
//
// Deprecated: Use PrmBalanceGet.Account instead.
func (x *PrmBalanceGet) SetAccount(id user.ID) { func (x *PrmBalanceGet) SetAccount(id user.ID) {
x.Account = id x.account = id
x.accountSet = true
} }
func (x *PrmBalanceGet) buildRequest(c *Client) (*v2accounting.BalanceRequest, error) { func (x *PrmBalanceGet) buildRequest(c *Client) (*v2accounting.BalanceRequest, error) {
if x.Account.IsEmpty() { if !x.accountSet {
return nil, errorAccountNotSet return nil, errorAccountNotSet
} }
var accountV2 refs.OwnerID var accountV2 refs.OwnerID
x.Account.WriteToV2(&accountV2) x.account.WriteToV2(&accountV2)
var body v2accounting.BalanceRequestBody var body v2accounting.BalanceRequestBody
body.SetOwnerID(&accountV2) body.SetOwnerID(&accountV2)
@ -64,9 +64,9 @@ func (x ResBalanceGet) Amount() accounting.Decimal {
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`, // Any client's internal or transport errors are returned as `error`,
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmBalanceGet docs). // Returns an error if parameters are set incorrectly (see PrmBalanceGet docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -79,7 +79,7 @@ func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalance
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }

View file

@ -1,79 +0,0 @@
package client
import (
"context"
"fmt"
apemanagerV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
)
// PrmAPEManagerAddChain groups parameters of APEManagerAddChain operation.
type PrmAPEManagerAddChain struct {
XHeaders []string
ChainTarget apeSDK.ChainTarget
Chain apeSDK.Chain
}
func (prm *PrmAPEManagerAddChain) buildRequest(c *Client) (*apemanagerV2.AddChainRequest, error) {
if len(prm.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
req := new(apemanagerV2.AddChainRequest)
reqBody := new(apemanagerV2.AddChainRequestBody)
reqBody.SetTarget(prm.ChainTarget.ToV2())
reqBody.SetChain(prm.Chain.ToV2())
req.SetBody(reqBody)
var meta sessionV2.RequestMetaHeader
writeXHeadersToMeta(prm.XHeaders, &meta)
c.prepareRequest(req, &meta)
return req, nil
}
type ResAPEManagerAddChain struct {
statusRes
// ChainID of set Chain. If Chain does not contain chainID before request, then
// ChainID is generated.
ChainID apeSDK.ChainID
}
// APEManagerAddChain sets Chain for ChainTarget.
func (c *Client) APEManagerAddChain(ctx context.Context, prm PrmAPEManagerAddChain) (*ResAPEManagerAddChain, error) {
req, err := prm.buildRequest(c)
if err != nil {
return nil, err
}
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
resp, err := rpcapi.AddChain(&c.c, req, client.WithContext(ctx))
if err != nil {
return nil, err
}
var res ResAPEManagerAddChain
res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) {
return &res, err
}
res.ChainID = resp.GetBody().GetChainID()
return &res, nil
}

View file

@ -1,80 +0,0 @@
package client
import (
"context"
"fmt"
apemanagerV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
)
// PrmAPEManagerListChains groups parameters of APEManagerListChains operation.
type PrmAPEManagerListChains struct {
XHeaders []string
ChainTarget apeSDK.ChainTarget
}
func (prm *PrmAPEManagerListChains) buildRequest(c *Client) (*apemanagerV2.ListChainsRequest, error) {
if len(prm.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
req := new(apemanagerV2.ListChainsRequest)
reqBody := new(apemanagerV2.ListChainsRequestBody)
reqBody.SetTarget(prm.ChainTarget.ToV2())
req.SetBody(reqBody)
var meta sessionV2.RequestMetaHeader
writeXHeadersToMeta(prm.XHeaders, &meta)
c.prepareRequest(req, &meta)
return req, nil
}
type ResAPEManagerListChains struct {
statusRes
Chains []apeSDK.Chain
}
// APEManagerListChains lists Chains for ChainTarget.
func (c *Client) APEManagerListChains(ctx context.Context, prm PrmAPEManagerListChains) (*ResAPEManagerListChains, error) {
req, err := prm.buildRequest(c)
if err != nil {
return nil, err
}
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
resp, err := rpcapi.ListChains(&c.c, req, client.WithContext(ctx))
if err != nil {
return nil, err
}
var res ResAPEManagerListChains
res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) {
return &res, err
}
for _, ch := range resp.GetBody().GetChains() {
var chSDK apeSDK.Chain
if err := chSDK.ReadFromV2(ch); err != nil {
return nil, err
}
res.Chains = append(res.Chains, chSDK)
}
return &res, nil
}

View file

@ -1,73 +0,0 @@
package client
import (
"context"
"fmt"
apemanagerV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
)
// PrmAPEManagerRemoveChain groups parameters of APEManagerRemoveChain operation.
type PrmAPEManagerRemoveChain struct {
XHeaders []string
ChainTarget apeSDK.ChainTarget
ChainID apeSDK.ChainID
}
func (prm *PrmAPEManagerRemoveChain) buildRequest(c *Client) (*apemanagerV2.RemoveChainRequest, error) {
if len(prm.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
req := new(apemanagerV2.RemoveChainRequest)
reqBody := new(apemanagerV2.RemoveChainRequestBody)
reqBody.SetTarget(prm.ChainTarget.ToV2())
reqBody.SetChainID(prm.ChainID)
req.SetBody(reqBody)
var meta sessionV2.RequestMetaHeader
writeXHeadersToMeta(prm.XHeaders, &meta)
c.prepareRequest(req, &meta)
return req, nil
}
type ResAPEManagerRemoveChain struct {
statusRes
}
// APEManagerRemoveChain removes Chain with ChainID defined for ChainTarget.
func (c *Client) APEManagerRemoveChain(ctx context.Context, prm PrmAPEManagerRemoveChain) (*ResAPEManagerRemoveChain, error) {
req, err := prm.buildRequest(c)
if err != nil {
return nil, err
}
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
resp, err := rpcapi.RemoveChain(&c.c, req, client.WithContext(ctx))
if err != nil {
return nil, err
}
var res ResAPEManagerRemoveChain
res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) {
return &res, err
}
return &res, nil
}

View file

@ -11,8 +11,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
// Client represents virtual connection to the FrostFS network to communicate // Client represents virtual connection to the FrostFS network to communicate
@ -80,41 +78,42 @@ func (c *Client) Init(prm PrmInit) {
// //
// See also Init / Close. // See also Init / Close.
func (c *Client) Dial(ctx context.Context, prm PrmDial) error { func (c *Client) Dial(ctx context.Context, prm PrmDial) error {
if prm.Endpoint == "" { if prm.endpoint == "" {
return errorServerAddrUnset return errorServerAddrUnset
} }
if prm.DialTimeout <= 0 { if prm.timeoutDialSet {
prm.DialTimeout = defaultDialTimeout if prm.timeoutDial <= 0 {
return errorNonPositiveTimeout
}
} else {
prm.timeoutDial = 5 * time.Second
} }
if prm.StreamTimeout <= 0 {
prm.StreamTimeout = defaultStreamTimeout if prm.streamTimeoutSet {
if prm.streamTimeout <= 0 {
return errorNonPositiveTimeout
}
} else {
prm.streamTimeout = 10 * time.Second
} }
c.c = *client.New(append( c.c = *client.New(append(
client.WithNetworkURIAddress(prm.Endpoint, prm.TLSConfig), client.WithNetworkURIAddress(prm.endpoint, prm.tlsConfig),
client.WithDialTimeout(prm.DialTimeout), client.WithDialTimeout(prm.timeoutDial),
client.WithRWTimeout(prm.StreamTimeout), client.WithRWTimeout(prm.streamTimeout),
client.WithGRPCDialOptions(prm.GRPCDialOptions), client.WithGRPCDialOptions(prm.dialOptions),
)...) )...)
c.setFrostFSAPIServer((*coreServer)(&c.c)) c.setFrostFSAPIServer((*coreServer)(&c.c))
// TODO: (neofs-api-go#382) perform generic dial stage of the client.Client
_, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest), _, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest),
client.WithContext(ctx), client.WithContext(ctx),
) )
if err != nil { // return context errors since they signal about dial problem
// return context errors since they signal about dial problem if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return err
return err
}
st, ok := status.FromError(err)
if ok && st.Code() == codes.Canceled {
return context.Canceled
}
if ok && st.Code() == codes.DeadlineExceeded {
return context.DeadlineExceeded
}
} }
return nil return nil
@ -145,67 +144,52 @@ func (c *Client) Close() error {
// //
// See also Init. // See also Init.
type PrmInit struct { type PrmInit struct {
DisableFrostFSErrorResolution bool resolveFrostFSErrors bool
Key ecdsa.PrivateKey key ecdsa.PrivateKey
ResponseInfoCallback func(ResponseMetaInfo) error cbRespInfo func(ResponseMetaInfo) error
NetMagic uint64 netMagic uint64
} }
// SetDefaultPrivateKey sets Client private key to be used for the protocol // SetDefaultPrivateKey sets Client private key to be used for the protocol
// communication by default. // communication by default.
// //
// Required for operations without custom key parametrization (see corresponding Prm* docs). // Required for operations without custom key parametrization (see corresponding Prm* docs).
//
// Deprecated: Use PrmInit.Key instead.
func (x *PrmInit) SetDefaultPrivateKey(key ecdsa.PrivateKey) { func (x *PrmInit) SetDefaultPrivateKey(key ecdsa.PrivateKey) {
x.Key = key x.key = key
} }
// Deprecated: method is no-op. Option is default. // ResolveFrostFSFailures makes the Client to resolve failure statuses of the
// FrostFS protocol into Go built-in errors. These errors are returned from
// each protocol operation. By default, statuses aren't resolved and written
// to the resulting structure (see corresponding Res* docs).
func (x *PrmInit) ResolveFrostFSFailures() { func (x *PrmInit) ResolveFrostFSFailures() {
} x.resolveFrostFSErrors = true
// DisableFrostFSFailuresResolution makes the Client to preserve failure statuses of the
// FrostFS protocol only in resulting structure (see corresponding Res* docs).
// These errors are returned from each protocol operation. By default, statuses
// are resolved and returned as a Go built-in errors.
//
// Deprecated: Use PrmInit.DisableFrostFSErrorResolution instead.
func (x *PrmInit) DisableFrostFSFailuresResolution() {
x.DisableFrostFSErrorResolution = true
} }
// SetResponseInfoCallback makes the Client to pass ResponseMetaInfo from each // SetResponseInfoCallback makes the Client to pass ResponseMetaInfo from each
// FrostFS server response to f. Nil (default) means ignore response meta info. // FrostFS server response to f. Nil (default) means ignore response meta info.
//
// Deprecated: Use PrmInit.ResponseInfoCallback instead.
func (x *PrmInit) SetResponseInfoCallback(f func(ResponseMetaInfo) error) { func (x *PrmInit) SetResponseInfoCallback(f func(ResponseMetaInfo) error) {
x.ResponseInfoCallback = f x.cbRespInfo = f
} }
const (
defaultDialTimeout = 5 * time.Second
defaultStreamTimeout = 10 * time.Second
)
// PrmDial groups connection parameters for the Client. // PrmDial groups connection parameters for the Client.
// //
// See also Dial. // See also Dial.
type PrmDial struct { type PrmDial struct {
Endpoint string endpoint string
TLSConfig *tls.Config tlsConfig *tls.Config
// If DialTimeout is non-positive, then it's set to defaultDialTimeout. timeoutDialSet bool
DialTimeout time.Duration timeoutDial time.Duration
// If StreamTimeout is non-positive, then it's set to defaultStreamTimeout. streamTimeoutSet bool
StreamTimeout time.Duration streamTimeout time.Duration
GRPCDialOptions []grpc.DialOption dialOptions []grpc.DialOption
} }
// SetServerURI sets server URI in the FrostFS network. // SetServerURI sets server URI in the FrostFS network.
@ -221,41 +205,33 @@ type PrmDial struct {
// grpcs // grpcs
// //
// See also SetTLSConfig. // See also SetTLSConfig.
//
// Deprecated: Use PrmDial.Endpoint instead.
func (x *PrmDial) SetServerURI(endpoint string) { func (x *PrmDial) SetServerURI(endpoint string) {
x.Endpoint = endpoint x.endpoint = endpoint
} }
// SetTLSConfig sets tls.Config to open TLS client connection // SetTLSConfig sets tls.Config to open TLS client connection
// to the FrostFS server. Nil (default) means insecure connection. // to the FrostFS server. Nil (default) means insecure connection.
// //
// See also SetServerURI. // See also SetServerURI.
//
// Depreacted: Use PrmDial.TLSConfig instead.
func (x *PrmDial) SetTLSConfig(tlsConfig *tls.Config) { func (x *PrmDial) SetTLSConfig(tlsConfig *tls.Config) {
x.TLSConfig = tlsConfig x.tlsConfig = tlsConfig
} }
// SetTimeout sets the timeout for connection to be established. // SetTimeout sets the timeout for connection to be established.
// MUST BE positive. If not called, 5s timeout will be used by default. // MUST BE positive. If not called, 5s timeout will be used by default.
//
// Deprecated: Use PrmDial.DialTimeout instead.
func (x *PrmDial) SetTimeout(timeout time.Duration) { func (x *PrmDial) SetTimeout(timeout time.Duration) {
x.DialTimeout = timeout x.timeoutDialSet = true
x.timeoutDial = timeout
} }
// SetStreamTimeout sets the timeout for individual operations in streaming RPC. // SetStreamTimeout sets the timeout for individual operations in streaming RPC.
// MUST BE positive. If not called, 10s timeout will be used by default. // MUST BE positive. If not called, 10s timeout will be used by default.
//
// Deprecated: Use PrmDial.StreamTimeout instead.
func (x *PrmDial) SetStreamTimeout(timeout time.Duration) { func (x *PrmDial) SetStreamTimeout(timeout time.Duration) {
x.StreamTimeout = timeout x.streamTimeoutSet = true
x.streamTimeout = timeout
} }
// SetGRPCDialOptions sets the gRPC dial options for new gRPC client connection. // SetGRPCDialOptions sets the gRPC dial options for new gRPC client connection.
//
// Deprecated: Use PrmDial.GRPCDialOptions instead.
func (x *PrmDial) SetGRPCDialOptions(opts ...grpc.DialOption) { func (x *PrmDial) SetGRPCDialOptions(opts ...grpc.DialOption) {
x.GRPCDialOptions = opts x.dialOptions = opts
} }

View file

@ -29,9 +29,8 @@ func assertStatusErr(tb testing.TB, res interface{ Status() apistatus.Status })
} }
func newClient(server frostFSAPIServer) *Client { func newClient(server frostFSAPIServer) *Client {
prm := PrmInit{ var prm PrmInit
Key: *key, prm.SetDefaultPrivateKey(*key)
}
var c Client var c Client
c.Init(prm) c.Init(prm)
@ -44,9 +43,8 @@ func TestClient_DialContext(t *testing.T) {
var c Client var c Client
// try to connect to any host // try to connect to any host
prm := PrmDial{ var prm PrmDial
Endpoint: "localhost:8080", prm.SetServerURI("localhost:8080")
}
assert := func(ctx context.Context, errExpected error) { assert := func(ctx context.Context, errExpected error) {
// expect particular context error according to Dial docs // expect particular context error according to Dial docs

View file

@ -24,6 +24,24 @@ func (x statusRes) Status() apistatus.Status {
return x.st return x.st
} }
// groups meta parameters shared between all Client operations.
type prmCommonMeta struct {
// FrostFS request X-Headers
xHeaders []string
}
// WithXHeaders specifies list of extended headers (string key-value pairs)
// to be attached to the request. Must have an even length.
//
// Slice must not be mutated until the operation completes.
func (x *prmCommonMeta) WithXHeaders(hs ...string) {
if len(hs)%2 != 0 {
panic("slice of X-Headers with odd length")
}
x.xHeaders = hs
}
func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) { func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) {
if len(xHeaders) == 0 { if len(xHeaders) == 0 {
return return
@ -46,13 +64,16 @@ func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) {
// error messages. // error messages.
var ( var (
errorMissingContainer = errors.New("missing container") errorMissingContainer = errors.New("missing container")
errorMissingObject = errors.New("missing object") errorMissingObject = errors.New("missing object")
errorAccountNotSet = errors.New("account not set") errorAccountNotSet = errors.New("account not set")
errorServerAddrUnset = errors.New("server address is unset or empty") errorServerAddrUnset = errors.New("server address is unset or empty")
errorZeroRangeLength = errors.New("zero range length") errorNonPositiveTimeout = errors.New("non-positive timeout")
errorMissingRanges = errors.New("missing ranges") errorEACLTableNotSet = errors.New("eACL table not set")
errorInvalidXHeaders = errors.New("xheaders must be presented only as key-value pairs") errorMissingAnnouncements = errors.New("missing announcements")
errorZeroRangeLength = errors.New("zero range length")
errorMissingRanges = errors.New("missing ranges")
errorInvalidXHeaders = errors.New("xheaders must be presented only as key-value pairs")
) )
type request interface { type request interface {
@ -75,19 +96,19 @@ func (c *Client) prepareRequest(req request, meta *v2session.RequestMetaHeader)
meta.SetTTL(ttl) meta.SetTTL(ttl)
meta.SetVersion(verV2) meta.SetVersion(verV2)
meta.SetNetworkMagic(c.prm.NetMagic) meta.SetNetworkMagic(c.prm.netMagic)
req.SetMetaHeader(meta) req.SetMetaHeader(meta)
} }
// processResponse verifies response signature and converts status to an error if needed. // processResponse verifies response signature and converts status to an error if needed.
func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) { func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) {
if c.prm.ResponseInfoCallback != nil { if c.prm.cbRespInfo != nil {
rmi := ResponseMetaInfo{ rmi := ResponseMetaInfo{
key: resp.GetVerificationHeader().GetBodySignature().GetKey(), key: resp.GetVerificationHeader().GetBodySignature().GetKey(),
epoch: resp.GetMetaHeader().GetEpoch(), epoch: resp.GetMetaHeader().GetEpoch(),
} }
if err := c.prm.ResponseInfoCallback(rmi); err != nil { if err := c.prm.cbRespInfo(rmi); err != nil {
return nil, fmt.Errorf("response callback error: %w", err) return nil, fmt.Errorf("response callback error: %w", err)
} }
} }
@ -98,7 +119,7 @@ func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) {
} }
st := apistatus.FromStatusV2(resp.GetMetaHeader().GetStatus()) st := apistatus.FromStatusV2(resp.GetMetaHeader().GetStatus())
if !c.prm.DisableFrostFSErrorResolution { if c.prm.resolveFrostFSErrors {
return st, apistatus.ErrFromStatus(st) return st, apistatus.ErrFromStatus(st)
} }
return st, nil return st, nil

View file

@ -52,7 +52,7 @@ func (prm *PrmContainerDelete) buildRequest(c *Client) (*v2container.DeleteReque
var sig frostfscrypto.Signature var sig frostfscrypto.Signature
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.Key), data) err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), data)
if err != nil { if err != nil {
return nil, fmt.Errorf("calculate signature: %w", err) return nil, fmt.Errorf("calculate signature: %w", err)
} }
@ -101,9 +101,9 @@ type ResContainerDelete struct {
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Operation is asynchronous and no guaranteed even in the absence of errors. // Operation is asynchronous and no guaranteed even in the absence of errors.
// The required time is also not predictable. // The required time is also not predictable.
@ -124,7 +124,7 @@ func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) (*
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }

110
client/container_eacl.go Normal file
View file

@ -0,0 +1,110 @@
package client
import (
"context"
"fmt"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
)
// PrmContainerEACL groups parameters of ContainerEACL operation.
type PrmContainerEACL struct {
// FrostFS request X-Headers.
XHeaders []string
ContainerID *cid.ID
}
// SetContainer sets identifier of the FrostFS container to read the eACL table.
// Required parameter.
//
// Deprecated: Use PrmContainerEACL.ContainerID instead.
func (x *PrmContainerEACL) SetContainer(id cid.ID) {
x.ContainerID = &id
}
func (x *PrmContainerEACL) buildRequest(c *Client) (*v2container.GetExtendedACLRequest, error) {
if x.ContainerID == nil {
return nil, errorMissingContainer
}
if len(x.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
var cidV2 refs.ContainerID
x.ContainerID.WriteToV2(&cidV2)
reqBody := new(v2container.GetExtendedACLRequestBody)
reqBody.SetContainerID(&cidV2)
var req v2container.GetExtendedACLRequest
req.SetBody(reqBody)
c.prepareRequest(&req, new(v2session.RequestMetaHeader))
return &req, nil
}
// ResContainerEACL groups resulting values of ContainerEACL operation.
type ResContainerEACL struct {
statusRes
table eacl.Table
}
// Table returns eACL table of the requested container.
func (x ResContainerEACL) Table() eacl.Table {
return x.table
}
// ContainerEACL reads eACL table of the FrostFS container.
//
// Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`.
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are returned as `error`, otherwise, are included
// in the returned result structure.
//
// Returns an error if parameters are set incorrectly (see PrmContainerEACL docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return statuses:
// - global (see Client docs);
// - *apistatus.ContainerNotFound;
// - *apistatus.EACLNotFound.
func (c *Client) ContainerEACL(ctx context.Context, prm PrmContainerEACL) (*ResContainerEACL, error) {
req, err := prm.buildRequest(c)
if err != nil {
return nil, err
}
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
resp, err := rpcapi.GetEACL(&c.c, req, client.WithContext(ctx))
if err != nil {
return nil, err
}
var res ResContainerEACL
res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) {
return &res, err
}
eACL := resp.GetBody().GetEACL()
if eACL == nil {
return &res, newErrMissingResponseField("eACL")
}
res.table = *eacl.NewTableFromV2(eACL)
return &res, nil
}

View file

@ -14,7 +14,6 @@ import (
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
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/session"
) )
// PrmContainerGet groups parameters of ContainerGet operation. // PrmContainerGet groups parameters of ContainerGet operation.
@ -23,8 +22,6 @@ type PrmContainerGet struct {
XHeaders []string XHeaders []string
ContainerID *cid.ID ContainerID *cid.ID
Session *session.Container
} }
// SetContainer sets identifier of the container to be read. // SetContainer sets identifier of the container to be read.
@ -50,19 +47,9 @@ func (prm *PrmContainerGet) buildRequest(c *Client) (*v2container.GetRequest, er
reqBody := new(v2container.GetRequestBody) reqBody := new(v2container.GetRequestBody)
reqBody.SetContainerID(&cidV2) reqBody.SetContainerID(&cidV2)
var meta v2session.RequestMetaHeader
writeXHeadersToMeta(prm.XHeaders, &meta)
if prm.Session != nil {
var tokv2 v2session.Token
prm.Session.WriteToV2(&tokv2)
meta.SetSessionToken(&tokv2)
}
var req v2container.GetRequest var req v2container.GetRequest
req.SetBody(reqBody) req.SetBody(reqBody)
c.prepareRequest(&req, &meta) c.prepareRequest(&req, new(v2session.RequestMetaHeader))
return &req, nil return &req, nil
} }
@ -84,9 +71,9 @@ func (x ResContainerGet) Container() container.Container {
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmContainerGet docs). // Returns an error if parameters are set incorrectly (see PrmContainerGet docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -100,7 +87,7 @@ func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResCon
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }

View file

@ -12,51 +12,38 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
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/session"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
) )
// PrmContainerList groups parameters of ContainerList operation. // PrmContainerList groups parameters of ContainerList operation.
type PrmContainerList struct { type PrmContainerList struct {
XHeaders []string prmCommonMeta
Account user.ID ownerSet bool
ownerID user.ID
Session *session.Container
} }
// SetAccount sets identifier of the FrostFS account to list the containers. // SetAccount sets identifier of the FrostFS account to list the containers.
// Required parameter. // Required parameter.
//
// Deprecated: Use PrmContainerList.Account instead.
func (x *PrmContainerList) SetAccount(id user.ID) { func (x *PrmContainerList) SetAccount(id user.ID) {
x.Account = id x.ownerID = id
x.ownerSet = true
} }
func (x *PrmContainerList) buildRequest(c *Client) (*v2container.ListRequest, error) { func (x *PrmContainerList) buildRequest(c *Client) (*v2container.ListRequest, error) {
if x.Account.IsEmpty() { if !x.ownerSet {
return nil, errorAccountNotSet return nil, errorAccountNotSet
} }
var ownerV2 refs.OwnerID var ownerV2 refs.OwnerID
x.Account.WriteToV2(&ownerV2) x.ownerID.WriteToV2(&ownerV2)
reqBody := new(v2container.ListRequestBody) reqBody := new(v2container.ListRequestBody)
reqBody.SetOwnerID(&ownerV2) reqBody.SetOwnerID(&ownerV2)
var meta v2session.RequestMetaHeader
writeXHeadersToMeta(x.XHeaders, &meta)
if x.Session != nil {
var tokv2 v2session.Token
x.Session.WriteToV2(&tokv2)
meta.SetSessionToken(&tokv2)
}
var req v2container.ListRequest var req v2container.ListRequest
req.SetBody(reqBody) req.SetBody(reqBody)
c.prepareRequest(&req, &meta) c.prepareRequest(&req, new(v2session.RequestMetaHeader))
return &req, nil return &req, nil
} }
@ -78,9 +65,9 @@ func (x ResContainerList) Containers() []cid.ID {
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmContainerList docs). // Returns an error if parameters are set incorrectly (see PrmContainerList docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -93,7 +80,7 @@ func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResC
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }

View file

@ -64,7 +64,7 @@ func (x *PrmContainerPut) buildRequest(c *Client) (*v2container.PutRequest, erro
var sig frostfscrypto.Signature var sig frostfscrypto.Signature
err := container.CalculateSignature(&sig, *x.Container, c.prm.Key) err := container.CalculateSignature(&sig, *x.Container, c.prm.key)
if err != nil { if err != nil {
return nil, fmt.Errorf("calculate container signature: %w", err) return nil, fmt.Errorf("calculate container signature: %w", err)
} }
@ -110,9 +110,9 @@ func (x ResContainerPut) ID() cid.ID {
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Operation is asynchronous and no guaranteed even in the absence of errors. // Operation is asynchronous and no guaranteed even in the absence of errors.
// The required time is also not predictable. // The required time is also not predictable.
@ -132,7 +132,7 @@ func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResCon
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }

View file

@ -0,0 +1,136 @@
package client
import (
"context"
"fmt"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
)
// PrmContainerSetEACL groups parameters of ContainerSetEACL operation.
type PrmContainerSetEACL struct {
// FrostFS request X-Headers.
XHeaders []string
Table *eacl.Table
Session *session.Container
}
// SetTable sets eACL table structure to be set for the container.
// Required parameter.
//
// Deprecated: Use PrmContainerSetEACL.Table instead.
func (x *PrmContainerSetEACL) SetTable(table eacl.Table) {
x.Table = &table
}
// WithinSession specifies session within which extended ACL of the container
// should be saved.
//
// Creator of the session acquires the authorship of the request. This affects
// the execution of an operation (e.g. access control).
//
// Session is optional, if set the following requirements apply:
// - if particular container is specified (ApplyOnlyTo), it MUST equal the container
// for which extended ACL is going to be set
// - session operation MUST be session.VerbContainerSetEACL (ForVerb)
// - token MUST be signed using private key of the owner of the container to be saved
//
// Deprecated: Use PrmContainerSetEACL.Session instead.
func (x *PrmContainerSetEACL) WithinSession(s session.Container) {
x.Session = &s
}
func (x *PrmContainerSetEACL) buildRequest(c *Client) (*v2container.SetExtendedACLRequest, error) {
if x.Table == nil {
return nil, errorEACLTableNotSet
}
if len(x.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
eaclV2 := x.Table.ToV2()
var sig frostfscrypto.Signature
err := sig.Calculate(frostfsecdsa.SignerRFC6979(c.prm.key), eaclV2.StableMarshal(nil))
if err != nil {
return nil, fmt.Errorf("calculate signature: %w", err)
}
var sigv2 refs.Signature
sig.WriteToV2(&sigv2)
reqBody := new(v2container.SetExtendedACLRequestBody)
reqBody.SetEACL(eaclV2)
reqBody.SetSignature(&sigv2)
var meta v2session.RequestMetaHeader
writeXHeadersToMeta(x.XHeaders, &meta)
if x.Session != nil {
var tokv2 v2session.Token
x.Session.WriteToV2(&tokv2)
meta.SetSessionToken(&tokv2)
}
var req v2container.SetExtendedACLRequest
req.SetBody(reqBody)
c.prepareRequest(&req, &meta)
return &req, nil
}
// ResContainerSetEACL groups resulting values of ContainerSetEACL operation.
type ResContainerSetEACL struct {
statusRes
}
// ContainerSetEACL sends request to update eACL table of the FrostFS container.
//
// Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`.
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are returned as `error`, otherwise, are included
// in the returned result structure.
//
// Operation is asynchronous and no guaranteed even in the absence of errors.
// The required time is also not predictable.
//
// Success can be verified by reading by identifier (see EACL).
//
// Returns an error if parameters are set incorrectly (see PrmContainerSetEACL docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return statuses:
// - global (see Client docs).
func (c *Client) ContainerSetEACL(ctx context.Context, prm PrmContainerSetEACL) (*ResContainerSetEACL, error) {
req, err := prm.buildRequest(c)
if err != nil {
return nil, err
}
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
resp, err := rpcapi.SetEACL(&c.c, req, client.WithContext(ctx))
if err != nil {
return nil, err
}
var res ResContainerSetEACL
res.st, err = c.processResponse(resp)
return &res, err
}

92
client/container_space.go Normal file
View file

@ -0,0 +1,92 @@
package client
import (
"context"
"fmt"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
)
// PrmAnnounceSpace groups parameters of ContainerAnnounceUsedSpace operation.
type PrmAnnounceSpace struct {
XHeaders []string
Announcements []container.SizeEstimation
}
// SetValues sets values describing volume of space that is used for the container objects.
// Required parameter. Must not be empty.
//
// Must not be mutated before the end of the operation.
//
// Deprecated: Use PrmAnnounceSpace.Announcements instead.
func (x *PrmAnnounceSpace) SetValues(vs []container.SizeEstimation) {
x.Announcements = vs
}
func (x *PrmAnnounceSpace) buildRequest(c *Client) (*v2container.AnnounceUsedSpaceRequest, error) {
if len(x.Announcements) == 0 {
return nil, errorMissingAnnouncements
}
v2announce := make([]v2container.UsedSpaceAnnouncement, len(x.Announcements))
for i := range x.Announcements {
x.Announcements[i].WriteToV2(&v2announce[i])
}
reqBody := new(v2container.AnnounceUsedSpaceRequestBody)
reqBody.SetAnnouncements(v2announce)
var req v2container.AnnounceUsedSpaceRequest
req.SetBody(reqBody)
c.prepareRequest(&req, new(v2session.RequestMetaHeader))
return &req, nil
}
// ResAnnounceSpace groups resulting values of ContainerAnnounceUsedSpace operation.
type ResAnnounceSpace struct {
statusRes
}
// ContainerAnnounceUsedSpace sends request to announce volume of the space used for the container objects.
//
// Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`.
// If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are returned as `error`, otherwise, are included
// in the returned result structure.
//
// Operation is asynchronous and no guaranteed even in the absence of errors.
// The required time is also not predictable.
//
// At this moment success can not be checked.
//
// Returns an error if parameters are set incorrectly (see PrmAnnounceSpace docs).
// Context is required and must not be nil. It is used for network communication.
//
// Return statuses:
// - global (see Client docs).
func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, prm PrmAnnounceSpace) (*ResAnnounceSpace, error) {
req, err := prm.buildRequest(c)
if err != nil {
return nil, err
}
if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
resp, err := rpcapi.AnnounceUsedSpace(&c.c, req, client.WithContext(ctx))
if err != nil {
return nil, err
}
var res ResAnnounceSpace
res.st, err = c.processResponse(resp)
return &res, err
}

View file

@ -61,18 +61,6 @@ func IsErrSessionNotFound(err error) bool {
return wrapsErrType[*apistatus.SessionTokenNotFound](err) return wrapsErrType[*apistatus.SessionTokenNotFound](err)
} }
// IsErrAPEManagerAccessDenied checks if err corresponds to FrostFS status return
// corresponding to apemanager access deny. Supports wrapped errors.
func IsErrAPEManagerAccessDenied(err error) bool {
return wrapsErrType[*apistatus.APEManagerAccessDenied](err)
}
// IsErrNodeUnderMaintenance checks if err corresponds to FrostFS status return
// corresponding to nodes being under maintenance. Supports wrapped errors.
func IsErrNodeUnderMaintenance(err error) bool {
return wrapsErrType[*apistatus.NodeUnderMaintenance](err)
}
// returns error describing missing field with the given name. // returns error describing missing field with the given name.
func newErrMissingResponseField(name string) error { func newErrMissingResponseField(name string) error {
return fmt.Errorf("missing %s field in the response", name) return fmt.Errorf("missing %s field in the response", name)

View file

@ -33,15 +33,10 @@ func TestErrors(t *testing.T) {
{ {
check: client.IsErrSessionExpired, check: client.IsErrSessionExpired,
err: new(apistatus.SessionTokenExpired), err: new(apistatus.SessionTokenExpired),
}, }, {
{
check: client.IsErrSessionNotFound, check: client.IsErrSessionNotFound,
err: new(apistatus.SessionTokenNotFound), err: new(apistatus.SessionTokenNotFound),
}, },
{
check: client.IsErrNodeUnderMaintenance,
err: new(apistatus.NodeUnderMaintenance),
},
} }
for i := range errs { for i := range errs {

View file

@ -53,9 +53,9 @@ func (x ResEndpointInfo) NodeInfo() netmap.NodeInfo {
// Method can be used as a health check to see if node is alive and responds to requests. // Method can be used as a health check to see if node is alive and responds to requests.
// //
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmEndpointInfo docs). // Returns an error if parameters are set incorrectly (see PrmEndpointInfo docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -71,7 +71,7 @@ func (c *Client) EndpointInfo(ctx context.Context, prm PrmEndpointInfo) (*ResEnd
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }
@ -140,9 +140,9 @@ func (x ResNetworkInfo) Info() netmap.NetworkInfo {
// NetworkInfo requests information about the FrostFS network of which the remote server is a part. // NetworkInfo requests information about the FrostFS network of which the remote server is a part.
// //
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmNetworkInfo docs). // Returns an error if parameters are set incorrectly (see PrmNetworkInfo docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -158,7 +158,7 @@ func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetwo
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }
@ -186,7 +186,8 @@ func (c *Client) NetworkInfo(ctx context.Context, prm PrmNetworkInfo) (*ResNetwo
} }
// PrmNetMapSnapshot groups parameters of NetMapSnapshot operation. // PrmNetMapSnapshot groups parameters of NetMapSnapshot operation.
type PrmNetMapSnapshot struct{} type PrmNetMapSnapshot struct {
}
// ResNetMapSnapshot groups resulting values of NetMapSnapshot operation. // ResNetMapSnapshot groups resulting values of NetMapSnapshot operation.
type ResNetMapSnapshot struct { type ResNetMapSnapshot struct {
@ -203,9 +204,9 @@ func (x ResNetMapSnapshot) NetMap() netmap.NetMap {
// NetMapSnapshot requests current network view of the remote server. // NetMapSnapshot requests current network view of the remote server.
// //
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly. // Returns an error if parameters are set incorrectly.
// Context is required and MUST NOT be nil. It is used for network communication. // Context is required and MUST NOT be nil. It is used for network communication.
@ -227,7 +228,7 @@ func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResN
req.SetBody(&body) req.SetBody(&body)
c.prepareRequest(&req, &meta) c.prepareRequest(&req, &meta)
err := signature.SignServiceMessage(&c.prm.Key, &req) err := signature.SignServiceMessage(&c.prm.key, &req)
if err != nil { if err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }
@ -239,8 +240,12 @@ func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResN
var res ResNetMapSnapshot var res ResNetMapSnapshot
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
const fieldNetMap = "network map" const fieldNetMap = "network map"

View file

@ -67,7 +67,6 @@ func TestClient_NetMapSnapshot(t *testing.T) {
var res *ResNetMapSnapshot var res *ResNetMapSnapshot
var srv serverNetMap var srv serverNetMap
c := newClient(&srv) c := newClient(&srv)
c.prm.DisableFrostFSFailuresResolution()
ctx := context.Background() ctx := context.Background()
// request signature // request signature

View file

@ -112,9 +112,9 @@ func (prm *PrmObjectDelete) buildRequest(c *Client) (*v2object.DeleteRequest, er
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`, // Any client's internal or transport errors are returned as `error`,
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmObjectDelete docs). // Returns an error if parameters are set incorrectly (see PrmObjectDelete docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -131,7 +131,7 @@ func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObj
return nil, err return nil, err
} }
key := c.prm.Key key := c.prm.key
if prm.Key != nil { if prm.Key != nil {
key = *prm.Key key = *prm.Key
} }
@ -148,8 +148,12 @@ func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObj
var res ResObjectDelete var res ResObjectDelete
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
const fieldTombstone = "tombstone" const fieldTombstone = "tombstone"

View file

@ -150,9 +150,6 @@ func (x *ObjectReader) ReadHeader(dst *object.Object) bool {
case *v2object.SplitInfo: case *v2object.SplitInfo:
x.err = object.NewSplitInfoError(object.NewSplitInfoFromV2(v)) x.err = object.NewSplitInfoError(object.NewSplitInfoFromV2(v))
return false return false
case *v2object.ECInfo:
x.err = object.NewECInfoError(object.NewECInfoFromV2(v))
return false
case *v2object.GetObjectPartInit: case *v2object.GetObjectPartInit:
partInit = v partInit = v
} }
@ -261,7 +258,6 @@ func (x *ObjectReader) close(ignoreEOF bool) (*ResObjectGet, error) {
// Return errors: // Return errors:
// //
// *object.SplitInfoError (returned on virtual objects with PrmObjectGet.MakeRaw). // *object.SplitInfoError (returned on virtual objects with PrmObjectGet.MakeRaw).
// *object.ECInfoError (returned on erasure-coded objects with PrmObjectGet.MakeRaw).
// //
// Return statuses: // Return statuses:
// - global (see Client docs); // - global (see Client docs);
@ -311,7 +307,7 @@ func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectRe
key := prm.Key key := prm.Key
if key == nil { if key == nil {
key = &c.prm.Key key = &c.prm.key
} }
err = signature.SignServiceMessage(key, req) err = signature.SignServiceMessage(key, req)
@ -448,9 +444,9 @@ func (prm *PrmObjectHead) buildRequest(c *Client) (*v2object.HeadRequest, error)
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`, // Any client's internal or transport errors are returned as `error`,
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmObjectHead docs). // Returns an error if parameters are set incorrectly (see PrmObjectHead docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -458,7 +454,6 @@ func (prm *PrmObjectHead) buildRequest(c *Client) (*v2object.HeadRequest, error)
// Return errors: // Return errors:
// //
// *object.SplitInfoError (returned on virtual objects with PrmObjectHead.MakeRaw). // *object.SplitInfoError (returned on virtual objects with PrmObjectHead.MakeRaw).
// *object.ECInfoError (returned on erasure-coded objects with PrmObjectHead.MakeRaw).
// //
// Return statuses: // Return statuses:
// - global (see Client docs); // - global (see Client docs);
@ -473,7 +468,7 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
return nil, err return nil, err
} }
key := c.prm.Key key := c.prm.key
if prm.Key != nil { if prm.Key != nil {
key = *prm.Key key = *prm.Key
} }
@ -492,8 +487,12 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
var res ResObjectHead var res ResObjectHead
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
res.idObj = *prm.ObjectID res.idObj = *prm.ObjectID
@ -503,8 +502,6 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
return nil, fmt.Errorf("unexpected header type %T", v) return nil, fmt.Errorf("unexpected header type %T", v)
case *v2object.SplitInfo: case *v2object.SplitInfo:
return nil, object.NewSplitInfoError(object.NewSplitInfoFromV2(v)) return nil, object.NewSplitInfoError(object.NewSplitInfoFromV2(v))
case *v2object.ECInfo:
return nil, object.NewECInfoError(object.NewECInfoFromV2(v))
case *v2object.HeaderWithSignature: case *v2object.HeaderWithSignature:
res.hdr = v res.hdr = v
} }
@ -668,9 +665,6 @@ func (x *ObjectRangeReader) readChunk(buf []byte) (int, bool) {
case *v2object.SplitInfo: case *v2object.SplitInfo:
x.err = object.NewSplitInfoError(object.NewSplitInfoFromV2(v)) x.err = object.NewSplitInfoError(object.NewSplitInfoFromV2(v))
return read, false return read, false
case *v2object.ECInfo:
x.err = object.NewECInfoError(object.NewECInfoFromV2(v))
return read, false
case *v2object.GetRangePartChunk: case *v2object.GetRangePartChunk:
partChunk = v partChunk = v
} }
@ -731,7 +725,6 @@ func (x *ObjectRangeReader) close(ignoreEOF bool) (*ResObjectRange, error) {
// Return errors: // Return errors:
// //
// *object.SplitInfoError (returned on virtual objects with PrmObjectRange.MakeRaw). // *object.SplitInfoError (returned on virtual objects with PrmObjectRange.MakeRaw).
// *object.ECInfoError (returned on erasure-coded objects with PrmObjectRange.MakeRaw).
// //
// Return statuses: // Return statuses:
// - global (see Client docs); // - global (see Client docs);
@ -783,7 +776,7 @@ func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*Obje
key := prm.Key key := prm.Key
if key == nil { if key == nil {
key = &c.prm.Key key = &c.prm.key
} }
err = signature.SignServiceMessage(key, req) err = signature.SignServiceMessage(key, req)

View file

@ -13,53 +13,121 @@ import (
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
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/object"
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"
) )
// PrmObjectHash groups parameters of ObjectHash operation. // PrmObjectHash groups parameters of ObjectHash operation.
type PrmObjectHash struct { type PrmObjectHash struct {
XHeaders []string meta v2session.RequestMetaHeader
BearerToken *bearer.Token body v2object.GetRangeHashRequestBody
Session *session.Object csAlgo v2refs.ChecksumType
Local bool addr v2refs.Address
Ranges []object.Range keySet bool
key ecdsa.PrivateKey
Salt []byte
ChecksumType checksum.Type
ContainerID *cid.ID
ObjectID *oid.ID
Key *ecdsa.PrivateKey
} }
// UseKey specifies private key to sign the requests. // UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used. // If key is not provided, then Client default key is used.
func (x *PrmObjectHash) UseKey(key ecdsa.PrivateKey) {
x.keySet = true
x.key = key
}
// MarkLocal tells the server to execute the operation locally.
func (x *PrmObjectHash) MarkLocal() {
x.meta.SetTTL(1)
}
// WithinSession specifies session within which object should be read.
// //
// Deprecated: Use PrmObjectHash.Key instead. // Creator of the session acquires the authorship of the request.
func (prm *PrmObjectHash) UseKey(key ecdsa.PrivateKey) { // This may affect the execution of an operation (e.g. access control).
prm.Key = &key //
// Must be signed.
func (x *PrmObjectHash) WithinSession(t session.Object) {
var tv2 v2session.Token
t.WriteToV2(&tv2)
x.meta.SetSessionToken(&tv2)
}
// WithBearerToken attaches bearer token to be used for the operation.
//
// If set, underlying eACL rules will be used in access control.
//
// Must be signed.
func (x *PrmObjectHash) WithBearerToken(t bearer.Token) {
var v2token acl.BearerToken
t.WriteToV2(&v2token)
x.meta.SetBearerToken(&v2token)
}
// FromContainer specifies FrostFS container of the object.
// Required parameter.
func (x *PrmObjectHash) FromContainer(id cid.ID) {
var cidV2 v2refs.ContainerID
id.WriteToV2(&cidV2)
x.addr.SetContainerID(&cidV2)
}
// ByID specifies identifier of the requested object.
// Required parameter.
func (x *PrmObjectHash) ByID(id oid.ID) {
var idV2 v2refs.ObjectID
id.WriteToV2(&idV2)
x.addr.SetObjectID(&idV2)
}
// SetRangeList sets list of ranges in (offset, length) pair format.
// Required parameter.
//
// If passed as slice, then it must not be mutated before the operation completes.
func (x *PrmObjectHash) SetRangeList(r ...uint64) {
ln := len(r)
if ln%2 != 0 {
panic("odd number of range parameters")
}
rs := make([]v2object.Range, ln/2)
for i := 0; i < ln/2; i++ {
rs[i].SetOffset(r[2*i])
rs[i].SetLength(r[2*i+1])
}
x.body.SetRanges(rs)
} }
// TillichZemorAlgo changes the hash function to Tillich-Zemor // TillichZemorAlgo changes the hash function to Tillich-Zemor
// (https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf). // (https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf).
// //
// By default, SHA256 hash function is used/. // By default, SHA256 hash function is used.
func (x *PrmObjectHash) TillichZemorAlgo() {
x.csAlgo = v2refs.TillichZemor
}
// UseSalt sets the salt to XOR the data range before hashing.
// //
// Deprecated: Use PrmObjectHash.ChecksumType instead. // Must not be mutated before the operation completes.
func (prm *PrmObjectHash) TillichZemorAlgo() { func (x *PrmObjectHash) UseSalt(salt []byte) {
prm.ChecksumType = checksum.TZ x.body.SetSalt(salt)
}
// WithXHeaders specifies list of extended headers (string key-value pairs)
// to be attached to the request. Must have an even length.
//
// Slice must not be mutated until the operation completes.
func (x *PrmObjectHash) WithXHeaders(hs ...string) {
writeXHeadersToMeta(hs, &x.meta)
} }
// ResObjectHash groups resulting values of ObjectHash operation. // ResObjectHash groups resulting values of ObjectHash operation.
@ -74,76 +142,6 @@ func (x ResObjectHash) Checksums() [][]byte {
return x.checksums return x.checksums
} }
func (prm *PrmObjectHash) buildRequest(c *Client) (*v2object.GetRangeHashRequest, error) {
if prm.ContainerID == nil {
return nil, errorMissingContainer
}
if prm.ObjectID == nil {
return nil, errorMissingObject
}
if len(prm.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
if len(prm.Ranges) == 0 {
return nil, errorMissingRanges
}
meta := new(v2session.RequestMetaHeader)
writeXHeadersToMeta(prm.XHeaders, meta)
if prm.BearerToken != nil {
v2BearerToken := new(acl.BearerToken)
prm.BearerToken.WriteToV2(v2BearerToken)
meta.SetBearerToken(v2BearerToken)
}
if prm.Session != nil {
v2SessionToken := new(v2session.Token)
prm.Session.WriteToV2(v2SessionToken)
meta.SetSessionToken(v2SessionToken)
}
if prm.Local {
meta.SetTTL(1)
}
addr := new(v2refs.Address)
cnrV2 := new(v2refs.ContainerID)
prm.ContainerID.WriteToV2(cnrV2)
addr.SetContainerID(cnrV2)
objV2 := new(v2refs.ObjectID)
prm.ObjectID.WriteToV2(objV2)
addr.SetObjectID(objV2)
rs := make([]v2object.Range, len(prm.Ranges))
for i := range prm.Ranges {
rs[i].SetOffset(prm.Ranges[i].GetOffset())
rs[i].SetLength(prm.Ranges[i].GetLength())
}
body := new(v2object.GetRangeHashRequestBody)
body.SetAddress(addr)
body.SetRanges(rs)
body.SetSalt(prm.Salt)
if prm.ChecksumType == checksum.Unknown {
body.SetType(v2refs.SHA256)
} else {
body.SetType(v2refs.ChecksumType(prm.ChecksumType))
}
req := new(v2object.GetRangeHashRequest)
req.SetBody(body)
c.prepareRequest(req, meta)
return req, nil
}
// ObjectHash requests checksum of the range list of the object payload using // ObjectHash requests checksum of the range list of the object payload using
// FrostFS API protocol. // FrostFS API protocol.
// //
@ -152,9 +150,9 @@ func (prm *PrmObjectHash) buildRequest(c *Client) (*v2object.GetRangeHashRequest
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`, // Any client's internal or transport errors are returned as `error`,
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmObjectHash docs). // Returns an error if parameters are set incorrectly (see PrmObjectHash docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -167,30 +165,49 @@ func (prm *PrmObjectHash) buildRequest(c *Client) (*v2object.GetRangeHashRequest
// - *apistatus.ObjectOutOfRange; // - *apistatus.ObjectOutOfRange;
// - *apistatus.SessionTokenExpired. // - *apistatus.SessionTokenExpired.
func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) { func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) {
req, err := prm.buildRequest(c) switch {
if err != nil { case prm.addr.GetContainerID() == nil:
return nil, err return nil, errorMissingContainer
case prm.addr.GetObjectID() == nil:
return nil, errorMissingObject
case len(prm.body.GetRanges()) == 0:
return nil, errorMissingRanges
} }
key := c.prm.Key prm.body.SetAddress(&prm.addr)
if prm.Key != nil { if prm.csAlgo == v2refs.UnknownChecksum {
key = *prm.Key prm.body.SetType(v2refs.SHA256)
} else {
prm.body.SetType(prm.csAlgo)
} }
err = signature.SignServiceMessage(&key, req) var req v2object.GetRangeHashRequest
c.prepareRequest(&req, &prm.meta)
req.SetBody(&prm.body)
key := c.prm.key
if prm.keySet {
key = prm.key
}
err := signature.SignServiceMessage(&key, &req)
if err != nil { if err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }
resp, err := rpcapi.HashObjectRange(&c.c, req, client.WithContext(ctx)) resp, err := rpcapi.HashObjectRange(&c.c, &req, client.WithContext(ctx))
if err != nil { if err != nil {
return nil, fmt.Errorf("write request: %w", err) return nil, fmt.Errorf("write request: %w", err)
} }
var res ResObjectHash var res ResObjectHash
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
res.checksums = resp.GetBody().GetHashList() res.checksums = resp.GetBody().GetHashList()

View file

@ -1,266 +0,0 @@
package client
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"io"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
)
// ObjectPatcher is designed to patch an object.
//
// Must be initialized using Client.ObjectPatchInit, any other
// usage is unsafe.
type ObjectPatcher interface {
// PatchAttributes patches attributes. Attributes can be patched no more than once,
// otherwise, the server returns an error.
//
// Result means success. Failure reason can be received via Close.
PatchAttributes(ctx context.Context, newAttrs []object.Attribute, replace bool) bool
// PatchPayload patches the object's payload.
//
// PatchPayload receives `payloadReader` and thus the payload of the patch is read and sent by chunks of
// `MaxChunkLength` length.
//
// Result means success. Failure reason can be received via Close.
PatchPayload(ctx context.Context, rng *object.Range, payloadReader io.Reader) bool
// Close ends patching the object and returns the result of the operation
// along with the final results. Must be called after using the ObjectPatcher.
//
// Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as Go built-in error.
// If Client is tuned to resolve FrostFS API statuses, then FrostFS failures
// codes are returned as error.
//
// Return statuses:
// - global (see Client docs);
// - *apistatus.ContainerNotFound;
// - *apistatus.ContainerAccessDenied;
// - *apistatus.ObjectAccessDenied;
// - *apistatus.ObjectAlreadyRemoved;
// - *apistatus.ObjectLocked;
// - *apistatus.ObjectOutOfRange;
// - *apistatus.SessionTokenNotFound;
// - *apistatus.SessionTokenExpired.
Close(_ context.Context) (*ResObjectPatch, error)
}
// ResObjectPatch groups resulting values of ObjectPatch operation.
type ResObjectPatch struct {
statusRes
obj oid.ID
}
// ObjectID returns an object ID of the patched object.
func (r ResObjectPatch) ObjectID() oid.ID {
return r.obj
}
// PrmObjectPatch groups parameters of ObjectPatch operation.
type PrmObjectPatch struct {
XHeaders []string
Address oid.Address
BearerToken *bearer.Token
Session *session.Object
Key *ecdsa.PrivateKey
MaxChunkLength int
}
// ObjectPatchInit initializes object patcher.
func (c *Client) ObjectPatchInit(ctx context.Context, prm PrmObjectPatch) (ObjectPatcher, error) {
if len(prm.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
var objectPatcher objectPatcher
stream, err := rpcapi.Patch(&c.c, &objectPatcher.respV2, client.WithContext(ctx))
if err != nil {
return nil, fmt.Errorf("open stream: %w", err)
}
objectPatcher.addr = prm.Address
objectPatcher.key = &c.prm.Key
if prm.Key != nil {
objectPatcher.key = prm.Key
}
objectPatcher.client = c
objectPatcher.stream = stream
if prm.MaxChunkLength > 0 {
objectPatcher.maxChunkLen = prm.MaxChunkLength
} else {
objectPatcher.maxChunkLen = defaultGRPCPayloadChunkLen
}
objectPatcher.req.SetBody(&v2object.PatchRequestBody{})
meta := new(v2session.RequestMetaHeader)
writeXHeadersToMeta(prm.XHeaders, meta)
if prm.BearerToken != nil {
v2BearerToken := new(acl.BearerToken)
prm.BearerToken.WriteToV2(v2BearerToken)
meta.SetBearerToken(v2BearerToken)
}
if prm.Session != nil {
v2SessionToken := new(v2session.Token)
prm.Session.WriteToV2(v2SessionToken)
meta.SetSessionToken(v2SessionToken)
}
c.prepareRequest(&objectPatcher.req, meta)
return &objectPatcher, nil
}
type objectPatcher struct {
client *Client
stream interface {
Write(*v2object.PatchRequest) error
Close() error
}
key *ecdsa.PrivateKey
res ResObjectPatch
err error
addr oid.Address
req v2object.PatchRequest
respV2 v2object.PatchResponse
maxChunkLen int
}
func (x *objectPatcher) PatchAttributes(_ context.Context, newAttrs []object.Attribute, replace bool) bool {
return x.patch(&object.Patch{
Address: x.addr,
NewAttributes: newAttrs,
ReplaceAttributes: replace,
})
}
func (x *objectPatcher) PatchPayload(_ context.Context, rng *object.Range, payloadReader io.Reader) bool {
offset := rng.GetOffset()
buf := make([]byte, x.maxChunkLen)
for patchIter := 0; ; patchIter++ {
n, err := payloadReader.Read(buf)
if err != nil && err != io.EOF {
x.err = fmt.Errorf("read payload: %w", err)
return false
}
if n == 0 {
if patchIter == 0 {
if rng.GetLength() == 0 {
x.err = errors.New("zero-length empty payload patch can't be applied")
return false
}
if !x.patch(&object.Patch{
Address: x.addr,
PayloadPatch: &object.PayloadPatch{
Range: rng,
Chunk: []byte{},
},
}) {
return false
}
}
break
}
rngPart := object.NewRange()
if patchIter == 0 {
rngPart.SetOffset(offset)
rngPart.SetLength(rng.GetLength())
} else {
rngPart.SetOffset(offset + rng.GetLength())
}
if !x.patch(&object.Patch{
Address: x.addr,
PayloadPatch: &object.PayloadPatch{
Range: rngPart,
Chunk: buf[:n],
},
}) {
return false
}
if err == io.EOF {
break
}
}
return true
}
func (x *objectPatcher) patch(patch *object.Patch) bool {
x.req.SetBody(patch.ToV2())
x.req.SetVerificationHeader(nil)
x.err = signature.SignServiceMessage(x.key, &x.req)
if x.err != nil {
x.err = fmt.Errorf("sign message: %w", x.err)
return false
}
x.err = x.stream.Write(&x.req)
return x.err == nil
}
func (x *objectPatcher) Close(_ context.Context) (*ResObjectPatch, error) {
// Ignore io.EOF error, because it is expected error for client-side
// stream termination by the server. E.g. when stream contains invalid
// message. Server returns an error in response message (in status).
if x.err != nil && !errors.Is(x.err, io.EOF) {
return nil, x.err
}
if x.err = x.stream.Close(); x.err != nil {
return nil, x.err
}
x.res.st, x.err = x.client.processResponse(&x.respV2)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) {
return &x.res, x.err
}
const fieldID = "ID"
idV2 := x.respV2.Body.ObjectID
if idV2 == nil {
return nil, newErrMissingResponseField(fieldID)
}
x.err = x.res.obj.ReadFromV2(*idV2)
if x.err != nil {
x.err = newErrInvalidResponseField(fieldID, x.err)
}
return &x.res, nil
}

View file

@ -1,295 +0,0 @@
package client
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"testing"
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/stretchr/testify/require"
)
type mockPatchStream struct {
streamedPayloadPatches []*object.PayloadPatch
}
func (m *mockPatchStream) Write(r *v2object.PatchRequest) error {
pp := new(object.PayloadPatch)
pp.FromV2(r.GetBody().GetPatch())
if r.GetBody().GetPatch() != nil {
bodyChunk := r.GetBody().GetPatch().Chunk
pp.Chunk = make([]byte, len(bodyChunk))
copy(pp.Chunk, bodyChunk)
}
m.streamedPayloadPatches = append(m.streamedPayloadPatches, pp)
return nil
}
func (m *mockPatchStream) Close() error {
return nil
}
func TestObjectPatcher(t *testing.T) {
type part struct {
offset int
length int
chunk string
}
for _, test := range []struct {
name string
patchPayload string
rng *object.Range
maxChunkLen int
expectParts []part
}{
{
name: "no split payload patch",
patchPayload: "011111",
rng: newRange(0, 6),
maxChunkLen: defaultGRPCPayloadChunkLen,
expectParts: []part{
{
offset: 0,
length: 6,
chunk: "011111",
},
},
},
{
name: "splitted payload patch",
patchPayload: "012345",
rng: newRange(0, 6),
maxChunkLen: 2,
expectParts: []part{
{
offset: 0,
length: 6,
chunk: "01",
},
{
offset: 6,
length: 0,
chunk: "23",
},
{
offset: 6,
length: 0,
chunk: "45",
},
},
},
{
name: "splitted payload patch with zero-length subpatches",
patchPayload: "0123456789!@",
rng: newRange(0, 4),
maxChunkLen: 2,
expectParts: []part{
{
offset: 0,
length: 4,
chunk: "01",
},
{
offset: 4,
length: 0,
chunk: "23",
},
{
offset: 4,
length: 0,
chunk: "45",
},
{
offset: 4,
length: 0,
chunk: "67",
},
{
offset: 4,
length: 0,
chunk: "89",
},
{
offset: 4,
length: 0,
chunk: "!@",
},
},
},
{
name: "splitted payload patch with zero-length subpatches only",
patchPayload: "0123456789!@",
rng: newRange(0, 0),
maxChunkLen: 2,
expectParts: []part{
{
offset: 0,
length: 0,
chunk: "01",
},
{
offset: 0,
length: 0,
chunk: "23",
},
{
offset: 0,
length: 0,
chunk: "45",
},
{
offset: 0,
length: 0,
chunk: "67",
},
{
offset: 0,
length: 0,
chunk: "89",
},
{
offset: 0,
length: 0,
chunk: "!@",
},
},
},
} {
t.Run(test.name, func(t *testing.T) {
m := &mockPatchStream{}
pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
patcher := objectPatcher{
client: &Client{},
stream: m,
addr: oidtest.Address(),
key: pk,
maxChunkLen: test.maxChunkLen,
}
success := patcher.PatchAttributes(context.Background(), nil, false)
require.True(t, success)
success = patcher.PatchPayload(context.Background(), test.rng, bytes.NewReader([]byte(test.patchPayload)))
require.True(t, success)
require.Len(t, m.streamedPayloadPatches, len(test.expectParts)+1)
// m.streamedPayloadPatches[0] is attribute patch, so skip it
for i, part := range test.expectParts {
requireRangeChunk(t, m.streamedPayloadPatches[i+1], part.offset, part.length, part.chunk)
}
})
}
}
func TestRepeatPayloadPatch(t *testing.T) {
t.Run("no payload patch partioning", func(t *testing.T) {
m := &mockPatchStream{}
pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
const maxChunkLen = 20
patcher := objectPatcher{
client: &Client{},
stream: m,
addr: oidtest.Address(),
key: pk,
maxChunkLen: maxChunkLen,
}
for _, pp := range []struct {
patchPayload string
rng *object.Range
}{
{
patchPayload: "xxxxxxxxxx",
rng: newRange(1, 6),
},
{
patchPayload: "yyyyyyyyyy",
rng: newRange(5, 9),
},
{
patchPayload: "zzzzzzzzzz",
rng: newRange(10, 0),
},
} {
success := patcher.PatchPayload(context.Background(), pp.rng, bytes.NewReader([]byte(pp.patchPayload)))
require.True(t, success)
}
requireRangeChunk(t, m.streamedPayloadPatches[0], 1, 6, "xxxxxxxxxx")
requireRangeChunk(t, m.streamedPayloadPatches[1], 5, 9, "yyyyyyyyyy")
requireRangeChunk(t, m.streamedPayloadPatches[2], 10, 0, "zzzzzzzzzz")
})
t.Run("payload patch partioning", func(t *testing.T) {
m := &mockPatchStream{}
pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
const maxChunkLen = 5
patcher := objectPatcher{
client: &Client{},
stream: m,
addr: oidtest.Address(),
key: pk,
maxChunkLen: maxChunkLen,
}
for _, pp := range []struct {
patchPayload string
rng *object.Range
}{
{
patchPayload: "xxxxxxxxxx",
rng: newRange(1, 6),
},
{
patchPayload: "yyyyyyyyyy",
rng: newRange(5, 9),
},
{
patchPayload: "zzzzzzzzzz",
rng: newRange(10, 0),
},
} {
success := patcher.PatchPayload(context.Background(), pp.rng, bytes.NewReader([]byte(pp.patchPayload)))
require.True(t, success)
}
requireRangeChunk(t, m.streamedPayloadPatches[0], 1, 6, "xxxxx")
requireRangeChunk(t, m.streamedPayloadPatches[1], 7, 0, "xxxxx")
requireRangeChunk(t, m.streamedPayloadPatches[2], 5, 9, "yyyyy")
requireRangeChunk(t, m.streamedPayloadPatches[3], 14, 0, "yyyyy")
requireRangeChunk(t, m.streamedPayloadPatches[4], 10, 0, "zzzzz")
requireRangeChunk(t, m.streamedPayloadPatches[5], 10, 0, "zzzzz")
})
}
func requireRangeChunk(t *testing.T, pp *object.PayloadPatch, offset, length int, chunk string) {
require.NotNil(t, pp)
require.Equal(t, uint64(offset), pp.Range.GetOffset())
require.Equal(t, uint64(length), pp.Range.GetLength())
require.Equal(t, []byte(chunk), pp.Chunk)
}
func newRange(offest, length uint64) *object.Range {
rng := &object.Range{}
rng.SetOffset(offest)
rng.SetLength(length)
return rng
}

View file

@ -4,7 +4,6 @@ import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
buffPool "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/pool"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@ -37,8 +36,6 @@ type PrmObjectPutInit struct {
WithoutHomomorphHash bool WithoutHomomorphHash bool
Key *ecdsa.PrivateKey Key *ecdsa.PrivateKey
Pool *buffPool.BufferPool
} }
// SetCopiesNumber sets number of object copies that is enough to consider put successful. // SetCopiesNumber sets number of object copies that is enough to consider put successful.
@ -72,8 +69,7 @@ func (x *PrmObjectPutInit) SetGRPCPayloadChunkLen(v int) {
type ResObjectPut struct { type ResObjectPut struct {
statusRes statusRes
obj oid.ID obj oid.ID
epoch uint64
} }
// StoredObjectID returns identifier of the saved object. // StoredObjectID returns identifier of the saved object.
@ -81,11 +77,6 @@ func (x ResObjectPut) StoredObjectID() oid.ID {
return x.obj return x.obj
} }
// StoredEpoch returns creation epoch of the saved object.
func (x ResObjectPut) StoredEpoch() uint64 {
return x.epoch
}
// ObjectWriter is designed to write one object or // ObjectWriter is designed to write one object or
// multiple parts of one object to FrostFS system. // multiple parts of one object to FrostFS system.
// //

View file

@ -28,7 +28,7 @@ func (c *Client) objectPutInitRaw(ctx context.Context, prm PrmObjectPutInit) (*o
return nil, fmt.Errorf("open stream: %w", err) return nil, fmt.Errorf("open stream: %w", err)
} }
w.key = &c.prm.Key w.key = &c.prm.key
if prm.Key != nil { if prm.Key != nil {
w.key = prm.Key w.key = prm.Key
} }
@ -156,8 +156,12 @@ func (x *objectWriterRaw) Close(_ context.Context) (*ResObjectPut, error) {
} }
x.res.st, x.err = x.client.processResponse(&x.respV2) x.res.st, x.err = x.client.processResponse(&x.respV2)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) { if x.err != nil {
return &x.res, x.err return nil, x.err
}
if !apistatus.IsSuccessful(x.res.st) {
return &x.res, nil
} }
const fieldID = "ID" const fieldID = "ID"
@ -171,7 +175,6 @@ func (x *objectWriterRaw) Close(_ context.Context) (*ResObjectPut, error) {
if x.err != nil { if x.err != nil {
x.err = newErrInvalidResponseField(fieldID, x.err) x.err = newErrInvalidResponseField(fieldID, x.err)
} }
x.res.epoch = x.respV2.GetMetaHeader().GetEpoch()
return &x.res, nil return &x.res, nil
} }

View file

@ -93,13 +93,6 @@ func (prm *PrmObjectPutSingle) SetObject(o *v2object.Object) {
// ResObjectPutSingle groups resulting values of PutSingle operation. // ResObjectPutSingle groups resulting values of PutSingle operation.
type ResObjectPutSingle struct { type ResObjectPutSingle struct {
statusRes statusRes
epoch uint64
}
// Epoch returns creation epoch of the saved object.
func (r *ResObjectPutSingle) Epoch() uint64 {
return r.epoch
} }
func (prm *PrmObjectPutSingle) buildRequest(c *Client) (*v2object.PutSingleRequest, error) { func (prm *PrmObjectPutSingle) buildRequest(c *Client) (*v2object.PutSingleRequest, error) {
@ -149,7 +142,7 @@ func (c *Client) ObjectPutSingle(ctx context.Context, prm PrmObjectPutSingle) (*
return nil, err return nil, err
} }
key := &c.prm.Key key := &c.prm.key
if prm.Key != nil { if prm.Key != nil {
key = prm.Key key = prm.Key
} }
@ -167,9 +160,8 @@ func (c *Client) ObjectPutSingle(ctx context.Context, prm PrmObjectPutSingle) (*
var res ResObjectPutSingle var res ResObjectPutSingle
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil { if err != nil {
return &res, err return nil, err
} }
res.epoch = resp.GetMetaHeader().GetEpoch()
return &res, nil return &res, nil
} }

View file

@ -3,7 +3,6 @@ package client
import ( import (
"context" "context"
buffPool "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/pool"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
@ -17,7 +16,7 @@ func (c *Client) objectPutInitTransformer(prm PrmObjectPutInit) (*objectWriterTr
client: c, client: c,
prm: prm, prm: prm,
} }
key := &c.prm.Key key := &c.prm.key
if prm.Key != nil { if prm.Key != nil {
key = prm.Key key = prm.Key
} }
@ -27,7 +26,6 @@ func (c *Client) objectPutInitTransformer(prm PrmObjectPutInit) (*objectWriterTr
MaxSize: prm.MaxSize, MaxSize: prm.MaxSize,
WithoutHomomorphicHash: prm.WithoutHomomorphHash, WithoutHomomorphicHash: prm.WithoutHomomorphHash,
NetworkState: prm.EpochSource, NetworkState: prm.EpochSource,
Pool: prm.Pool,
}) })
return &w, nil return &w, nil
} }
@ -49,20 +47,13 @@ func (x *objectWriterTransformer) WritePayloadChunk(ctx context.Context, chunk [
} }
func (x *objectWriterTransformer) Close(ctx context.Context) (*ResObjectPut, error) { func (x *objectWriterTransformer) Close(ctx context.Context) (*ResObjectPut, error) {
if x.err != nil {
return nil, x.err
}
ai, err := x.ot.Close(ctx) ai, err := x.ot.Close(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ai != nil { if ai != nil && ai.ParentID != nil {
x.it.res.epoch = ai.Epoch x.it.res.obj = *ai.ParentID
if ai.ParentID != nil {
x.it.res.obj = *ai.ParentID
}
} }
return x.it.res, nil return x.it.res, nil
} }
@ -92,7 +83,7 @@ func (it *internalTarget) putAsStream(ctx context.Context, o *object.Object) err
wrt.WritePayloadChunk(ctx, o.Payload()) wrt.WritePayloadChunk(ctx, o.Payload())
} }
it.res, err = wrt.Close(ctx) it.res, err = wrt.Close(ctx)
if err == nil && it.client.prm.DisableFrostFSErrorResolution && !apistatus.IsSuccessful(it.res.st) { if err == nil && !it.client.prm.resolveFrostFSErrors && !apistatus.IsSuccessful(it.res.st) {
err = apistatus.ErrFromStatus(it.res.st) err = apistatus.ErrFromStatus(it.res.st)
} }
return err return err
@ -119,26 +110,15 @@ func (it *internalTarget) tryPutSingle(ctx context.Context, o *object.Object) (b
} }
if err == nil { if err == nil {
it.returnBuffPool(o.Payload())
id, _ := o.ID() id, _ := o.ID()
it.res = &ResObjectPut{ it.res = &ResObjectPut{
statusRes: res.statusRes, statusRes: res.statusRes,
obj: id, obj: id,
epoch: res.epoch,
} }
if it.client.prm.DisableFrostFSErrorResolution && !apistatus.IsSuccessful(it.res.st) { if !it.client.prm.resolveFrostFSErrors && !apistatus.IsSuccessful(it.res.st) {
return true, apistatus.ErrFromStatus(it.res.st) return true, apistatus.ErrFromStatus(it.res.st)
} }
return true, nil return true, nil
} }
return true, err return true, err
} }
func (it *internalTarget) returnBuffPool(playback []byte) {
if it.prm.Pool == nil {
return
}
var buffer buffPool.Buffer
buffer.Data = playback
it.prm.Pool.Put(&buffer)
}

View file

@ -24,26 +24,19 @@ import (
// PrmObjectSearch groups parameters of ObjectSearch operation. // PrmObjectSearch groups parameters of ObjectSearch operation.
type PrmObjectSearch struct { type PrmObjectSearch struct {
XHeaders []string meta v2session.RequestMetaHeader
Local bool key *ecdsa.PrivateKey
BearerToken *bearer.Token cnrSet bool
cnrID cid.ID
Session *session.Object filters object.SearchFilters
ContainerID *cid.ID
Key *ecdsa.PrivateKey
Filters object.SearchFilters
} }
// MarkLocal tells the server to execute the operation locally. // MarkLocal tells the server to execute the operation locally.
//
// Deprecated: Use PrmObjectSearch.Local instead.
func (x *PrmObjectSearch) MarkLocal() { func (x *PrmObjectSearch) MarkLocal() {
x.Local = true x.meta.SetTTL(1)
} }
// WithinSession specifies session within which the search query must be executed. // WithinSession specifies session within which the search query must be executed.
@ -52,10 +45,10 @@ func (x *PrmObjectSearch) MarkLocal() {
// This may affect the execution of an operation (e.g. access control). // This may affect the execution of an operation (e.g. access control).
// //
// Must be signed. // Must be signed.
//
// Deprecated: Use PrmObjectSearch.Session instead.
func (x *PrmObjectSearch) WithinSession(t session.Object) { func (x *PrmObjectSearch) WithinSession(t session.Object) {
x.Session = &t var tokv2 v2session.Token
t.WriteToV2(&tokv2)
x.meta.SetSessionToken(&tokv2)
} }
// WithBearerToken attaches bearer token to be used for the operation. // WithBearerToken attaches bearer token to be used for the operation.
@ -63,44 +56,37 @@ func (x *PrmObjectSearch) WithinSession(t session.Object) {
// If set, underlying eACL rules will be used in access control. // If set, underlying eACL rules will be used in access control.
// //
// Must be signed. // Must be signed.
//
// Deprecated: Use PrmObjectSearch.BearerToken instead.
func (x *PrmObjectSearch) WithBearerToken(t bearer.Token) { func (x *PrmObjectSearch) WithBearerToken(t bearer.Token) {
x.BearerToken = &t var v2token acl.BearerToken
t.WriteToV2(&v2token)
x.meta.SetBearerToken(&v2token)
} }
// WithXHeaders specifies list of extended headers (string key-value pairs) // WithXHeaders specifies list of extended headers (string key-value pairs)
// to be attached to the request. Must have an even length. // to be attached to the request. Must have an even length.
// //
// Slice must not be mutated until the operation completes. // Slice must not be mutated until the operation completes.
//
// Deprecated: Use PrmObjectSearch.XHeaders instead.
func (x *PrmObjectSearch) WithXHeaders(hs ...string) { func (x *PrmObjectSearch) WithXHeaders(hs ...string) {
x.XHeaders = hs writeXHeadersToMeta(hs, &x.meta)
} }
// UseKey specifies private key to sign the requests. // UseKey specifies private key to sign the requests.
// If key is not provided, then Client default key is used. // If key is not provided, then Client default key is used.
//
// Deprecated: Use PrmObjectSearch.Key instead.
func (x *PrmObjectSearch) UseKey(key ecdsa.PrivateKey) { func (x *PrmObjectSearch) UseKey(key ecdsa.PrivateKey) {
x.Key = &key x.key = &key
} }
// InContainer specifies the container in which to look for objects. // InContainer specifies the container in which to look for objects.
// Required parameter. // Required parameter.
//
// Deprecated: Use PrmObjectSearch.ContainerID instead.
func (x *PrmObjectSearch) InContainer(id cid.ID) { func (x *PrmObjectSearch) InContainer(id cid.ID) {
x.ContainerID = &id x.cnrID = id
x.cnrSet = true
} }
// SetFilters sets filters by which to select objects. All container objects // SetFilters sets filters by which to select objects. All container objects
// match unset/empty filters. // match unset/empty filters.
//
// Deprecated: Use PrmObjectSearch.Filters instead.
func (x *PrmObjectSearch) SetFilters(filters object.SearchFilters) { func (x *PrmObjectSearch) SetFilters(filters object.SearchFilters) {
x.Filters = filters x.filters = filters
} }
// ResObjectSearch groups the final result values of ObjectSearch operation. // ResObjectSearch groups the final result values of ObjectSearch operation.
@ -226,48 +212,6 @@ func (x *ObjectListReader) Close() (*ResObjectSearch, error) {
return &x.res, nil return &x.res, nil
} }
func (x *PrmObjectSearch) buildRequest(c *Client) (*v2object.SearchRequest, error) {
if x.ContainerID == nil {
return nil, errorMissingContainer
}
if len(x.XHeaders)%2 != 0 {
return nil, errorInvalidXHeaders
}
meta := new(v2session.RequestMetaHeader)
writeXHeadersToMeta(x.XHeaders, meta)
if x.BearerToken != nil {
v2BearerToken := new(acl.BearerToken)
x.BearerToken.WriteToV2(v2BearerToken)
meta.SetBearerToken(v2BearerToken)
}
if x.Session != nil {
v2SessionToken := new(v2session.Token)
x.Session.WriteToV2(v2SessionToken)
meta.SetSessionToken(v2SessionToken)
}
if x.Local {
meta.SetTTL(1)
}
cnrV2 := new(v2refs.ContainerID)
x.ContainerID.WriteToV2(cnrV2)
body := new(v2object.SearchRequestBody)
body.SetVersion(1)
body.SetContainerID(cnrV2)
body.SetFilters(x.Filters.ToV2())
req := new(v2object.SearchRequest)
req.SetBody(body)
c.prepareRequest(req, meta)
return req, nil
}
// ObjectSearchInit initiates object selection through a remote server using FrostFS API protocol. // ObjectSearchInit initiates object selection through a remote server using FrostFS API protocol.
// //
// The call only opens the transmission channel, explicit fetching of matched objects // The call only opens the transmission channel, explicit fetching of matched objects
@ -277,17 +221,30 @@ func (x *PrmObjectSearch) buildRequest(c *Client) (*v2object.SearchRequest, erro
// Returns an error if parameters are set incorrectly (see PrmObjectSearch docs). // Returns an error if parameters are set incorrectly (see PrmObjectSearch docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*ObjectListReader, error) { func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*ObjectListReader, error) {
req, err := prm.buildRequest(c) // check parameters
if err != nil { if !prm.cnrSet {
return nil, err return nil, errorMissingContainer
} }
key := prm.Key var cidV2 v2refs.ContainerID
prm.cnrID.WriteToV2(&cidV2)
var body v2object.SearchRequestBody
body.SetVersion(1)
body.SetContainerID(&cidV2)
body.SetFilters(prm.filters.ToV2())
// init reader
var req v2object.SearchRequest
req.SetBody(&body)
c.prepareRequest(&req, &prm.meta)
key := prm.key
if key == nil { if key == nil {
key = &c.prm.Key key = &c.prm.key
} }
err = signature.SignServiceMessage(key, req) err := signature.SignServiceMessage(key, &req)
if err != nil { if err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }
@ -295,7 +252,7 @@ func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*Ob
var r ObjectListReader var r ObjectListReader
ctx, r.cancelCtxStream = context.WithCancel(ctx) ctx, r.cancelCtxStream = context.WithCancel(ctx)
r.stream, err = rpcapi.SearchObjects(&c.c, req, client.WithContext(ctx)) r.stream, err = rpcapi.SearchObjects(&c.c, &req, client.WithContext(ctx))
if err != nil { if err != nil {
return nil, fmt.Errorf("open stream: %w", err) return nil, fmt.Errorf("open stream: %w", err)
} }

View file

@ -16,32 +16,30 @@ import (
// PrmSessionCreate groups parameters of SessionCreate operation. // PrmSessionCreate groups parameters of SessionCreate operation.
type PrmSessionCreate struct { type PrmSessionCreate struct {
XHeaders []string prmCommonMeta
Expiration uint64 exp uint64
Key *ecdsa.PrivateKey keySet bool
key ecdsa.PrivateKey
} }
// SetExp sets number of the last NepFS epoch in the lifetime of the session after which it will be expired. // SetExp sets number of the last NepFS epoch in the lifetime of the session after which it will be expired.
//
// Deprecated: Use PrmSessionCreate.Expiration instead.
func (x *PrmSessionCreate) SetExp(exp uint64) { func (x *PrmSessionCreate) SetExp(exp uint64) {
x.Expiration = exp x.exp = exp
} }
// UseKey specifies private key to sign the requests and compute token owner. // UseKey specifies private key to sign the requests and compute token owner.
// If key is not provided, then Client default key is used. // If key is not provided, then Client default key is used.
//
// Deprecated: Use PrmSessionCreate.Key instead.
func (x *PrmSessionCreate) UseKey(key ecdsa.PrivateKey) { func (x *PrmSessionCreate) UseKey(key ecdsa.PrivateKey) {
x.Key = &key x.keySet = true
x.key = key
} }
func (x *PrmSessionCreate) buildRequest(c *Client) (*v2session.CreateRequest, error) { func (x *PrmSessionCreate) buildRequest(c *Client) (*v2session.CreateRequest, error) {
ownerKey := c.prm.Key.PublicKey ownerKey := c.prm.key.PublicKey
if x.Key != nil { if x.keySet {
ownerKey = x.Key.PublicKey ownerKey = x.key.PublicKey
} }
var ownerID user.ID var ownerID user.ID
user.IDFromKey(&ownerID, ownerKey) user.IDFromKey(&ownerID, ownerKey)
@ -51,10 +49,10 @@ func (x *PrmSessionCreate) buildRequest(c *Client) (*v2session.CreateRequest, er
reqBody := new(v2session.CreateRequestBody) reqBody := new(v2session.CreateRequestBody)
reqBody.SetOwnerID(&ownerIDV2) reqBody.SetOwnerID(&ownerIDV2)
reqBody.SetExpiration(x.Expiration) reqBody.SetExpiration(x.exp)
var meta v2session.RequestMetaHeader var meta v2session.RequestMetaHeader
writeXHeadersToMeta(x.XHeaders, &meta) writeXHeadersToMeta(x.xHeaders, &meta)
var req v2session.CreateRequest var req v2session.CreateRequest
req.SetBody(reqBody) req.SetBody(reqBody)
@ -89,9 +87,9 @@ func (x ResSessionCreate) PublicKey() []byte {
// //
// Exactly one return value is non-nil. By default, server status is returned in res structure. // Exactly one return value is non-nil. By default, server status is returned in res structure.
// Any client's internal or transport errors are returned as `error`. // Any client's internal or transport errors are returned as `error`.
// If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful // If PrmInit.ResolveFrostFSFailures has been called, unsuccessful
// FrostFS status codes are included in the returned result structure, // FrostFS status codes are returned as `error`, otherwise, are included
// otherwise, are also returned as `error`. // in the returned result structure.
// //
// Returns an error if parameters are set incorrectly (see PrmSessionCreate docs). // Returns an error if parameters are set incorrectly (see PrmSessionCreate docs).
// Context is required and must not be nil. It is used for network communication. // Context is required and must not be nil. It is used for network communication.
@ -104,7 +102,7 @@ func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResS
return nil, err return nil, err
} }
if err := signature.SignServiceMessage(&c.prm.Key, req); err != nil { if err := signature.SignServiceMessage(&c.prm.key, req); err != nil {
return nil, fmt.Errorf("sign request: %w", err) return nil, fmt.Errorf("sign request: %w", err)
} }

View file

@ -1,53 +0,0 @@
package apistatus
import (
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status"
)
// APEManagerAccessDenied describes status of the failure because of the access control violation.
// Instances provide Status and StatusV2 interfaces.
type APEManagerAccessDenied struct {
v2 status.Status
}
const defaultAPEManagerAccessDeniedMsg = "apemanager access denied"
func (x *APEManagerAccessDenied) Error() string {
msg := x.v2.Message()
if msg == "" {
msg = defaultAPEManagerAccessDeniedMsg
}
return errMessageStatusV2(
globalizeCodeV2(apemanager.StatusAPEManagerAccessDenied, apemanager.GlobalizeFail),
msg,
)
}
func (x *APEManagerAccessDenied) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ToStatusV2 converts APEManagerAccessDenied to v2's Status.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: APE_MANAGER_ACCESS_DENIED;
// - string message: "apemanager access denied";
// - details: empty.
func (x APEManagerAccessDenied) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(apemanager.StatusAPEManagerAccessDenied, apemanager.GlobalizeFail))
x.v2.SetMessage(defaultAPEManagerAccessDeniedMsg)
return &x.v2
}
// WriteReason writes human-readable access rejection reason.
func (x *APEManagerAccessDenied) WriteReason(reason string) {
apemanager.WriteAccessDeniedDesc(&x.v2, reason)
}
// Reason returns human-readable access rejection reason returned by the server.
// Returns empty value is reason is not presented.
func (x APEManagerAccessDenied) Reason() string {
return apemanager.ReadAccessDeniedDesc(x.v2)
}

View file

@ -3,7 +3,6 @@ package apistatus
import ( import (
"fmt" "fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
@ -93,12 +92,6 @@ func FromStatusV2(st *status.Status) Status {
case session.StatusTokenExpired: case session.StatusTokenExpired:
decoder = new(SessionTokenExpired) decoder = new(SessionTokenExpired)
} }
case apemanager.LocalizeFailStatus(&code):
//nolint:exhaustive
switch code {
case apemanager.StatusAPEManagerAccessDenied:
decoder = new(APEManagerAccessDenied)
}
} }
if decoder == nil { if decoder == nil {

View file

@ -125,12 +125,6 @@ func TestToStatusV2(t *testing.T) {
}), }),
codeV2: 4097, codeV2: 4097,
}, },
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.APEManagerAccessDenied)
}),
codeV2: 5120,
},
{ {
status: (statusConstructor)(func() apistatus.Status { status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.NodeUnderMaintenance) return new(apistatus.NodeUnderMaintenance)
@ -284,12 +278,6 @@ func TestFromStatusV2(t *testing.T) {
}), }),
codeV2: 4097, codeV2: 4097,
}, },
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.APEManagerAccessDenied)
}),
codeV2: 5120,
},
{ {
status: (statusConstructor)(func() apistatus.Status { status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.NodeUnderMaintenance) return new(apistatus.NodeUnderMaintenance)

View file

@ -308,7 +308,7 @@ func (x *Container) SetAttribute(key, value string) {
attrs := x.v2.GetAttributes() attrs := x.v2.GetAttributes()
ln := len(attrs) ln := len(attrs)
for i := range ln { for i := 0; i < ln; i++ {
if attrs[i].GetKey() == key { if attrs[i].GetKey() == key {
attrs[i].SetValue(value) attrs[i].SetValue(value)
return return
@ -355,8 +355,9 @@ func (x Container) IterateAttributes(f func(key, val string)) {
func (x Container) IterateUserAttributes(f func(key, val string)) { func (x Container) IterateUserAttributes(f func(key, val string)) {
attrs := x.v2.GetAttributes() attrs := x.v2.GetAttributes()
for _, attr := range attrs { for _, attr := range attrs {
key := attr.GetKey() var key = attr.GetKey()
if !strings.HasPrefix(key, container.SysAttributePrefix) { if !strings.HasPrefix(key, container.SysAttributePrefix) &&
!strings.HasPrefix(key, container.SysAttributePrefixNeoFS) {
f(key, attr.GetValue()) f(key, attr.GetValue())
} }
} }
@ -416,7 +417,8 @@ func DisableHomomorphicHashing(cnr *Container) {
// //
// Zero Container has enabled hashing. // Zero Container has enabled hashing.
func IsHomomorphicHashingDisabled(cnr Container) bool { func IsHomomorphicHashingDisabled(cnr Container) bool {
return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled ||
cnr.Attribute(container.SysAttributeHomomorphicHashingNeoFS) == attributeHomoHashEnabled
} }
// Domain represents information about container domain registered in the NNS // Domain represents information about container domain registered in the NNS
@ -465,6 +467,9 @@ func ReadDomain(cnr Container) (res Domain) {
if name := cnr.Attribute(container.SysAttributeName); name != "" { if name := cnr.Attribute(container.SysAttributeName); name != "" {
res.SetName(name) res.SetName(name)
res.SetZone(cnr.Attribute(container.SysAttributeZone)) res.SetZone(cnr.Attribute(container.SysAttributeZone))
} else if name = cnr.Attribute(container.SysAttributeNameNeoFS); name != "" {
res.SetName(name)
res.SetZone(cnr.Attribute(container.SysAttributeZoneNeoFS))
} }
return return

View file

@ -78,7 +78,7 @@ func TestContainer_Owner(t *testing.T) {
val = containertest.Container() val = containertest.Container()
owner := usertest.ID() owner := *usertest.ID()
val.SetOwner(owner) val.SetOwner(owner)
@ -150,7 +150,7 @@ func assertContainsAttribute(t *testing.T, m v2container.Container, key, val str
} }
func TestContainer_Attribute(t *testing.T) { func TestContainer_Attribute(t *testing.T) {
const attrKey1, attrKey2 = v2container.SysAttributePrefix + "key1", v2container.SysAttributePrefix + "key2" const attrKey1, attrKey2 = v2container.SysAttributePrefix + "key1", v2container.SysAttributePrefixNeoFS + "key2"
const attrVal1, attrVal2 = "val1", "val2" const attrVal1, attrVal2 = "val1", "val2"
val := containertest.Container() val := containertest.Container()

View file

@ -1,8 +1,8 @@
package cid_test package cid_test
import ( import (
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"math/rand"
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"

View file

@ -1,8 +1,8 @@
package cidtest package cidtest
import ( import (
"crypto/rand"
"crypto/sha256" "crypto/sha256"
"math/rand"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
) )
@ -11,7 +11,7 @@ import (
func ID() cid.ID { func ID() cid.ID {
checksum := [sha256.Size]byte{} checksum := [sha256.Size]byte{}
_, _ = rand.Read(checksum[:]) rand.Read(checksum[:])
return IDWithChecksum(checksum) return IDWithChecksum(checksum)
} }

104
container/size.go Normal file
View file

@ -0,0 +1,104 @@
package container
import (
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
)
// SizeEstimation groups information about estimation of the size of the data
// stored in the FrostFS container.
//
// SizeEstimation is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container.UsedSpaceAnnouncement
// message. See ReadFromV2 / WriteToV2 methods.
type SizeEstimation struct {
m container.UsedSpaceAnnouncement
}
// ReadFromV2 reads SizeEstimation from the container.UsedSpaceAnnouncement message.
// Checks if the message conforms to FrostFS API V2 protocol.
//
// See also WriteToV2.
func (x *SizeEstimation) ReadFromV2(m container.UsedSpaceAnnouncement) error {
cnrV2 := m.GetContainerID()
if cnrV2 == nil {
return errors.New("missing container")
}
var cnr cid.ID
err := cnr.ReadFromV2(*cnrV2)
if err != nil {
return fmt.Errorf("invalid container: %w", err)
}
x.m = m
return nil
}
// WriteToV2 writes SizeEstimation into the container.UsedSpaceAnnouncement message.
// The message MUST NOT be nil.
//
// See also ReadFromV2.
func (x SizeEstimation) WriteToV2(m *container.UsedSpaceAnnouncement) {
*m = x.m
}
// SetEpoch sets epoch when estimation of the container data size was calculated.
//
// See also Epoch.
func (x *SizeEstimation) SetEpoch(epoch uint64) {
x.m.SetEpoch(epoch)
}
// Epoch return epoch set using SetEpoch.
//
// Zero SizeEstimation represents estimation in zero epoch.
func (x SizeEstimation) Epoch() uint64 {
return x.m.GetEpoch()
}
// SetContainer specifies the container for which the amount of data is estimated.
// Required by the FrostFS API protocol.
//
// See also Container.
func (x *SizeEstimation) SetContainer(cnr cid.ID) {
var cidV2 refs.ContainerID
cnr.WriteToV2(&cidV2)
x.m.SetContainerID(&cidV2)
}
// Container returns container set using SetContainer.
//
// Zero SizeEstimation is not bound to any container (returns zero) which is
// incorrect according to FrostFS API protocol.
func (x SizeEstimation) Container() (res cid.ID) {
m := x.m.GetContainerID()
if m != nil {
err := res.ReadFromV2(*m)
if err != nil {
panic(fmt.Errorf("unexpected error from cid.ID.ReadFromV2: %w", err))
}
}
return
}
// SetValue sets estimated amount of data (in bytes) in the specified container.
//
// See also Value.
func (x *SizeEstimation) SetValue(value uint64) {
x.m.SetUsedSpace(value)
}
// Value returns data size estimation set using SetValue.
//
// Zero SizeEstimation has zero value.
func (x SizeEstimation) Value() uint64 {
return x.m.GetUsedSpace()
}

94
container/size_test.go Normal file
View file

@ -0,0 +1,94 @@
package container_test
import (
"crypto/sha256"
"testing"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"github.com/stretchr/testify/require"
)
func TestSizeEstimation_Epoch(t *testing.T) {
var val container.SizeEstimation
require.Zero(t, val.Epoch())
const epoch = 123
val.SetEpoch(epoch)
require.EqualValues(t, epoch, val.Epoch())
var msg v2container.UsedSpaceAnnouncement
val.WriteToV2(&msg)
require.EqualValues(t, epoch, msg.GetEpoch())
}
func TestSizeEstimation_Container(t *testing.T) {
var val container.SizeEstimation
require.Zero(t, val.Container())
cnr := cidtest.ID()
val.SetContainer(cnr)
require.True(t, val.Container().Equals(cnr))
var msg v2container.UsedSpaceAnnouncement
val.WriteToV2(&msg)
var msgCnr refs.ContainerID
cnr.WriteToV2(&msgCnr)
require.Equal(t, &msgCnr, msg.GetContainerID())
}
func TestSizeEstimation_Value(t *testing.T) {
var val container.SizeEstimation
require.Zero(t, val.Value())
const value = 876
val.SetValue(value)
require.EqualValues(t, value, val.Value())
var msg v2container.UsedSpaceAnnouncement
val.WriteToV2(&msg)
require.EqualValues(t, value, msg.GetUsedSpace())
}
func TestSizeEstimation_ReadFromV2(t *testing.T) {
const epoch = 654
const value = 903
var cnrMsg refs.ContainerID
var msg v2container.UsedSpaceAnnouncement
var val container.SizeEstimation
require.Error(t, val.ReadFromV2(msg))
msg.SetContainerID(&cnrMsg)
require.Error(t, val.ReadFromV2(msg))
cnrMsg.SetValue(make([]byte, sha256.Size))
var cnr cid.ID
require.NoError(t, cnr.ReadFromV2(cnrMsg))
msg.SetEpoch(epoch)
msg.SetUsedSpace(value)
require.NoError(t, val.ReadFromV2(msg))
require.EqualValues(t, epoch, val.Epoch())
require.EqualValues(t, value, val.Value())
require.EqualValues(t, cnr, val.Container())
}

View file

@ -5,6 +5,7 @@ import (
"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" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test" netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test" usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
) )
@ -15,13 +16,22 @@ func Container() (x container.Container) {
x.Init() x.Init()
x.SetAttribute("some attribute", "value") x.SetAttribute("some attribute", "value")
x.SetOwner(owner) x.SetOwner(*owner)
x.SetBasicACL(BasicACL()) x.SetBasicACL(BasicACL())
x.SetPlacementPolicy(netmaptest.PlacementPolicy()) x.SetPlacementPolicy(netmaptest.PlacementPolicy())
return x return x
} }
// SizeEstimation returns random container.SizeEstimation.
func SizeEstimation() (x container.SizeEstimation) {
x.SetContainer(cidtest.ID())
x.SetEpoch(rand.Uint64())
x.SetValue(rand.Uint64())
return x
}
// BasicACL returns random acl.Basic. // BasicACL returns random acl.Basic.
func BasicACL() (x acl.Basic) { func BasicACL() (x acl.Basic) {
x.FromBits(rand.Uint32()) x.FromBits(rand.Uint32())

View file

@ -1,7 +1,7 @@
package frostfscrypto_test package frostfscrypto_test
import ( import (
"crypto/rand" "math/rand"
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -101,16 +101,16 @@ In a nutshell, a `SELECT` takes a filter result as input and outputs a specific
Let's see some examples Let's see some examples
```sql ```sql
-- Selects exactly one node from the entire netmap -- Selects exactly one node from the entire netmap
SELECT 1 FROM * SELECT 1 FROM *
-- Same as above, but with an identifier for the selection -- Same as above, but with an identifier for the selection
SELECT 1 FROM * AS ONE SELECT 1 FROM * AS ONE
-- Selects two nodes from the RedOrBlueNodes filter, such that both selected nodes -- Selects two nodes from the RedOrBlueNodes filter, such that both selected nodes
-- share the same value for the Color attribute, i.e. both red or both blue. -- share the same value for the Color attribute, i.e. both red or both blue.
SELECT 2 IN SAME Color FROM RedOrBlueNodes SELECT 2 IN SAME Color FROM RedOrBlueNodes
-- Selects two nodes from the RedOrBlueNodes filter, such that the selected nodes -- Selects two nodes from the RedOrBlueNodes filter, such that the selected nodes
-- have distinct values for the Color attribute, i.e. one red and one blue. -- have distinct values for the Color attribute, i.e. one red and one blue.
-- The selection is also given an identifier. -- The selection is also given an identifier.
SELECT 2 IN DISTINCT Color FROM RedOrBlueNodes AS MyNodes SELECT 2 IN DISTINCT Color FROM RedOrBlueNodes AS MyNodes
@ -131,8 +131,7 @@ Its basic syntax is as follows:
REP <count> {IN <select>} REP <count> {IN <select>}
``` ```
If a select is not specified, then the entire netmap is used as input. The only exception to this rule is when exactly 1 replica and 1 selector are being present: in this case the only selector is being used instead of the whole netmap. If a select is not specified, then the entire netmap is used as input. The resulting nodes will be used to actually store objects and they constitute a replica group (or simply, "a replica").
The resulting nodes will be used to actually store objects and they constitute a replica group (or simply, "a replica").
Examples Examples
```sql ```sql
@ -174,18 +173,18 @@ In additional to this basic syntax, there are a couple of additional useful opti
### The policy playground ### The policy playground
> This section assumes you have an up-to-date version of the `frostfs-cli`. > This section assumes you have an up-to-date version of the `frostfs-cli`.
While simple placement policies have predictable results that can be understood at a glance, more complex ones need careful consideration before deployment. In order to simplify understanding a policy's outcome and experimenting while learning, a builtin tool is provided as part of the `frostfs-cli` for this purpose: the policy playground. While simple placement policies have predictable results that can be understood at a glance, more complex ones need careful consideration before deployment. In order to simplify understanding a policy's outcome and experimenting while learning, a builtin tool is provided as part of the `frostfs-cli` for this purpose: the policy playground.
For the remainder of this guide, we will use the policy playground to setup a virtual netmap (that is, one that doesn't require any networking or deployment) and test various policies. In order to visualize this netmap easily, each node will have three attributes: a character, a shape and a color For the remainder of this guide, we will use the policy playground to setup a virtual netmap (that is, one that doesn't require any networking or deployment) and test various policies. In order to visualize this netmap easily, each node will have three attributes: a character, a shape and a color
![Sample Netmap](./image/sample_netmap.svg) ![Sample Netmap](./image/sample_netmap.svg)
We can start the policy playground as follows: We can start the policy playground as follows:
```sh ```sh
$ frostfs-cli container policy-playground $ frostfs-cli container policy-playground
> >
``` ```
Since we didn't pass any endpoint, the initial netmap is empty, which we can verify with the `ls` command (to list the nodes in the netmap): Since we didn't pass any endpoint, the initial netmap is empty, which we can verify with the `ls` command (to list the nodes in the netmap):
@ -401,7 +400,7 @@ FILTER Color EQ 'Green' AS GreenNodes
#### Example #6 #### Example #6
```sql ```sql
REP 1 IN MyNodes REP 1 IN MyNodes
REP 2 REP 2
CBF 2 CBF 2
SELECT 1 FROM CuteNodes AS MyNodes SELECT 1 FROM CuteNodes AS MyNodes
FILTER (Color EQ 'Blue') AND NOT (Shape EQ 'Circle' OR Shape EQ 'Square') AS CuteNodes FILTER (Color EQ 'Blue') AND NOT (Shape EQ 'Circle' OR Shape EQ 'Square') AS CuteNodes
@ -428,13 +427,6 @@ Comparison operators (all binary):
- `LE`: less or equal - `LE`: less or equal
- `LT`: less than - `LT`: less than
Pattern operator:
- `LIKE`: specifies pattern for an attribute. Uses as a wildcard symbol `*`.
- `... ATTR LIKE "VAL"` - the behaviour is equal to `EQ`
- `... ATTR LIKE "VAL*"` - matches all which starts with `VAL`
- `... ATTR LIKE "*VAL"` - matches all which ends with `VAL`
- `... ATTR LIKE "*VAL*"` - matches all which contains `VAL`
Logical operators: Logical operators:
- `NOT`: negation (unary) - `NOT`: negation (unary)
- `AND`: conjunction (binary) - `AND`: conjunction (binary)
@ -450,4 +442,4 @@ Others:
- `ls`: list nodes in the current netmap and their attributes - `ls`: list nodes in the current netmap and their attributes
- `add`: add a node to the current netmap. If it already exists, it will be overwritten. - `add`: add a node to the current netmap. If it already exists, it will be overwritten.
- `remove`: remove a node from the current netmap. - `remove`: remove a node from the current netmap.
- `eval`: evaluate a placement policy on the current netmap. - `eval`: evaluate a placement policy on the current netmap.

View file

@ -1,27 +0,0 @@
# Node connection pool
* Distributes requests between fixed number of nodes
* Wraps
The distribution between nodes in connection pool is based on priority and weight parameters from
NodeParam struct. The distribution model is presented below. On this scheme nodes with the same
priority have the same color.
![](./image/pool.png "Pool connections distribution model")
## Priority
Pool component forwards request to the nodes with the highest priority (the lower the value -
the higher the priority). The `node 1` from the image's scenario (I) is healthy
and has the highest priority (1), that's why the pool forwards requests from it. There are no other
nodes with priority 1, so `node 1` receives all requests. In the second scenario (II) `node 1`
becomes unhealthy. In that case pool tries to connect nodes with next in priority nodes e.g.
`Node 4` and `node 2`. If all of them become unhealthy too, the pool sends requests to nodes with
priority 3 in scenario (III) and so on.
## Weights
If there are several nodes with the same priority, then requests are distributed randomly between
these nodes based on their weights. To do that the proportion of weights is calculated.
For example, for `node 2` and `node 4` with weights 2 and 8 the distribution would be 20 and 80 percent
respectively.

View file

@ -125,7 +125,7 @@ func (r *Record) AddObjectContainerIDFilter(m Match, id cid.ID) {
} }
// AddObjectOwnerIDFilter adds filter by object owner ID. // AddObjectOwnerIDFilter adds filter by object owner ID.
func (r *Record) AddObjectOwnerIDFilter(m Match, id user.ID) { func (r *Record) AddObjectOwnerIDFilter(m Match, id *user.ID) {
r.addObjectReservedFilter(m, fKeyObjOwnerID, id) r.addObjectReservedFilter(m, fKeyObjOwnerID, id)
} }
@ -286,13 +286,13 @@ func equalRecords(r1, r2 Record) bool {
return false return false
} }
for i := range len(fs1) { for i := 0; i < len(fs1); i++ {
if !equalFilters(fs1[i], fs2[i]) { if !equalFilters(fs1[i], fs2[i]) {
return false return false
} }
} }
for i := range len(ts1) { for i := 0; i < len(ts1); i++ {
if !equalTargets(ts1[i], ts2[i]) { if !equalTargets(ts1[i], ts2[i]) {
return false return false
} }

View file

@ -225,6 +225,14 @@ func TestReservedRecords(t *testing.T) {
key: v2acl.FilterObjectType, key: v2acl.FilterObjectType,
value: "TOMBSTONE", value: "TOMBSTONE",
}, },
{
f: func(r *Record) {
require.True(t, typ.FromString("STORAGE_GROUP"))
r.AddObjectTypeFilter(MatchStringEqual, *typ)
},
key: v2acl.FilterObjectType,
value: "STORAGE_GROUP",
},
} }
for n, testCase := range testSuit { for n, testCase := range testSuit {

View file

@ -212,7 +212,7 @@ func EqualTables(t1, t2 Table) bool {
return false return false
} }
for i := range len(rs1) { for i := 0; i < len(rs1); i++ {
if !equalRecords(rs1[i], rs2[i]) { if !equalRecords(rs1[i], rs2[i]) {
return false return false
} }

View file

@ -51,7 +51,7 @@ func SetTargetECDSAKeys(t *Target, pubs ...*ecdsa.PublicKey) {
binKeys = make([][]byte, 0, ln) binKeys = make([][]byte, 0, ln)
} }
for i := range ln { for i := 0; i < ln; i++ {
binKeys = append(binKeys, (*keys.PublicKey)(pubs[i]).Bytes()) binKeys = append(binKeys, (*keys.PublicKey)(pubs[i]).Bytes())
} }
@ -67,7 +67,7 @@ func TargetECDSAKeys(t *Target) []*ecdsa.PublicKey {
pubs := make([]*ecdsa.PublicKey, ln) pubs := make([]*ecdsa.PublicKey, ln)
for i := range ln { for i := 0; i < ln; i++ {
p := new(keys.PublicKey) p := new(keys.PublicKey)
if p.DecodeBytes(binKeys[i]) == nil { if p.DecodeBytes(binKeys[i]) == nil {
pubs[i] = (*ecdsa.PublicKey)(p) pubs[i] = (*ecdsa.PublicKey)(p)
@ -169,7 +169,7 @@ func equalTargets(t1, t2 Target) bool {
return false return false
} }
for i := range len(keys1) { for i := 0; i < len(keys1); i++ {
if !bytes.Equal(keys1[i], keys2[i]) { if !bytes.Equal(keys1[i], keys2[i]) {
return false return false
} }

View file

@ -2,7 +2,7 @@ package eacltest
import ( import (
"bytes" "bytes"
"crypto/rand" "math/rand"
"testing" "testing"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
@ -19,7 +19,7 @@ func baseBenchmarkTableBinaryComparison(b *testing.B, factor int) {
b.StopTimer() b.StopTimer()
b.ResetTimer() b.ResetTimer()
b.StartTimer() b.StartTimer()
for range b.N { for i := 0; i < b.N; i++ {
got, _ := t.Marshal() got, _ := t.Marshal()
if !bytes.Equal(exp, got) { if !bytes.Equal(exp, got) {
b.Fail() b.Fail()
@ -38,7 +38,7 @@ func baseBenchmarkTableEqualsComparison(b *testing.B, factor int) {
b.StopTimer() b.StopTimer()
b.ResetTimer() b.ResetTimer()
b.StartTimer() b.StartTimer()
for range b.N { for i := 0; i < b.N; i++ {
if !eacl.EqualTables(*t, *t2) { if !eacl.EqualTables(*t, *t2) {
b.Fail() b.Fail()
} }
@ -76,7 +76,7 @@ func TargetN(n int) *eacl.Target {
x.SetRole(eacl.RoleSystem) x.SetRole(eacl.RoleSystem)
keys := make([][]byte, n) keys := make([][]byte, n)
for i := range n { for i := 0; i < n; i++ {
keys[i] = make([]byte, 32) keys[i] = make([]byte, 32)
rand.Read(keys[i]) rand.Read(keys[i])
} }
@ -94,7 +94,7 @@ func RecordN(n int) *eacl.Record {
x.SetOperation(eacl.OperationRangeHash) x.SetOperation(eacl.OperationRangeHash)
x.SetTargets(*TargetN(n)) x.SetTargets(*TargetN(n))
for range n { for i := 0; i < n; i++ {
x.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, "", cidtest.ID().EncodeToString()) x.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, "", cidtest.ID().EncodeToString())
} }
@ -106,7 +106,7 @@ func TableN(n int) *eacl.Table {
x.SetCID(cidtest.ID()) x.SetCID(cidtest.ID())
for range n { for i := 0; i < n; i++ {
x.AddRecord(RecordN(n)) x.AddRecord(RecordN(n))
} }

View file

@ -7,7 +7,8 @@ import (
// Validator is a tool that calculates // Validator is a tool that calculates
// the action on a request according // the action on a request according
// to the extended ACL rule table. // to the extended ACL rule table.
type Validator struct{} type Validator struct {
}
// NewValidator creates and initializes a new Validator using options. // NewValidator creates and initializes a new Validator using options.
func NewValidator() *Validator { func NewValidator() *Validator {

View file

@ -1,7 +1,7 @@
package eacl package eacl
import ( import (
"crypto/rand" "math/rand"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

61
go.mod
View file

@ -1,49 +1,46 @@
module git.frostfs.info/TrueCloudLab/frostfs-sdk-go module git.frostfs.info/TrueCloudLab/frostfs-sdk-go
go 1.22 go 1.20
require ( require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
git.frostfs.info/TrueCloudLab/hrw v1.2.1 git.frostfs.info/TrueCloudLab/hrw v1.2.1
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 git.frostfs.info/TrueCloudLab/tzhash v1.8.0
github.com/antlr4-go/antlr/v4 v4.13.1 github.com/antlr4-go/antlr/v4 v4.13.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.3.0
github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/golang-lru/v2 v2.0.2
github.com/klauspost/reedsolomon v1.12.1
github.com/mr-tron/base58 v1.2.0 github.com/mr-tron/base58 v1.2.0
github.com/nspcc-dev/neo-go v0.106.2 github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.8.3
go.uber.org/zap v1.27.0 go.uber.org/zap v1.24.0
google.golang.org/grpc v1.66.2 google.golang.org/grpc v1.55.0
google.golang.org/protobuf v1.34.1 google.golang.org/protobuf v1.30.0
gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
github.com/VictoriaMetrics/easyproto v0.1.4 // indirect github.com/benbjohnson/clock v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/golang/snappy v0.0.1 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/gorilla/websocket v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce // indirect
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d // indirect
github.com/nspcc-dev/rfc6979 v0.2.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
github.com/twmb/murmur3 v1.1.8 // indirect github.com/twmb/murmur3 v1.1.8 // indirect
go.etcd.io/bbolt v1.3.9 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/goleak v1.2.1 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
golang.org/x/net v0.26.0 // indirect golang.org/x/net v0.10.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.21.0 // indirect golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

743
go.sum
View file

@ -1,7 +1,40 @@
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240916093537-13fa0da3741e h1:740ABnOBYx4o6jxULHdSSnVW2fYIO35ohg+Uz59sxd0= 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.20240916093537-13fa0da3741e/go.mod h1:F5GS7hRb62PUy5sTYDC4ajVdeffoAfjHSSHTKUJEaYU= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e h1:kcBqZBiFIUBATUqEuvVigtkJJWQ2Gug/eYXn967o3M4= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/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.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/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=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44 h1:v6JqBD/VzZx3QSxbaXnUwnnJ1KEYheU4LzLGr3IhsAE=
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.15.1-0.20230802075510-964c3edb3f44/go.mod h1:pKJJRLOChW4zDQsAt1e8k/snWKljJtpkiPfxV53ngjI=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb h1:S/TrbOOu9qEXZRZ9/Ddw7crnxbBUQLo68PSzQWYrc9M=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb/go.mod h1:nkR5gaGeez3Zv2SE7aceP0YwxG2FzIB5cGKpQO2vV2o=
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 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/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc= git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc=
@ -10,183 +43,691 @@ git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9m
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc= git.frostfs.info/TrueCloudLab/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=
github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I=
github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA=
github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs=
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
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.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.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 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/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/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
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.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU=
github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/klauspost/reedsolomon v1.12.1 h1:NhWgum1efX1x58daOBGCFWcxtEhOhXKKl1HAPQUp03Q= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/klauspost/reedsolomon v1.12.1/go.mod h1:nEi5Kjb6QqtbofI6s+cbG/j1da11c96IBYBSnVGtuBs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0 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/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nspcc-dev/neo-go v0.106.2 h1:KXSJ2J5Oacc7LrX3r4jvnC8ihKqHs5NB21q4f2S3r9o= github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw=
github.com/nspcc-dev/neo-go v0.106.2/go.mod h1:Ojwfx3/lv0VTeEHMpQ17g0wTnXcCSoFQVq5GEeCZmGo= github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k= github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM= github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y=
github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc= github.com/nspcc-dev/dbft v0.0.0-20220902113116-58a5e763e647/go.mod h1:g9xisXmX9NP9MjioaTe862n9SlZTrP+6PVUWLBYOr98=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg=
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU=
github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg=
github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM=
github.com/nspcc-dev/neo-go v0.99.4/go.mod h1:mKTolfRUfKjFso5HPvGSQtUZc70n0VKBMs16eGuC5gA=
github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc h1:fySIWvUQsitK5e5qYIHnTDCXuPpwzz89SEUEIyY11sg=
github.com/nspcc-dev/neo-go v0.101.2-0.20230601131642-a0117042e8fc/go.mod h1:s9QhjMC784MWqTURovMbyYduIJc86mnCruxcMiAebpc=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220927123257-24c107e3a262/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce h1:vLGuUNDkmQrWMa4rr4vTd1u8ULqejWxVmNz1L7ocTEI=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230615193820-9185820289ce/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg=
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs=
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40=
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
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.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/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.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.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 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
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/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8 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.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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/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.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190613194153-d28f0bde5980/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-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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-20190813064441-fde4db37ae7a/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-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/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-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.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.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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
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-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
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=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
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/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/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-20200513103714-09dca8ec2884/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-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-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.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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.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=

View file

@ -1,6 +1,8 @@
package netmap package netmap
import "slices" import (
"sort"
)
type ( type (
// aggregator can calculate some value across all netmap // aggregator can calculate some value across all netmap
@ -21,11 +23,11 @@ type (
} }
minAgg struct { minAgg struct {
min float64 min float64
minFound bool
} }
meanIQRAgg struct { meanIQRAgg struct {
k float64
arr []float64 arr []float64
} }
@ -71,10 +73,16 @@ func newMinAgg() aggregator {
return new(minAgg) return new(minAgg)
} }
// newMeanIQRAgg returns an aggregator which
// computes mean value of values from IQR interval.
func newMeanIQRAgg() aggregator {
return new(meanIQRAgg)
}
// newReverseMinNorm returns a normalizer which // newReverseMinNorm returns a normalizer which
// normalize values in range of 0.0 to 1.0 to a minimum value. // normalize values in range of 0.0 to 1.0 to a minimum value.
func newReverseMinNorm(minV float64) normalizer { func newReverseMinNorm(min float64) normalizer {
return &reverseMinNorm{min: minV} return &reverseMinNorm{min: min}
} }
// newSigmoidNorm returns a normalizer which // newSigmoidNorm returns a normalizer which
@ -94,13 +102,7 @@ func (a *meanAgg) Compute() float64 {
} }
func (a *minAgg) Add(n float64) { func (a *minAgg) Add(n float64) {
if !a.minFound { if a.min == 0 || n < a.min {
a.min = n
a.minFound = true
return
}
if n < a.min {
a.min = n a.min = n
} }
} }
@ -109,10 +111,6 @@ func (a *minAgg) Compute() float64 {
return a.min return a.min
} }
func (a *meanIQRAgg) clear() {
a.arr = a.arr[:0]
}
func (a *meanIQRAgg) Add(n float64) { func (a *meanIQRAgg) Add(n float64) {
a.arr = append(a.arr, n) a.arr = append(a.arr, n)
} }
@ -123,24 +121,25 @@ func (a *meanIQRAgg) Compute() float64 {
return 0 return 0
} }
slices.Sort(a.arr) sort.Slice(a.arr, func(i, j int) bool { return a.arr[i] < a.arr[j] })
var minV, maxV float64 var min, max float64
const minLn = 4 const minLn = 4
if l < minLn { if l < minLn {
minV, maxV = a.arr[0], a.arr[l-1] min, max = a.arr[0], a.arr[l-1]
} else { } else {
start, end := l/minLn, l*3/minLn-1 start, end := l/minLn, l*3/minLn-1
minV, maxV = a.arr[start], a.arr[end] iqr := a.k * (a.arr[end] - a.arr[start])
min, max = a.arr[start]-iqr, a.arr[end]+iqr
} }
count := 0 count := 0
sum := float64(0) sum := float64(0)
for _, e := range a.arr { for _, e := range a.arr {
if e >= minV && e <= maxV { if e >= min && e <= max {
sum += e sum += e
count++ count++
} }
@ -150,7 +149,11 @@ func (a *meanIQRAgg) Compute() float64 {
} }
func (r *reverseMinNorm) Normalize(w float64) float64 { func (r *reverseMinNorm) Normalize(w float64) float64 {
return (r.min + 1) / (w + 1) if w == 0 {
return 0
}
return r.min / w
} }
func (r *sigmoidNorm) Normalize(w float64) float64 { func (r *sigmoidNorm) Normalize(w float64) float64 {

View file

@ -1,44 +0,0 @@
package netmap
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestMinAgg(t *testing.T) {
tests := []struct {
vals []float64
res float64
}{
{
vals: []float64{1, 2, 3, 0, 10},
res: 0,
},
{
vals: []float64{10, 0, 10, 0},
res: 0,
},
{
vals: []float64{0, 1, 2, 3, 10},
res: 0,
},
{
vals: []float64{0, 0, 0, 0},
res: 0,
},
{
vals: []float64{10, 10, 10, 10},
res: 10,
},
}
for _, test := range tests {
a := newMinAgg()
for _, val := range test.vals {
a.Add(val)
}
require.Equal(t, test.res, a.Compute())
}
}

View file

@ -47,12 +47,13 @@ func BenchmarkNetmap_ContainerNodes(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for range b.N { for i := 0; i < b.N; i++ {
_, err := nm.ContainerNodes(p, pivot) _, err := nm.ContainerNodes(p, pivot)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
} }
}) })
} }
} }

View file

@ -40,10 +40,6 @@ type context struct {
// policy uses the UNIQUE flag. Nodes marked as used are not used in subsequent // policy uses the UNIQUE flag. Nodes marked as used are not used in subsequent
// base selections. // base selections.
usedNodes map[uint64]bool usedNodes map[uint64]bool
// If true, returns an error when netmap does not contain enough nodes for selection.
// By default best effort is taken.
strict bool
} }
// Various validation errors. // Various validation errors.
@ -94,14 +90,14 @@ func (c *context) addUsedNodes(ns ...NodeInfo) {
func defaultWeightFunc(ns nodes) weightFunc { func defaultWeightFunc(ns nodes) weightFunc {
mean := newMeanAgg() mean := newMeanAgg()
minV := newMinAgg() min := newMinAgg()
for i := range ns { for i := range ns {
mean.Add(float64(ns[i].capacity())) mean.Add(float64(ns[i].capacity()))
minV.Add(float64(ns[i].Price())) min.Add(float64(ns[i].Price()))
} }
return newWeightFunc( return newWeightFunc(
newSigmoidNorm(mean.Compute()), newSigmoidNorm(mean.Compute()),
newReverseMinNorm(minV.Compute())) newReverseMinNorm(min.Compute()))
} }

View file

@ -3,7 +3,6 @@ package netmap
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
) )
@ -12,8 +11,6 @@ import (
// which points to the whole netmap. // which points to the whole netmap.
const mainFilterName = "*" const mainFilterName = "*"
const likeWildcard = "*"
// processFilters processes filters and returns error is any of them is invalid. // processFilters processes filters and returns error is any of them is invalid.
func (c *context) processFilters(p PlacementPolicy) error { func (c *context) processFilters(p PlacementPolicy) error {
for i := range p.filters { for i := range p.filters {
@ -56,7 +53,7 @@ func (c *context) processFilter(f netmap.Filter, top bool) error {
} }
switch op { switch op {
case netmap.EQ, netmap.NE, netmap.LIKE: case netmap.EQ, netmap.NE:
case netmap.GT, netmap.GE, netmap.LT, netmap.LE: case netmap.GT, netmap.GE, netmap.LT, netmap.LE:
val := f.GetValue() val := f.GetValue()
n, err := strconv.ParseUint(val, 10, 64) n, err := strconv.ParseUint(val, 10, 64)
@ -113,19 +110,6 @@ func (c *context) matchKeyValue(f *netmap.Filter, b NodeInfo) bool {
switch op := f.GetOp(); op { switch op := f.GetOp(); op {
case netmap.EQ: case netmap.EQ:
return b.Attribute(f.GetKey()) == f.GetValue() return b.Attribute(f.GetKey()) == f.GetValue()
case netmap.LIKE:
str, prefix := strings.CutPrefix(f.GetValue(), likeWildcard)
str, suffix := strings.CutSuffix(str, likeWildcard)
if prefix && suffix {
return strings.Contains(b.Attribute(f.GetKey()), str)
}
if prefix && !suffix {
return strings.HasSuffix(b.Attribute(f.GetKey()), str)
}
if !prefix && suffix {
return strings.HasPrefix(b.Attribute(f.GetKey()), str)
}
return b.Attribute(f.GetKey()) == f.GetValue()
case netmap.NE: case netmap.NE:
return b.Attribute(f.GetKey()) != f.GetValue() return b.Attribute(f.GetKey()) != f.GetValue()
default: default:

View file

@ -1,49 +1,30 @@
package netmap package netmap
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
) )
// TestCase represents collection of placement policy tests for a single node set. // TestCase represents collection of placement policy tests for a single node set.
type TestCase struct { type TestCase struct {
Name string `json:"name" yaml:"name"` Name string `json:"name"`
Nodes []NodeInfo `json:"nodes" yaml:"nodes"` Nodes []NodeInfo `json:"nodes"`
Tests map[string]struct { Tests map[string]struct {
Policy PlacementPolicy Policy PlacementPolicy `json:"policy"`
Pivot Base64 Pivot []byte `json:"pivot,omitempty"`
Result [][]int `json:"result,omitempty" yaml:"result,omitempty"` Result [][]int `json:"result,omitempty"`
Error string `json:"error,omitempty" yaml:"error,omitempty"` Error string `json:"error,omitempty"`
Placement struct { Placement struct {
Pivot Base64 `json:"pivot" yaml:"pivot"` Pivot []byte
Result [][]int `json:"result,omitempty" yaml:"result,omitempty"` Result [][]int
} `json:"placement,omitempty" yaml:"placement,omitempty"` } `json:"placement,omitempty"`
} }
} }
// Base64 is a type that will hold the decoded Base64 data.
type Base64 []byte
func (b *Base64) UnmarshalYAML(unmarshal func(interface{}) error) error {
var base64Str string
if err := unmarshal(&base64Str); err != nil {
return err
}
decodedBytes, err := base64.StdEncoding.DecodeString(base64Str)
if err != nil {
return err
}
*b = decodedBytes
return nil
}
var _, _ json.Unmarshaler = new(NodeInfo), new(PlacementPolicy) var _, _ json.Unmarshaler = new(NodeInfo), new(PlacementPolicy)
func compareNodes(t testing.TB, expected [][]int, nodes nodes, actual [][]NodeInfo) { func compareNodes(t testing.TB, expected [][]int, nodes nodes, actual [][]NodeInfo) {
@ -57,7 +38,7 @@ func compareNodes(t testing.TB, expected [][]int, nodes nodes, actual [][]NodeIn
} }
func TestPlacementPolicy_Interopability(t *testing.T) { func TestPlacementPolicy_Interopability(t *testing.T) {
const testsDir = "./yml_tests" const testsDir = "./json_tests"
f, err := os.Open(testsDir) f, err := os.Open(testsDir)
require.NoError(t, err) require.NoError(t, err)
@ -66,17 +47,16 @@ func TestPlacementPolicy_Interopability(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
for i := range ds { for i := range ds {
filename := filepath.Join(testsDir, ds[i].Name()) bs, err := os.ReadFile(filepath.Join(testsDir, ds[i].Name()))
bs, err := os.ReadFile(filename)
require.NoError(t, err) require.NoError(t, err)
var tc TestCase var tc TestCase
require.NoError(t, yaml.Unmarshal(bs, &tc), "cannot unmarshal %s", ds[i].Name()) require.NoError(t, json.Unmarshal(bs, &tc), "cannot unmarshal %s", ds[i].Name())
srcNodes := make([]NodeInfo, len(tc.Nodes)) srcNodes := make([]NodeInfo, len(tc.Nodes))
copy(srcNodes, tc.Nodes) copy(srcNodes, tc.Nodes)
t.Run(fmt.Sprintf("%s:%s", filename, tc.Name), func(t *testing.T) { t.Run(tc.Name, func(t *testing.T) {
var nm NetMap var nm NetMap
nm.SetNodes(tc.Nodes) nm.SetNodes(tc.Nodes)
@ -106,7 +86,7 @@ func TestPlacementPolicy_Interopability(t *testing.T) {
} }
func BenchmarkPlacementPolicyInteropability(b *testing.B) { func BenchmarkPlacementPolicyInteropability(b *testing.B) {
const testsDir = "./yml_tests" const testsDir = "./json_tests"
f, err := os.Open(testsDir) f, err := os.Open(testsDir)
require.NoError(b, err) require.NoError(b, err)
@ -119,7 +99,7 @@ func BenchmarkPlacementPolicyInteropability(b *testing.B) {
require.NoError(b, err) require.NoError(b, err)
var tc TestCase var tc TestCase
require.NoError(b, yaml.Unmarshal(bs, &tc), "cannot unmarshal %s", ds[i].Name()) require.NoError(b, json.Unmarshal(bs, &tc), "cannot unmarshal %s", ds[i].Name())
b.Run(tc.Name, func(b *testing.B) { b.Run(tc.Name, func(b *testing.B) {
var nm NetMap var nm NetMap
@ -130,7 +110,7 @@ func BenchmarkPlacementPolicyInteropability(b *testing.B) {
b.Run(name, func(b *testing.B) { b.Run(name, func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
b.StartTimer() b.StartTimer()
v, err := nm.ContainerNodes(tt.Policy, tt.Pivot) v, err := nm.ContainerNodes(tt.Policy, tt.Pivot)
b.StopTimer() b.StopTimer()
@ -158,12 +138,12 @@ func BenchmarkPlacementPolicyInteropability(b *testing.B) {
} }
func BenchmarkManySelects(b *testing.B) { func BenchmarkManySelects(b *testing.B) {
testsFile := filepath.Join("yml_tests", "many_selects.yml") testsFile := filepath.Join("json_tests", "many_selects.json")
bs, err := os.ReadFile(testsFile) bs, err := os.ReadFile(testsFile)
require.NoError(b, err) require.NoError(b, err)
var tc TestCase var tc TestCase
require.NoError(b, yaml.Unmarshal(bs, &tc)) require.NoError(b, json.Unmarshal(bs, &tc))
tt, ok := tc.Tests["Select"] tt, ok := tc.Tests["Select"]
require.True(b, ok) require.True(b, ok)
@ -173,7 +153,7 @@ func BenchmarkManySelects(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for range b.N { for i := 0; i < b.N; i++ {
_, err = nm.ContainerNodes(tt.Policy, tt.Pivot) _, err = nm.ContainerNodes(tt.Policy, tt.Pivot)
if err != nil { if err != nil {
b.FailNow() b.FailNow()

View file

@ -0,0 +1,99 @@
{
"name": "default CBF is 3",
"nodes": [
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "DE"
},
{
"key": "City",
"value": "Berlin"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "FR"
},
{
"key": "City",
"value": "Paris"
}
]
}
],
"tests": {
"set default CBF": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "EU"
}
],
"containerBackupFactor": 0,
"selectors": [
{
"name": "EU",
"count": 1,
"clause": "SAME",
"attribute": "Location",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2
]
]
}
}
}

View file

@ -0,0 +1,99 @@
{
"name": "Real node count multiplier is in range [1, specified CBF]",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Country",
"value": "DE"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Country",
"value": "DE"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Country",
"value": "DE"
}
]
}
],
"tests": {
"select 2, CBF is 2": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "X"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "X",
"count": 2,
"clause": "SAME",
"attribute": "Country",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2
]
]
},
"select 3, CBF is 2": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "X"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "X",
"count": 3,
"clause": "SAME",
"attribute": "Country",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2
]
]
}
}
}

View file

@ -0,0 +1,155 @@
{
"name": "CBF requirements",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Attr",
"value": "Same"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Attr",
"value": "Same"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Attr",
"value": "Same"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "4"
},
{
"key": "Attr",
"value": "Same"
}
]
}
],
"tests": {
"default CBF, no selector": {
"policy": {
"replicas": [
{
"count": 2,
"selector": ""
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"result": [
[
0,
2,
1,
3
]
]
},
"explicit CBF, no selector": {
"policy": {
"replicas": [
{
"count": 2,
"selector": ""
}
],
"containerBackupFactor": 3,
"selectors": [],
"filters": []
},
"result": [
[
0,
2,
1,
3
]
]
},
"select distinct, weak CBF": {
"policy": {
"replicas": [
{
"count": 2,
"selector": "X"
}
],
"containerBackupFactor": 3,
"selectors": [
{
"name": "X",
"count": 2,
"clause": "DISTINCT",
"attribute": "",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
2,
1,
3
]
]
},
"select same, weak CBF": {
"policy": {
"replicas": [
{
"count": 2,
"selector": "X"
}
],
"containerBackupFactor": 3,
"selectors": [
{
"name": "X",
"count": 2,
"clause": "SAME",
"attribute": "Attr",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2,
3
]
]
}
}
}

View file

@ -0,0 +1,383 @@
{
"name": "compound filter",
"nodes": [
{
"attributes": [
{
"key": "Storage",
"value": "SSD"
},
{
"key": "Rating",
"value": "10"
},
{
"key": "IntField",
"value": "100"
},
{
"key": "Param",
"value": "Value1"
}
]
}
],
"tests": {
"good": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "SSD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
},
{
"name": "Main",
"key": "",
"op": "AND",
"value": "",
"filters": [
{
"name": "StorageSSD",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "",
"op": "OR",
"value": "",
"filters": [
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value1",
"filters": []
},
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
},
"result": [
[
0
]
]
},
"bad storage type": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "HDD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
},
{
"name": "Main",
"key": "",
"op": "AND",
"value": "",
"filters": [
{
"name": "StorageSSD",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "",
"op": "OR",
"value": "",
"filters": [
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value1",
"filters": []
},
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
},
"error": "not enough nodes"
},
"bad rating": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "SSD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "15",
"filters": []
},
{
"name": "Main",
"key": "",
"op": "AND",
"value": "",
"filters": [
{
"name": "StorageSSD",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "",
"op": "OR",
"value": "",
"filters": [
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value1",
"filters": []
},
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
},
"error": "not enough nodes"
},
"bad param": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "SSD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
},
{
"name": "Main",
"key": "",
"op": "AND",
"value": "",
"filters": [
{
"name": "StorageSSD",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"key": "",
"op": "OPERATION_UNSPECIFIED",
"value": "",
"filters": []
},
{
"name": "",
"key": "",
"op": "OR",
"value": "",
"filters": [
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value0",
"filters": []
},
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
},
"error": "not enough nodes"
}
}
}

View file

@ -0,0 +1,83 @@
{
"name": "invalid integer field",
"nodes": [
{
"attributes": [
{
"key": "IntegerField",
"value": "true"
}
]
},
{
"attributes": [
{
"key": "IntegerField",
"value": "str"
}
]
}
],
"tests": {
"empty string is not casted to 0": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "IntegerField",
"op": "LE",
"value": "8",
"filters": []
}
]
},
"error": "not enough nodes"
},
"non-empty string is not casted to a number": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "IntegerField",
"op": "GE",
"value": "0",
"filters": []
}
]
},
"error": "not enough nodes"
}
}
}

View file

@ -0,0 +1,403 @@
{
"name": "single-op filters",
"nodes": [
{
"attributes": [
{
"key": "Rating",
"value": "4"
},
{
"key": "Country",
"value": "Germany"
}
]
}
],
"tests": {
"GE true": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
}
]
},
"result": [
[
0
]
]
},
"GE false": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GE",
"value": "5",
"filters": []
}
]
},
"error": "not enough nodes"
},
"GT true": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GT",
"value": "3",
"filters": []
}
]
},
"result": [
[
0
]
]
},
"GT false": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GT",
"value": "4",
"filters": []
}
]
},
"error": "not enough nodes"
},
"LE true": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LE",
"value": "4",
"filters": []
}
]
},
"result": [
[
0
]
]
},
"LE false": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LE",
"value": "3",
"filters": []
}
]
},
"error": "not enough nodes"
},
"LT true": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LT",
"value": "5",
"filters": []
}
]
},
"result": [
[
0
]
]
},
"LT false": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LT",
"value": "4",
"filters": []
}
]
},
"error": "not enough nodes"
},
"EQ true": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "EQ",
"value": "Germany",
"filters": []
}
]
},
"result": [
[
0
]
]
},
"EQ false": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "EQ",
"value": "China",
"filters": []
}
]
},
"error": "not enough nodes"
},
"NE true": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "NE",
"value": "France",
"filters": []
}
]
},
"result": [
[
0
]
]
},
"NE false": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"attribute": "",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "NE",
"value": "Germany",
"filters": []
}
]
},
"error": "not enough nodes"
}
}
}

View file

@ -0,0 +1,165 @@
{
"name": "HRW ordering",
"nodes": [
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Price",
"value": "2"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Price",
"value": "4"
},
{
"key": "Capacity",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Price",
"value": "3"
},
{
"key": "Capacity",
"value": "10"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Price",
"value": "2"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Price",
"value": "1"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Price",
"value": "100"
},
{
"key": "Capacity",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Price",
"value": "7"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Price",
"value": "2"
},
{
"key": "Capacity",
"value": "1"
}
]
}
],
"tests": {
"select 3 nodes in 3 distinct countries, same placement": {
"policy": {"replicas":[{"count":1,"selector":"Main"}],"containerBackupFactor":1,"selectors":[{"name":"Main","count":3,"clause":"DISTINCT","attribute":"Country","filter":"*"}],"filters":[]},
"pivot": "Y29udGFpbmVySUQ=",
"result": [[4, 0, 7]],
"placement": {
"pivot": "b2JqZWN0SUQ=",
"result": [[4, 0, 7]]
}
},
"select 6 nodes in 3 distinct countries, different placement": {
"policy": {"replicas":[{"count":1,"selector":"Main"}],"containerBackupFactor":2,"selectors":[{"name":"Main","count":3,"clause":"DISTINCT","attribute":"Country","filter":"*"}],"filters":[]},
"pivot": "Y29udGFpbmVySUQ=",
"result": [[4, 3, 0, 1, 7, 2]],
"placement": {
"pivot": "b2JqZWN0SUQ=",
"result": [[4, 3, 0, 7, 2, 1]]
}
}
}
}

View file

@ -0,0 +1,108 @@
{
"name": "unnamed selector (nspcc-dev/neofs-api-go#213)",
"nodes": [
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Russia"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Russia"
},
{
"key": "City",
"value": "Saint-Petersburg"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Sweden"
},
{
"key": "City",
"value": "Stockholm"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Finalnd"
},
{
"key": "City",
"value": "Helsinki"
}
]
}
],
"tests": {
"test": {
"policy": {
"replicas": [
{
"count": 4,
"selector": ""
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "",
"count": 4,
"clause": "DISTINCT",
"attribute": "",
"filter": "LOC_EU"
}
],
"filters": [
{
"name": "LOC_EU",
"key": "Location",
"op": "EQ",
"value": "Europe",
"filters": []
}
]
},
"result": [
[
0,
1,
2,
3
]
]
}
}
}

View file

@ -0,0 +1,192 @@
{
"name": "single-op filters",
"nodes": [
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "1"
},
{
"key": "City",
"value": "SPB"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Rating",
"value": "5"
},
{
"key": "City",
"value": "Berlin"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "6"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Rating",
"value": "4"
},
{
"key": "City",
"value": "Paris"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Rating",
"value": "1"
},
{
"key": "City",
"value": "Lyon"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "5"
},
{
"key": "City",
"value": "SPB"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "7"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Rating",
"value": "3"
},
{
"key": "City",
"value": "Darmstadt"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Rating",
"value": "7"
},
{
"key": "City",
"value": "Frankfurt"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "9"
},
{
"key": "City",
"value": "SPB"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "9"
},
{
"key": "City",
"value": "SPB"
}
]
}
],
"tests": {
"Select": {
"policy": {"replicas":[{"count":1,"selector":"SameRU"},{"count":1,"selector":"DistinctRU"},{"count":1,"selector":"Good"},{"count":1,"selector":"Main"}],"containerBackupFactor":2,"selectors":[{"name":"SameRU","count":2,"clause":"SAME","attribute":"City","filter":"FromRU"},{"name":"DistinctRU","count":2,"clause":"DISTINCT","attribute":"City","filter":"FromRU"},{"name":"Good","count":2,"clause":"DISTINCT","attribute":"Country","filter":"Good"},{"name":"Main","count":3,"clause":"DISTINCT","attribute":"Country","filter":"*"}],"filters":[{"name":"FromRU","key":"Country","op":"EQ","value":"Russia"},{"name":"Good","key":"Rating","op":"GE","value":"4"}]},
"result": [
[0, 5, 9, 10],
[2, 6, 0, 5],
[1, 8, 2, 5],
[3, 4, 1, 7, 0, 2]
]
}
}
}

View file

@ -0,0 +1,94 @@
{
"name": "multiple replicas (#215)",
"nodes": [
{
"attributes": [
{
"key": "City",
"value": "Saint-Petersburg"
}
]
},
{
"attributes": [
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "City",
"value": "Berlin"
}
]
},
{
"attributes": [
{
"key": "City",
"value": "Paris"
}
]
}
],
"tests": {
"test": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "LOC_SPB_PLACE"
},
{
"count": 1,
"selector": "LOC_MSK_PLACE"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "LOC_SPB_PLACE",
"count": 1,
"clause": "CLAUSE_UNSPECIFIED",
"attribute": "",
"filter": "LOC_SPB"
},
{
"name": "LOC_MSK_PLACE",
"count": 1,
"clause": "CLAUSE_UNSPECIFIED",
"attribute": "",
"filter": "LOC_MSK"
}
],
"filters": [
{
"name": "LOC_SPB",
"key": "City",
"op": "EQ",
"value": "Saint-Petersburg",
"filters": []
},
{
"name": "LOC_MSK",
"key": "City",
"op": "EQ",
"value": "Moscow",
"filters": []
}
]
},
"result": [
[
0
],
[
1
]
]
}
}
}

View file

@ -0,0 +1,331 @@
{
"name": "multiple REP, asymmetric",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "0"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "4"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "5"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "6"
},
{
"key": "Continent",
"value": "NA"
},
{
"key": "City",
"value": "NewYork"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "7"
},
{
"key": "Continent",
"value": "AF"
},
{
"key": "City",
"value": "Cairo"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "8"
},
{
"key": "Continent",
"value": "AF"
},
{
"key": "City",
"value": "Cairo"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "9"
},
{
"key": "Continent",
"value": "SA"
},
{
"key": "City",
"value": "Lima"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "10"
},
{
"key": "Continent",
"value": "AF"
},
{
"key": "City",
"value": "Cairo"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "11"
},
{
"key": "Continent",
"value": "NA"
},
{
"key": "City",
"value": "NewYork"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "12"
},
{
"key": "Continent",
"value": "NA"
},
{
"key": "City",
"value": "LosAngeles"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "13"
},
{
"key": "Continent",
"value": "SA"
},
{
"key": "City",
"value": "Lima"
}
]
}
],
"tests": {
"test": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "SPB"
},
{
"count": 2,
"selector": "Americas"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "SPB",
"count": 1,
"clause": "SAME",
"attribute": "City",
"filter": "SPBSSD"
},
{
"name": "Americas",
"count": 2,
"clause": "DISTINCT",
"attribute": "City",
"filter": "Americas"
}
],
"filters": [
{
"name": "SPBSSD",
"key": "",
"op": "AND",
"value": "",
"filters": [
{
"name": "",
"key": "Country",
"op": "EQ",
"value": "RU",
"filters": []
},
{
"name": "",
"key": "City",
"op": "EQ",
"value": "St.Petersburg",
"filters": []
},
{
"name": "",
"key": "SSD",
"op": "EQ",
"value": "1",
"filters": []
}
]
},
{
"name": "Americas",
"key": "",
"op": "OR",
"value": "",
"filters": [
{
"name": "",
"key": "Continent",
"op": "EQ",
"value": "NA",
"filters": []
},
{
"name": "",
"key": "Continent",
"op": "EQ",
"value": "SA",
"filters": []
}
]
}
]
},
"result": [
[
1,
4
],
[
8,
12,
5,
10
]
]
}
}
}

View file

@ -0,0 +1,110 @@
{
"name": "REP X",
"nodes": [
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Saint-Petersburg",
"parents": []
}
],
"state": "UNSPECIFIED"
},
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Moscow",
"parents": []
}
],
"state": "UNSPECIFIED"
},
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Berlin",
"parents": []
}
],
"state": "UNSPECIFIED"
},
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Paris",
"parents": []
}
],
"state": "UNSPECIFIED"
}
],
"tests": {
"REP 1": {
"policy": {
"replicas": [
{
"count": 1,
"selector": ""
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"result": [
[
0,
1,
2
]
]
},
"REP 3": {
"policy": {
"replicas": [
{
"count": 3,
"selector": ""
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"result": [
[
0,
3,
1,
2
]
]
},
"REP 5": {
"policy": {
"replicas": [
{
"count": 5,
"selector": ""
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"error": "not enough nodes"
}
}
}

View file

@ -0,0 +1,116 @@
{
"name": "select with unspecified attribute",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "0"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "4"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
}
],
"tests": {
"test": {
"policy": {
"replicas": [
{
"count": 1,
"selector": "X"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "X",
"count": 4,
"clause": "DISTINCT",
"attribute": "",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2,
3
]
]
}
}
}

View file

@ -0,0 +1,101 @@
{
"name": "invalid selections",
"nodes": [
{
"attributes": [
{
"key": "Country",
"value": "Russia"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
}
]
},
{
"attributes": []
}
],
"tests": {
"missing filter": {
"policy": {
"replicas": [],
"containerBackupFactor": 1,
"selectors": [
{
"name": "MyStore",
"count": 1,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "FromNL"
}
],
"filters": [
{
"name": "FromRU",
"key": "Country",
"op": "EQ",
"value": "Russia",
"filters": []
}
]
},
"error": "filter not found"
},
"not enough nodes (backup factor)": {
"policy": {
"replicas": [],
"containerBackupFactor": 2,
"selectors": [
{
"name": "MyStore",
"count": 2,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "FromRU"
}
],
"filters": [
{
"name": "FromRU",
"key": "Country",
"op": "EQ",
"value": "Russia",
"filters": []
}
]
},
"error": "not enough nodes"
},
"not enough nodes (buckets)": {
"policy": {
"replicas": [],
"containerBackupFactor": 1,
"selectors": [
{
"name": "MyStore",
"count": 2,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "FromRU"
}
],
"filters": [
{
"name": "FromRU",
"key": "Country",
"op": "EQ",
"value": "Russia",
"filters": []
}
]
},
"error": "not enough nodes"
}
}
}

View file

@ -113,10 +113,9 @@ func (n nodes) Hash() uint64 {
return 0 return 0
} }
func (n nodes) appendWeightsTo(wf weightFunc, w []float64) []float64 { // weights returns slice of nodes weights W.
if cap(w) < len(n) { func (n nodes) weights(wf weightFunc) []float64 {
w = make([]float64, 0, len(n)) w := make([]float64, 0, len(n))
}
for i := range n { for i := range n {
w = append(w, wf(n[i])) w = append(w, wf(n[i]))
} }
@ -150,13 +149,10 @@ func (m NetMap) PlacementVectors(vectors [][]NodeInfo, pivot []byte) ([][]NodeIn
wf := defaultWeightFunc(m.nodes) wf := defaultWeightFunc(m.nodes)
result := make([][]NodeInfo, len(vectors)) result := make([][]NodeInfo, len(vectors))
var ws []float64
for i := range vectors { for i := range vectors {
result[i] = make([]NodeInfo, len(vectors[i])) result[i] = make([]NodeInfo, len(vectors[i]))
copy(result[i], vectors[i]) copy(result[i], vectors[i])
ws = nodes(result[i]).appendWeightsTo(wf, ws[:0]) hrw.SortHasherSliceByWeightValue(result[i], nodes(result[i]).weights(wf), h)
hrw.SortHasherSliceByWeightValue(result[i], ws, h)
} }
return result, nil return result, nil
@ -213,25 +209,6 @@ func (m NetMap) SelectFilterNodes(expr *SelectFilterExpr) ([][]NodeInfo, error)
return ret, nil return ret, nil
} }
func countNodes(r netmap.Replica) uint32 {
if r.GetCount() != 0 {
return r.GetCount()
}
return r.GetECDataCount() + r.GetECParityCount()
}
func (p PlacementPolicy) isUnique() bool {
if p.unique {
return true
}
for _, r := range p.replicas {
if r.GetECDataCount() != 0 || r.GetECParityCount() != 0 {
return true
}
}
return false
}
// ContainerNodes returns two-dimensional list of nodes as a result of applying // ContainerNodes returns two-dimensional list of nodes as a result of applying
// given PlacementPolicy to the NetMap. Each line of the list corresponds to a // given PlacementPolicy to the NetMap. Each line of the list corresponds to a
// replica descriptor. Line order corresponds to order of ReplicaDescriptor list // replica descriptor. Line order corresponds to order of ReplicaDescriptor list
@ -253,7 +230,6 @@ func (m NetMap) ContainerNodes(p PlacementPolicy, pivot []byte) ([][]NodeInfo, e
return nil, err return nil, err
} }
unique := p.isUnique()
result := make([][]NodeInfo, len(p.replicas)) result := make([][]NodeInfo, len(p.replicas))
// Note that the cached selectors are not used when the policy contains the UNIQUE flag. // Note that the cached selectors are not used when the policy contains the UNIQUE flag.
@ -262,9 +238,9 @@ func (m NetMap) ContainerNodes(p PlacementPolicy, pivot []byte) ([][]NodeInfo, e
// marked as used by earlier replicas. // marked as used by earlier replicas.
for i := range p.replicas { for i := range p.replicas {
sName := p.replicas[i].GetSelector() sName := p.replicas[i].GetSelector()
if sName == "" && !(len(p.replicas) == 1 && len(p.selectors) == 1) { if sName == "" {
var s netmap.Selector var s netmap.Selector
s.SetCount(countNodes(p.replicas[i])) s.SetCount(p.replicas[i].GetCount())
s.SetFilter(mainFilterName) s.SetFilter(mainFilterName)
nodes, err := c.getSelection(s) nodes, err := c.getSelection(s)
@ -274,17 +250,14 @@ func (m NetMap) ContainerNodes(p PlacementPolicy, pivot []byte) ([][]NodeInfo, e
result[i] = append(result[i], flattenNodes(nodes)...) result[i] = append(result[i], flattenNodes(nodes)...)
if unique { if p.unique {
c.addUsedNodes(result[i]...) c.addUsedNodes(result[i]...)
} }
continue continue
} }
if unique { if p.unique {
if c.processedSelectors[sName] == nil {
return nil, fmt.Errorf("selector not found: '%s'", sName)
}
nodes, err := c.getSelection(*c.processedSelectors[sName]) nodes, err := c.getSelection(*c.processedSelectors[sName])
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -49,6 +49,11 @@ func (x *NetworkInfo) readFromV2(m netmap.NetworkInfo, checkFieldPresence bool)
mNames[name] = struct{}{} mNames[name] = struct{}{}
switch name { switch name {
default:
if len(prm.GetValue()) == 0 {
err = fmt.Errorf("empty attribute value %s", name)
return true
}
case case
configAuditFee, configAuditFee,
configStoragePrice, configStoragePrice,
@ -57,8 +62,6 @@ func (x *NetworkInfo) readFromV2(m netmap.NetworkInfo, checkFieldPresence bool)
configEpochDuration, configEpochDuration,
configIRCandidateFee, configIRCandidateFee,
configMaxObjSize, configMaxObjSize,
configMaxECDataCount,
configMaxECParityCount,
configWithdrawalFee: configWithdrawalFee:
_, err = decodeConfigValueUint64(prm.GetValue()) _, err = decodeConfigValueUint64(prm.GetValue())
case configHomomorphicHashingDisabled, case configHomomorphicHashingDisabled,
@ -231,8 +234,6 @@ func (x *NetworkInfo) IterateRawNetworkParameters(f func(name string, value []by
configEpochDuration, configEpochDuration,
configIRCandidateFee, configIRCandidateFee,
configMaxObjSize, configMaxObjSize,
configMaxECDataCount,
configMaxECParityCount,
configWithdrawalFee, configWithdrawalFee,
configHomomorphicHashingDisabled, configHomomorphicHashingDisabled,
configMaintenanceModeAllowed: configMaintenanceModeAllowed:
@ -431,34 +432,6 @@ func (x NetworkInfo) MaxObjectSize() uint64 {
return x.configUint64(configMaxObjSize) return x.configUint64(configMaxObjSize)
} }
const configMaxECDataCount = "MaxECDataCount"
// SetMaxECDataCount sets maximum number of data shards for erasure codes.
//
// Zero means no restrictions.
func (x *NetworkInfo) SetMaxECDataCount(dataCount uint64) {
x.setConfigUint64(configMaxECDataCount, dataCount)
}
// MaxECDataCount returns maximum number of data shards for erasure codes.
func (x NetworkInfo) MaxECDataCount() uint64 {
return x.configUint64(configMaxECDataCount)
}
const configMaxECParityCount = "MaxECParityCount"
// SetMaxECParityCount sets maximum number of parity shards for erasure codes.
//
// Zero means no restrictions.
func (x *NetworkInfo) SetMaxECParityCount(parityCount uint64) {
x.setConfigUint64(configMaxECParityCount, parityCount)
}
// MaxECParityCount returns maximum number of parity shards for erasure codes.
func (x NetworkInfo) MaxECParityCount() uint64 {
return x.configUint64(configMaxECParityCount)
}
const configWithdrawalFee = "WithdrawFee" const configWithdrawalFee = "WithdrawFee"
// SetWithdrawalFee sets fee for withdrawals from the FrostFS accounts that // SetWithdrawalFee sets fee for withdrawals from the FrostFS accounts that

View file

@ -173,32 +173,6 @@ func TestNetworkInfo_MaxObjectSize(t *testing.T) {
) )
} }
func TestNetworkInfo_MaxECDataCount(t *testing.T) {
testConfigValue(t,
func(x NetworkInfo) any { return x.MaxECDataCount() },
func(info *NetworkInfo, val any) { info.SetMaxECDataCount(val.(uint64)) },
uint64(1), uint64(2),
"MaxECDataCount", func(val any) []byte {
data := make([]byte, 8)
binary.LittleEndian.PutUint64(data, val.(uint64))
return data
},
)
}
func TestNetworkInfo_MaxECParityCount(t *testing.T) {
testConfigValue(t,
func(x NetworkInfo) any { return x.MaxECParityCount() },
func(info *NetworkInfo, val any) { info.SetMaxECParityCount(val.(uint64)) },
uint64(1), uint64(2),
"MaxECParityCount", func(val any) []byte {
data := make([]byte, 8)
binary.LittleEndian.PutUint64(data, val.(uint64))
return data
},
)
}
func TestNetworkInfo_WithdrawalFee(t *testing.T) { func TestNetworkInfo_WithdrawalFee(t *testing.T) {
testConfigValue(t, testConfigValue(t,
func(x NetworkInfo) any { return x.WithdrawalFee() }, func(x NetworkInfo) any { return x.WithdrawalFee() },

View file

@ -3,7 +3,7 @@ package netmap
import ( import (
"errors" "errors"
"fmt" "fmt"
"slices" "sort"
"strconv" "strconv"
"strings" "strings"
@ -227,6 +227,14 @@ func (x NodeInfo) Hash() uint64 {
return hrw.Hash(x.m.GetPublicKey()) return hrw.Hash(x.m.GetPublicKey())
} }
// less declares "less than" comparison between two NodeInfo instances:
// x1 is less than x2 if it has less Hash().
//
// Method is needed for internal placement needs.
func less(x1, x2 NodeInfo) bool {
return x1.Hash() < x2.Hash()
}
func (x *NodeInfo) setNumericAttribute(key string, num uint64) { func (x *NodeInfo) setNumericAttribute(key string, num uint64) {
x.SetAttribute(key, strconv.FormatUint(num, 10)) x.SetAttribute(key, strconv.FormatUint(num, 10))
} }
@ -447,11 +455,15 @@ func (x *NodeInfo) SortAttributes() {
return return
} }
slices.SortFunc(as, func(ai, aj netmap.Attribute) int { sort.Slice(as, func(i, j int) bool {
if r := strings.Compare(ai.GetKey(), aj.GetKey()); r != 0 { switch strings.Compare(as[i].GetKey(), as[j].GetKey()) {
return r case -1:
return true
case 1:
return false
default:
return as[i].GetValue() < as[j].GetValue()
} }
return strings.Compare(ai.GetValue(), aj.GetValue())
}) })
x.m.SetAttributes(as) x.m.SetAttributes(as)
@ -460,10 +472,6 @@ func (x *NodeInfo) SortAttributes() {
// SetOffline sets the state of the node to "offline". When a node updates // SetOffline sets the state of the node to "offline". When a node updates
// information about itself in the network map, this action is interpreted as // information about itself in the network map, this action is interpreted as
// an intention to leave the network. // an intention to leave the network.
//
// See also IsOffline.
//
// Deprecated: use SetStatus instead.
func (x *NodeInfo) SetOffline() { func (x *NodeInfo) SetOffline() {
x.m.SetState(netmap.Offline) x.m.SetState(netmap.Offline)
} }
@ -474,8 +482,6 @@ func (x *NodeInfo) SetOffline() {
// mean online). // mean online).
// //
// See also SetOffline. // See also SetOffline.
//
// Deprecated: use Status instead.
func (x NodeInfo) IsOffline() bool { func (x NodeInfo) IsOffline() bool {
return x.m.GetState() == netmap.Offline return x.m.GetState() == netmap.Offline
} }
@ -485,8 +491,6 @@ func (x NodeInfo) IsOffline() bool {
// action is interpreted as an intention to enter the network. // action is interpreted as an intention to enter the network.
// //
// See also IsOnline. // See also IsOnline.
//
// Deprecated: use SetStatus instead.
func (x *NodeInfo) SetOnline() { func (x *NodeInfo) SetOnline() {
x.m.SetState(netmap.Online) x.m.SetState(netmap.Online)
} }
@ -497,8 +501,6 @@ func (x *NodeInfo) SetOnline() {
// mean offline). // mean offline).
// //
// See also SetOnline. // See also SetOnline.
//
// Deprecated: use Status instead.
func (x NodeInfo) IsOnline() bool { func (x NodeInfo) IsOnline() bool {
return x.m.GetState() == netmap.Online return x.m.GetState() == netmap.Online
} }
@ -508,8 +510,6 @@ func (x NodeInfo) IsOnline() bool {
// state declares temporal unavailability for a node. // state declares temporal unavailability for a node.
// //
// See also IsMaintenance. // See also IsMaintenance.
//
// Deprecated: use SetStatus instead.
func (x *NodeInfo) SetMaintenance() { func (x *NodeInfo) SetMaintenance() {
x.m.SetState(netmap.Maintenance) x.m.SetState(netmap.Maintenance)
} }
@ -519,63 +519,6 @@ func (x *NodeInfo) SetMaintenance() {
// Zero NodeInfo has undefined state. // Zero NodeInfo has undefined state.
// //
// See also SetMaintenance. // See also SetMaintenance.
//
// Deprecated: use Status instead.
func (x NodeInfo) IsMaintenance() bool { func (x NodeInfo) IsMaintenance() bool {
return x.m.GetState() == netmap.Maintenance return x.m.GetState() == netmap.Maintenance
} }
type NodeState netmap.NodeState
const (
UnspecifiedState = NodeState(netmap.UnspecifiedState)
Online = NodeState(netmap.Online)
Offline = NodeState(netmap.Offline)
Maintenance = NodeState(netmap.Maintenance)
)
// ToV2 converts NodeState to v2.
func (ns NodeState) ToV2() netmap.NodeState {
return netmap.NodeState(ns)
}
// FromV2 reads NodeState to v2.
func (ns *NodeState) FromV2(state netmap.NodeState) {
*ns = NodeState(state)
}
// Status returns the current state of the node in the network map.
//
// Zero NodeInfo has an undefined state, neither online nor offline.
func (x NodeInfo) Status() NodeState {
return NodeState(x.m.GetState())
}
// SetState updates the state of the node in the network map.
//
// The state determines the node's current status within the network:
// - "online": Indicates the node intends to enter the network.
// - "offline": Indicates the node intends to leave the network.
// - "maintenance": Indicates the node is temporarily unavailable.
//
// See also Status.
func (x *NodeInfo) SetStatus(state NodeState) {
x.m.SetState(netmap.NodeState(state))
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions.
func (ns NodeState) String() string {
return netmap.NodeState(ns).String()
}
// IsOnline checks if the current state is "online".
func (ns NodeState) IsOnline() bool { return ns == Online }
// IsOffline checks if the current state is "offline".
func (ns NodeState) IsOffline() bool { return ns == Offline }
// IsMaintenance checks if the current state is "maintenance".
func (ns NodeState) IsMaintenance() bool { return ns == Maintenance }

Some files were not shown because too many files have changed in this diff Show more