Compare commits

..

10 commits

Author SHA1 Message Date
2be938f3cd [#823] go.mod: Update frostfs-api-go
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-21 14:46:50 +03:00
eac61fe9b2 [#733] frostfs-cli: Add control ir remove-container
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-11-03 15:42:58 +03:00
dc81b4b50c [#725] writecache: Fix metric values
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-10-27 16:25:04 +03:00
98da032324 [#758] ir: Do not exclude node in maintenance mode from netmap
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-10-26 16:05:02 +03:00
5acc13fa94 [#732] containersvc: Remove load announcement
IR code was removed in 8879c6ea.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-09 15:17:10 +03:00
e0f0b93b5e [#723] netmap: Drop already bootstraped check
Because of this check, under certain conditions,
the node could be removed from the network map,
although the node was functioning normally.

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-10-05 15:31:18 +03:00
eb5248621a [#723] netmap: Send bootstrap at each epoch tick
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-10-05 15:31:10 +03:00
02be6a4341 [#702] node: Update SDK version
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-09-29 18:46:06 +03:00
368774be95 [#691] node: Compare node info during initial bootstrap properly
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-09-18 07:30:15 +00:00
b9ef294b99 [#692] go.mod: Update sdk-go
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-09-15 16:54:15 +03:00
350 changed files with 1707 additions and 5065 deletions

View file

@ -39,7 +39,7 @@ linters-settings:
alias: objectSDK alias: objectSDK
custom: custom:
truecloudlab-linters: truecloudlab-linters:
path: bin/linters/external_linters.so path: bin/external_linters.so
original-url: git.frostfs.info/TrueCloudLab/linters.git original-url: git.frostfs.info/TrueCloudLab/linters.git
settings: settings:
noliteral: noliteral:

View file

@ -47,15 +47,6 @@ repos:
types: [go] types: [go]
language: system language: system
- repo: local
hooks:
- id: gofumpt
name: gofumpt
entry: make fumpt
pass_filenames: false
types: [go]
language: system
- repo: https://github.com/TekWizely/pre-commit-golang - repo: https://github.com/TekWizely/pre-commit-golang
rev: v1.0.0-rc.1 rev: v1.0.0-rc.1
hooks: hooks:

View file

@ -10,14 +10,6 @@ HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
GO_VERSION ?= 1.21 GO_VERSION ?= 1.21
LINT_VERSION ?= 1.54.0 LINT_VERSION ?= 1.54.0
TRUECLOUDLAB_LINT_VERSION ?= 0.0.2 TRUECLOUDLAB_LINT_VERSION ?= 0.0.2
PROTOC_VERSION ?= 25.0
PROTOC_GEN_GO_VERSION ?= $(shell go list -f '{{.Version}}' -m google.golang.org/protobuf)
PROTOGEN_FROSTFS_VERSION ?= $(shell go list -f '{{.Version}}' -m git.frostfs.info/TrueCloudLab/frostfs-api-go/v2)
PROTOC_OS_VERSION=osx-x86_64
ifeq ($(shell uname), Linux)
PROTOC_OS_VERSION=linux-x86_64
endif
STATICCHECK_VERSION ?= 2023.1.6
ARCH = amd64 ARCH = amd64
BIN = bin BIN = bin
@ -34,17 +26,11 @@ PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
sed -E "s/(.*)-(g[a-fA-F0-9]{6,8})(.*)/\1\3~\2/" | \ sed -E "s/(.*)-(g[a-fA-F0-9]{6,8})(.*)/\1\3~\2/" | \
sed "s/-/~/")-${OS_RELEASE} sed "s/-/~/")-${OS_RELEASE}
OUTPUT_LINT_DIR ?= $(abspath $(BIN))/linters OUTPUT_LINT_DIR ?= $(shell pwd)/bin
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION) LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
TMP_DIR := .cache TMP_DIR := .cache
PROTOBUF_DIR ?= $(abspath $(BIN))/protobuf
PROTOC_DIR ?= $(PROTOBUF_DIR)/protoc-v$(PROTOC_VERSION)
PROTOC_GEN_GO_DIR ?= $(PROTOBUF_DIR)/protoc-gen-go-$(PROTOC_GEN_GO_VERSION)
PROTOGEN_FROSTFS_DIR ?= $(PROTOBUF_DIR)/protogen-$(PROTOGEN_FROSTFS_VERSION)
STATICCHECK_DIR ?= $(abspath $(BIN))/staticcheck
STATICCHECK_VERSION_DIR ?= $(STATICCHECK_DIR)/$(STATICCHECK_VERSION)
.PHONY: help all images dep clean fmts fumpt imports test lint docker/lint .PHONY: help all images dep clean fmts fmt imports test lint docker/lint
prepare-release debpackage pre-commit unpre-commit prepare-release debpackage pre-commit unpre-commit
# To build a specific binary, use it's name prefix with bin/ as a target # To build a specific binary, use it's name prefix with bin/ as a target
@ -84,40 +70,24 @@ dep:
CGO_ENABLED=0 \ CGO_ENABLED=0 \
go mod tidy -v && echo OK go mod tidy -v && echo OK
# Build export-metrics
export-metrics: dep
@printf "⇒ Build export-metrics\n"
CGO_ENABLED=0 \
go build -v -trimpath -o bin/export-metrics ./scripts/export-metrics
# Regenerate proto files: # Regenerate proto files:
protoc: protoc:
@if [ ! -d "$(PROTOC_DIR)" ] || [ ! -d "$(PROTOC_GEN_GO_DIR)" ] || [ ! -d "$(PROTOGEN_FROSTFS_DIR)" ]; then \ @GOPRIVATE=github.com/TrueCloudLab go mod vendor
make protoc-install; \ # Install specific version for protobuf lib
fi @go list -f '{{.Path}}/...@{{.Version}}' -m github.com/golang/protobuf | xargs go install -v
@for f in `find . -type f -name '*.proto' -not -path './bin/*'`; do \ @GOBIN=$(abspath $(BIN)) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/protogen
# Protoc generate
@for f in `find . -type f -name '*.proto' -not -path './vendor/*'`; do \
echo "⇒ Processing $$f "; \ echo "⇒ Processing $$f "; \
$(PROTOC_DIR)/bin/protoc \ protoc \
--proto_path=.:$(PROTOC_DIR)/include:/usr/local/include \ --proto_path=.:./vendor:/usr/local/include \
--plugin=protoc-gen-go=$(PROTOC_GEN_GO_DIR)/protoc-gen-go \ --plugin=protoc-gen-go-frostfs=$(BIN)/protogen \
--plugin=protoc-gen-go-frostfs=$(PROTOGEN_FROSTFS_DIR)/protogen \
--go-frostfs_out=. --go-frostfs_opt=paths=source_relative \ --go-frostfs_out=. --go-frostfs_opt=paths=source_relative \
--go_out=. --go_opt=paths=source_relative \ --go_out=. --go_opt=paths=source_relative \
--go-grpc_opt=require_unimplemented_servers=false \ --go-grpc_opt=require_unimplemented_servers=false \
--go-grpc_out=. --go-grpc_opt=paths=source_relative $$f; \ --go-grpc_out=. --go-grpc_opt=paths=source_relative $$f; \
done done
rm -rf vendor
protoc-install:
@rm -rf $(PROTOBUF_DIR)
@mkdir $(PROTOBUF_DIR)
@echo "⇒ Installing protoc... "
@wget -q -O $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip 'https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(PROTOC_OS_VERSION).zip'
@unzip -q -o $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip -d $(PROTOC_DIR)
@rm $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip
@echo "⇒ Installing protoc-gen-go..."
@GOBIN=$(PROTOC_GEN_GO_DIR) go install -v google.golang.org/protobuf/...@$(PROTOC_GEN_GO_VERSION)
@echo "⇒ Instaling protogen FrostFS plugin..."
@GOBIN=$(PROTOGEN_FROSTFS_DIR) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/protogen@$(PROTOGEN_FROSTFS_VERSION)
# Build FrostFS component's docker image # Build FrostFS component's docker image
image-%: image-%:
@ -146,17 +116,18 @@ docker/%:
# Run all code formatters # Run all code formatters
fmts: fumpt imports fmts: fmt imports
# Reformat code
fmt:
@echo "⇒ Processing gofmt check"
@gofmt -s -w cmd/ pkg/ misc/
# Reformat imports # Reformat imports
imports: imports:
@echo "⇒ Processing goimports check" @echo "⇒ Processing goimports check"
@goimports -w cmd/ pkg/ misc/ @goimports -w cmd/ pkg/ misc/
fumpt:
@echo "⇒ Processing gofumpt check"
@gofumpt -l -w cmd/ pkg/ misc/
# Run Unit Test with go test # Run Unit Test with go test
test: test:
@echo "⇒ Running go test" @echo "⇒ Running go test"
@ -167,8 +138,6 @@ pre-commit-run:
# Install linters # Install linters
lint-install: lint-install:
@rm -rf $(OUTPUT_LINT_DIR)
@mkdir $(OUTPUT_LINT_DIR)
@mkdir -p $(TMP_DIR) @mkdir -p $(TMP_DIR)
@rm -rf $(TMP_DIR)/linters @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 @git -c advice.detachedHead=false clone --branch v$(TRUECLOUDLAB_LINT_VERSION) https://git.frostfs.info/TrueCloudLab/linters.git $(TMP_DIR)/linters
@ -180,22 +149,18 @@ lint-install:
# Run linters # Run linters
lint: lint:
@if [ ! -d "$(LINT_DIR)" ]; then \ @if [ ! -d "$(LINT_DIR)" ]; then \
make lint-install; \ echo "Run make lint-install"; \
exit 1; \
fi fi
$(LINT_DIR)/golangci-lint run $(LINT_DIR)/golangci-lint run
# Install staticcheck # Install staticcheck
staticcheck-install: staticcheck-install:
@rm -rf $(STATICCHECK_DIR) @go install honnef.co/go/tools/cmd/staticcheck@latest
@mkdir $(STATICCHECK_DIR)
@GOBIN=$(STATICCHECK_VERSION_DIR) go install honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION)
# Run staticcheck # Run staticcheck
staticcheck-run: staticcheck-run:
@if [ ! -d "$(STATICCHECK_VERSION_DIR)" ]; then \ @staticcheck ./...
make staticcheck-install; \
fi
@$(STATICCHECK_VERSION_DIR)/staticcheck ./...
# Run linters in Docker # Run linters in Docker
docker/lint: docker/lint:
@ -219,6 +184,7 @@ version:
# Delete built artifacts # Delete built artifacts
clean: clean:
rm -rf vendor
rm -rf .cache rm -rf .cache
rm -rf $(BIN) rm -rf $(BIN)
rm -rf $(RELEASE) rm -rf $(RELEASE)

View file

@ -50,12 +50,12 @@ func initConfig(cmd *cobra.Command, _ []string) error {
} }
pathDir := filepath.Dir(configPath) pathDir := filepath.Dir(configPath)
err = os.MkdirAll(pathDir, 0o700) err = os.MkdirAll(pathDir, 0700)
if err != nil { if err != nil {
return fmt.Errorf("create dir %s: %w", pathDir, err) return fmt.Errorf("create dir %s: %w", pathDir, err)
} }
f, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_SYNC, 0o600) f, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_SYNC, 0600)
if err != nil { if err != nil {
return fmt.Errorf("open %s: %w", configPath, err) return fmt.Errorf("open %s: %w", configPath, err)
} }

View file

@ -16,7 +16,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt" "github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
@ -57,8 +56,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
inv := invoker.New(c, nil) inv := invoker.New(c, nil)
if dumpStorage || dumpAlphabet || dumpProxy { if dumpStorage || dumpAlphabet || dumpProxy {
r := management.NewReader(inv) nnsCs, err = c.GetContractStateByID(1)
nnsCs, err = r.GetContractByID(1)
if err != nil { if err != nil {
return fmt.Errorf("can't get NNS contract info: %w", err) return fmt.Errorf("can't get NNS contract info: %w", err)
} }

View file

@ -13,7 +13,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
@ -30,9 +29,8 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
} }
inv := invoker.New(c, nil) inv := invoker.New(c, nil)
r := management.NewReader(inv)
cs, err := r.GetContractByID(1) cs, err := c.GetContractStateByID(1)
if err != nil { if err != nil {
return fmt.Errorf("can't get NNS contract info: %w", err) return fmt.Errorf("can't get NNS contract info: %w", err)
} }
@ -89,8 +87,7 @@ func setConfigCmd(cmd *cobra.Command, args []string) error {
return fmt.Errorf("can't initialize context: %w", err) return fmt.Errorf("can't initialize context: %w", err)
} }
r := management.NewReader(wCtx.ReadOnlyInvoker) cs, err := wCtx.Client.GetContractStateByID(1)
cs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return fmt.Errorf("can't get NNS contract info: %w", err) return fmt.Errorf("can't get NNS contract info: %w", err)
} }

View file

@ -11,7 +11,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -23,15 +22,14 @@ import (
var errInvalidContainerResponse = errors.New("invalid response from container contract") var errInvalidContainerResponse = errors.New("invalid response from container contract")
func getContainerContractHash(cmd *cobra.Command, inv *invoker.Invoker) (util.Uint160, error) { func getContainerContractHash(cmd *cobra.Command, inv *invoker.Invoker, c Client) (util.Uint160, error) {
s, err := cmd.Flags().GetString(containerContractFlag) s, err := cmd.Flags().GetString(containerContractFlag)
var ch util.Uint160 var ch util.Uint160
if err == nil { if err == nil {
ch, err = util.Uint160DecodeStringLE(s) ch, err = util.Uint160DecodeStringLE(s)
} }
if err != nil { if err != nil {
r := management.NewReader(inv) nnsCs, err := c.GetContractStateByID(1)
nnsCs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err) return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
} }
@ -80,7 +78,7 @@ func dumpContainers(cmd *cobra.Command, _ []string) error {
inv := invoker.New(c, nil) inv := invoker.New(c, nil)
ch, err := getContainerContractHash(cmd, inv) ch, err := getContainerContractHash(cmd, inv, c)
if err != nil { if err != nil {
return fmt.Errorf("unable to get contaract hash: %w", err) return fmt.Errorf("unable to get contaract hash: %w", err)
} }
@ -170,7 +168,7 @@ func listContainers(cmd *cobra.Command, _ []string) error {
inv := invoker.New(c, nil) inv := invoker.New(c, nil)
ch, err := getContainerContractHash(cmd, inv) ch, err := getContainerContractHash(cmd, inv, c)
if err != nil { if err != nil {
return fmt.Errorf("unable to get contaract hash: %w", err) return fmt.Errorf("unable to get contaract hash: %w", err)
} }
@ -300,8 +298,7 @@ func parseContainers(filename string) ([]Container, error) {
} }
func fetchContainerContractHash(wCtx *initializeContext) (util.Uint160, error) { func fetchContainerContractHash(wCtx *initializeContext) (util.Uint160, error) {
r := management.NewReader(wCtx.ReadOnlyInvoker) nnsCs, err := wCtx.Client.GetContractStateByID(1)
nnsCs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err) return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
} }

View file

@ -76,8 +76,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
return err return err
} }
r := management.NewReader(c.ReadOnlyInvoker) nnsCs, err := c.Client.GetContractStateByID(1)
nnsCs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return fmt.Errorf("can't fetch NNS contract state: %w", err) return fmt.Errorf("can't fetch NNS contract state: %w", err)
} }

View file

@ -11,7 +11,6 @@ import (
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client" morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -37,8 +36,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("can't create N3 client: %w", err) return fmt.Errorf("can't create N3 client: %w", err)
} }
r := management.NewReader(invoker.New(c, nil)) cs, err := c.GetContractStateByID(1)
cs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return err return err
} }

View file

@ -6,7 +6,6 @@ import (
"strings" "strings"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -21,8 +20,7 @@ func forceNewEpochCmd(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("can't to initialize context: %w", err) return fmt.Errorf("can't to initialize context: %w", err)
} }
r := management.NewReader(wCtx.ReadOnlyInvoker) cs, err := wCtx.Client.GetContractStateByID(1)
cs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return fmt.Errorf("can't get NNS contract info: %w", err) return fmt.Errorf("can't get NNS contract info: %w", err)
} }

View file

@ -1,42 +0,0 @@
package morph
import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/spf13/viper"
)
// neo-go doesn't support []util.Uint160 type:
// https://github.com/nspcc-dev/neo-go/blob/v0.103.0/pkg/smartcontract/parameter.go#L262
// Thus, return []smartcontract.Parameter.
func getFrostfsIDAuthorizedKeys(v *viper.Viper, defaultOwner util.Uint160) ([]smartcontract.Parameter, error) {
var res []smartcontract.Parameter
res = append(res, smartcontract.Parameter{Type: smartcontract.Hash160Type, Value: defaultOwner})
ks := v.GetStringSlice(frostfsIDAuthorizedKeysConfigKey)
for i := range ks {
h, err := address.StringToUint160(ks[i])
if err == nil {
res = append(res, smartcontract.Parameter{Type: smartcontract.Hash160Type, Value: h})
continue
}
h, err = util.Uint160DecodeStringLE(ks[i])
if err == nil {
res = append(res, smartcontract.Parameter{Type: smartcontract.Hash160Type, Value: h})
continue
}
pk, err := keys.NewPublicKeyFromString(ks[i])
if err == nil {
res = append(res, smartcontract.Parameter{Type: smartcontract.Hash160Type, Value: pk.GetScriptHash()})
continue
}
return nil, fmt.Errorf("frostfsid: #%d item in authorized_keys is invalid: '%s'", i, ks[i])
}
return res, nil
}

View file

@ -1,49 +0,0 @@
package morph
import (
"encoding/hex"
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
func TestFrostfsIDConfig(t *testing.T) {
pks := make([]*keys.PrivateKey, 4)
for i := range pks {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
pks[i] = pk
}
v := viper.New()
v.Set("frostfsid.authorized_keys", []string{
pks[0].GetScriptHash().StringLE(),
address.Uint160ToString(pks[1].GetScriptHash()),
hex.EncodeToString(pks[2].PublicKey().UncompressedBytes()),
hex.EncodeToString(pks[3].PublicKey().Bytes()),
})
comm := util.Uint160{1, 2, 3}
actual, err := getFrostfsIDAuthorizedKeys(v, comm)
require.NoError(t, err)
require.Equal(t, len(pks)+1, len(actual))
require.Equal(t, smartcontract.Hash160Type, actual[0].Type)
require.Equal(t, comm, actual[0].Value)
for i := range pks {
require.Equal(t, smartcontract.Hash160Type, actual[i+1].Type)
require.Equal(t, pks[i].GetScriptHash(), actual[i+1].Value)
}
t.Run("bad key", func(t *testing.T) {
v := viper.New()
v.Set("frostfsid.authorized_keys", []string{"abc"})
_, err := getFrostfsIDAuthorizedKeys(v, comm)
require.Error(t, err)
})
}

View file

@ -76,7 +76,7 @@ func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, er
} }
p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json") p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json")
f, err := os.OpenFile(p, os.O_CREATE, 0o644) f, err := os.OpenFile(p, os.O_CREATE, 0644)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't create wallet file: %w", err) return nil, fmt.Errorf("can't create wallet file: %w", err)
} }

View file

@ -7,7 +7,6 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client" morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
@ -16,7 +15,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
@ -314,8 +312,7 @@ func (c *initializeContext) nnsContractState() (*state.Contract, error) {
return c.nnsCs, nil return c.nnsCs, nil
} }
r := management.NewReader(c.ReadOnlyInvoker) cs, err := c.Client.GetContractStateByID(1)
cs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -377,18 +374,36 @@ func (c *clientContext) awaitTx(cmd *cobra.Command) error {
func awaitTx(cmd *cobra.Command, c Client, txs []hashVUBPair) error { func awaitTx(cmd *cobra.Command, c Client, txs []hashVUBPair) error {
cmd.Println("Waiting for transactions to persist...") cmd.Println("Waiting for transactions to persist...")
const pollInterval = time.Second
tick := time.NewTicker(pollInterval)
defer tick.Stop()
at := trigger.Application at := trigger.Application
var retErr error var retErr error
currBlock, err := c.GetBlockCount()
if err != nil {
return fmt.Errorf("can't fetch current block height: %w", err)
}
loop: loop:
for i := range txs { for i := range txs {
var it int res, err := c.GetApplicationLog(txs[i].hash, &at)
var pollInterval time.Duration if err == nil {
var pollIntervalChanged bool if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vmstate.Halt {
for { retErr = fmt.Errorf("tx %d persisted in %s state: %s",
i, res.Executions[0].VMState, res.Executions[0].FaultException)
}
continue loop
}
if txs[i].vub < currBlock {
return fmt.Errorf("tx was not persisted: vub=%d, height=%d", txs[i].vub, currBlock)
}
for range tick.C {
// We must fetch current height before application log, to avoid race condition. // We must fetch current height before application log, to avoid race condition.
currBlock, err := c.GetBlockCount() currBlock, err = c.GetBlockCount()
if err != nil { if err != nil {
return fmt.Errorf("can't fetch current block height: %w", err) return fmt.Errorf("can't fetch current block height: %w", err)
} }
@ -403,43 +418,12 @@ loop:
if txs[i].vub < currBlock { if txs[i].vub < currBlock {
return fmt.Errorf("tx was not persisted: vub=%d, height=%d", txs[i].vub, currBlock) return fmt.Errorf("tx was not persisted: vub=%d, height=%d", txs[i].vub, currBlock)
} }
pollInterval, pollIntervalChanged = nextPollInterval(it, pollInterval)
if pollIntervalChanged && viper.GetBool(commonflags.Verbose) {
cmd.Printf("Pool interval to check transaction persistence changed: %s\n", pollInterval.String())
}
timer := time.NewTimer(pollInterval)
select {
case <-cmd.Context().Done():
return cmd.Context().Err()
case <-timer.C:
}
it++
} }
} }
return retErr return retErr
} }
func nextPollInterval(it int, previous time.Duration) (time.Duration, bool) {
const minPollInterval = 1 * time.Second
const maxPollInterval = 16 * time.Second
const changeAfter = 5
if it == 0 {
return minPollInterval, true
}
if it%changeAfter != 0 {
return previous, false
}
nextInterval := previous * 2
if nextInterval > maxPollInterval {
return maxPollInterval, previous != maxPollInterval
}
return nextInterval, true
}
// sendCommitteeTx creates transaction from script, signs it by committee nodes and sends it to RPC. // sendCommitteeTx creates transaction from script, signs it by committee nodes and sends it to RPC.
// If tryGroup is false, global scope is used for the signer (useful when // If tryGroup is false, global scope is used for the signer (useful when
// working with native contracts). // working with native contracts).

View file

@ -18,8 +18,10 @@ import (
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client" morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
io2 "github.com/nspcc-dev/neo-go/pkg/io" io2 "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management" "github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
@ -31,7 +33,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/spf13/viper" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
) )
const ( const (
@ -43,19 +45,15 @@ const (
containerContract = "container" containerContract = "container"
frostfsIDContract = "frostfsid" frostfsIDContract = "frostfsid"
netmapContract = "netmap" netmapContract = "netmap"
policyContract = "policy"
proxyContract = "proxy" proxyContract = "proxy"
) )
const frostfsIDAuthorizedKeysConfigKey = "frostfsid.authorized_keys"
var ( var (
contractList = []string{ contractList = []string{
balanceContract, balanceContract,
containerContract, containerContract,
frostfsIDContract, frostfsIDContract,
netmapContract, netmapContract,
policyContract,
proxyContract, proxyContract,
} }
@ -96,10 +94,7 @@ func (c *initializeContext) deployNNS(method string) error {
h := cs.Hash h := cs.Hash
nnsCs, err := c.nnsContractState() nnsCs, err := c.nnsContractState()
if err != nil { if err == nil {
return err
}
if nnsCs != nil {
if nnsCs.NEF.Checksum == cs.NEF.Checksum { if nnsCs.NEF.Checksum == cs.NEF.Checksum {
if method == deployMethodName { if method == deployMethodName {
c.Command.Println("NNS contract is already deployed.") c.Command.Println("NNS contract is already deployed.")
@ -117,13 +112,28 @@ func (c *initializeContext) deployNNS(method string) error {
} }
params := getContractDeployParameters(cs, nil) params := getContractDeployParameters(cs, nil)
signer := transaction.Signer{
Account: c.CommitteeAcc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
}
invokeHash := management.Hash invokeHash := management.Hash
if method == updateMethodName { if method == updateMethodName {
invokeHash = nnsCs.Hash invokeHash = nnsCs.Hash
} }
tx, err := c.CommitteeAct.MakeCall(invokeHash, method, params...) res, err := invokeFunction(c.Client, invokeHash, method, params, []transaction.Signer{signer})
if err != nil {
return fmt.Errorf("can't deploy NNS contract: %w", err)
}
if res.State != vmstate.Halt.String() {
return fmt.Errorf("can't deploy NNS contract: %s", res.FaultException)
}
tx, err := c.Client.CreateTxFromScript(res.Script, c.CommitteeAcc, res.GasConsumed, 0, []rpcclient.SignerAccount{{
Signer: signer,
Account: c.CommitteeAcc,
}})
if err != nil { if err != nil {
return fmt.Errorf("failed to create deploy tx for %s: %w", nnsContract, err) return fmt.Errorf("failed to create deploy tx for %s: %w", nnsContract, err)
} }
@ -356,9 +366,8 @@ func (c *initializeContext) deployContracts() error {
} }
func (c *initializeContext) isUpdated(ctrHash util.Uint160, cs *contractState) bool { func (c *initializeContext) isUpdated(ctrHash util.Uint160, cs *contractState) bool {
r := management.NewReader(c.ReadOnlyInvoker) realCs, err := c.Client.GetContractStateByHash(ctrHash)
realCs, err := r.GetContract(ctrHash) return err == nil && realCs.NEF.Checksum == cs.NEF.Checksum
return err == nil && realCs != nil && realCs.NEF.Checksum == cs.NEF.Checksum
} }
func (c *initializeContext) getContract(ctrName string) *contractState { func (c *initializeContext) getContract(ctrName string) *contractState {
@ -508,7 +517,8 @@ func getContractDeployParameters(cs *contractState, deployData []any) []any {
} }
func (c *initializeContext) getContractDeployData(ctrName string, keysParam []any, method string) []any { func (c *initializeContext) getContractDeployData(ctrName string, keysParam []any, method string) []any {
items := make([]any, 0, 6) items := make([]any, 1, 6)
items[0] = false // notaryDisabled is false
switch ctrName { switch ctrName {
case frostfsContract: case frostfsContract:
@ -526,8 +536,7 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an
case containerContract: case containerContract:
// In case if NNS is updated multiple times, we can't calculate // In case if NNS is updated multiple times, we can't calculate
// it's actual hash based on local data, thus query chain. // it's actual hash based on local data, thus query chain.
r := management.NewReader(c.ReadOnlyInvoker) nnsCs, err := c.Client.GetContractStateByID(1)
nnsCs, err := r.GetContractByID(1)
if err != nil { if err != nil {
panic("NNS is not yet deployed") panic("NNS is not yet deployed")
} }
@ -538,12 +547,9 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an
nnsCs.Hash, nnsCs.Hash,
"container") "container")
case frostfsIDContract: case frostfsIDContract:
hs, err := getFrostfsIDAuthorizedKeys(viper.GetViper(), c.CommitteeAcc.PublicKey().GetScriptHash()) items = append(items,
if err != nil { c.Contracts[netmapContract].Hash,
panic(err) c.Contracts[containerContract].Hash)
}
items = append(items, hs)
case netmapContract: case netmapContract:
md := getDefaultNetmapContractConfigMap() md := getDefaultNetmapContractConfigMap()
if method == updateMethodName { if method == updateMethodName {
@ -577,8 +583,6 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an
configParam) configParam)
case proxyContract: case proxyContract:
items = nil items = nil
case policyContract:
items = []any{nil}
default: default:
panic(fmt.Sprintf("invalid contract name: %s", ctrName)) panic(fmt.Sprintf("invalid contract name: %s", ctrName))
} }
@ -586,8 +590,7 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []an
} }
func (c *initializeContext) getNetConfigFromNetmapContract() ([]stackitem.Item, error) { func (c *initializeContext) getNetConfigFromNetmapContract() ([]stackitem.Item, error) {
r := management.NewReader(c.ReadOnlyInvoker) cs, err := c.Client.GetContractStateByID(1)
cs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return nil, fmt.Errorf("NNS is not yet deployed: %w", err) return nil, fmt.Errorf("NNS is not yet deployed: %w", err)
} }
@ -603,11 +606,12 @@ func (c *initializeContext) getNetConfigFromNetmapContract() ([]stackitem.Item,
} }
func (c *initializeContext) getAlphabetDeployItems(i, n int) []any { func (c *initializeContext) getAlphabetDeployItems(i, n int) []any {
items := make([]any, 5) items := make([]any, 6)
items[0] = c.Contracts[netmapContract].Hash items[0] = false
items[1] = c.Contracts[proxyContract].Hash items[1] = c.Contracts[netmapContract].Hash
items[2] = innerring.GlagoliticLetter(i).String() items[2] = c.Contracts[proxyContract].Hash
items[3] = int64(i) items[3] = innerring.GlagoliticLetter(i).String()
items[4] = int64(n) items[4] = int64(i)
items[5] = int64(n)
return items return items
} }

View file

@ -15,7 +15,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
nnsClient "github.com/nspcc-dev/neo-go/pkg/rpcclient/nns" nnsClient "github.com/nspcc-dev/neo-go/pkg/rpcclient/nns"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
@ -31,8 +30,7 @@ const defaultExpirationTime = 10 * 365 * 24 * time.Hour / time.Second
const frostfsOpsEmail = "ops@frostfs.info" const frostfsOpsEmail = "ops@frostfs.info"
func (c *initializeContext) setNNS() error { func (c *initializeContext) setNNS() error {
r := management.NewReader(c.ReadOnlyInvoker) nnsCs, err := c.Client.GetContractStateByID(1)
nnsCs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return err return err
} }

View file

@ -3,17 +3,14 @@ package morph
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -44,12 +41,12 @@ func (c *initializeContext) registerCandidateRange(start, end int) error {
panic(fmt.Sprintf("BUG: %v", w.Err)) panic(fmt.Sprintf("BUG: %v", w.Err))
} }
signers := []actor.SignerAccount{{ signers := []rpcclient.SignerAccount{{
Signer: c.getSigner(false, c.CommitteeAcc), Signer: c.getSigner(false, c.CommitteeAcc),
Account: c.CommitteeAcc, Account: c.CommitteeAcc,
}} }}
for _, acc := range c.Accounts[start:end] { for _, acc := range c.Accounts[start:end] {
signers = append(signers, actor.SignerAccount{ signers = append(signers, rpcclient.SignerAccount{
Signer: transaction.Signer{ Signer: transaction.Signer{
Account: acc.Contract.ScriptHash(), Account: acc.Contract.ScriptHash(),
Scopes: transaction.CustomContracts, Scopes: transaction.CustomContracts,
@ -59,11 +56,7 @@ func (c *initializeContext) registerCandidateRange(start, end int) error {
}) })
} }
act, err := actor.New(c.Client, signers) tx, err := c.Client.CreateTxFromScript(w.Bytes(), c.CommitteeAcc, -1, 0, signers)
if err != nil {
return fmt.Errorf("can't create actor: %w", err)
}
tx, err := act.MakeRun(w.Bytes())
if err != nil { if err != nil {
return fmt.Errorf("can't create tx: %w", err) return fmt.Errorf("can't create tx: %w", err)
} }
@ -141,9 +134,8 @@ func (c *initializeContext) transferNEOToAlphabetContracts() error {
} }
func (c *initializeContext) transferNEOFinished(neoHash util.Uint160) (bool, error) { func (c *initializeContext) transferNEOFinished(neoHash util.Uint160) (bool, error) {
r := nep17.NewReader(c.ReadOnlyInvoker, neoHash) bal, err := c.Client.NEP17BalanceOf(neoHash, c.CommitteeAcc.Contract.ScriptHash())
bal, err := r.BalanceOf(c.CommitteeAcc.Contract.ScriptHash()) return bal < native.NEOTotalSupply, err
return bal.Cmp(big.NewInt(native.NEOTotalSupply)) == -1, err
} }
var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response") var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response")

View file

@ -20,7 +20,7 @@ import (
) )
const ( const (
contractsPath = "../../../../../../contract/frostfs-contract-v0.18.0.tar.gz" contractsPath = "../../../../../../frostfs-contract/frostfs-contract-v0.16.0.tar.gz"
protoFileName = "proto.yml" protoFileName = "proto.yml"
) )
@ -58,9 +58,7 @@ func testInitialize(t *testing.T, committeeSize int) {
// Set to the path or remove the next statement to download from the network. // Set to the path or remove the next statement to download from the network.
require.NoError(t, initCmd.Flags().Set(contractsInitFlag, contractsPath)) require.NoError(t, initCmd.Flags().Set(contractsInitFlag, contractsPath))
v.Set(localDumpFlag, filepath.Join(testdataDir, "out"))
dumpPath := filepath.Join(testdataDir, "out")
require.NoError(t, initCmd.Flags().Set(localDumpFlag, dumpPath))
v.Set(alphabetWalletsFlag, testdataDir) v.Set(alphabetWalletsFlag, testdataDir)
v.Set(epochDurationInitFlag, 1) v.Set(epochDurationInitFlag, 1)
v.Set(maxObjectSizeInitFlag, 1024) v.Set(maxObjectSizeInitFlag, 1024)
@ -69,15 +67,12 @@ func testInitialize(t *testing.T, committeeSize int) {
require.NoError(t, initializeSideChainCmd(initCmd, nil)) require.NoError(t, initializeSideChainCmd(initCmd, nil))
t.Run("force-new-epoch", func(t *testing.T) { t.Run("force-new-epoch", func(t *testing.T) {
require.NoError(t, forceNewEpoch.Flags().Set(localDumpFlag, dumpPath))
require.NoError(t, forceNewEpochCmd(forceNewEpoch, nil)) require.NoError(t, forceNewEpochCmd(forceNewEpoch, nil))
}) })
t.Run("set-config", func(t *testing.T) { t.Run("set-config", func(t *testing.T) {
require.NoError(t, setConfig.Flags().Set(localDumpFlag, dumpPath))
require.NoError(t, setConfigCmd(setConfig, []string{"MaintenanceModeAllowed=true"})) require.NoError(t, setConfigCmd(setConfig, []string{"MaintenanceModeAllowed=true"}))
}) })
t.Run("set-policy", func(t *testing.T) { t.Run("set-policy", func(t *testing.T) {
require.NoError(t, setPolicy.Flags().Set(localDumpFlag, dumpPath))
require.NoError(t, setPolicyCmd(setPolicy, []string{"ExecFeeFactor=1"})) require.NoError(t, setPolicyCmd(setPolicy, []string{"ExecFeeFactor=1"}))
}) })
t.Run("remove-node", func(t *testing.T) { t.Run("remove-node", func(t *testing.T) {
@ -85,7 +80,6 @@ func testInitialize(t *testing.T, committeeSize int) {
require.NoError(t, err) require.NoError(t, err)
pub := hex.EncodeToString(pk.PublicKey().Bytes()) pub := hex.EncodeToString(pk.PublicKey().Bytes())
require.NoError(t, removeNodes.Flags().Set(localDumpFlag, dumpPath))
require.NoError(t, removeNodesCmd(removeNodes, []string{pub})) require.NoError(t, removeNodesCmd(removeNodes, []string{pub}))
}) })
} }
@ -145,38 +139,3 @@ func setTestCredentials(v *viper.Viper, size int) {
} }
v.Set("credentials.contract", testContractPassword) v.Set("credentials.contract", testContractPassword)
} }
func TestNextPollInterval(t *testing.T) {
var pollInterval time.Duration
var iteration int
pollInterval, hasChanged := nextPollInterval(iteration, pollInterval)
require.True(t, hasChanged)
require.Equal(t, time.Second, pollInterval)
iteration = 4
pollInterval, hasChanged = nextPollInterval(iteration, pollInterval)
require.False(t, hasChanged)
require.Equal(t, time.Second, pollInterval)
iteration = 5
pollInterval, hasChanged = nextPollInterval(iteration, pollInterval)
require.True(t, hasChanged)
require.Equal(t, 2*time.Second, pollInterval)
iteration = 10
pollInterval, hasChanged = nextPollInterval(iteration, pollInterval)
require.True(t, hasChanged)
require.Equal(t, 4*time.Second, pollInterval)
iteration = 20
pollInterval = 32 * time.Second
pollInterval, hasChanged = nextPollInterval(iteration, pollInterval)
require.True(t, hasChanged) // from 32s to 16s
require.Equal(t, 16*time.Second, pollInterval)
pollInterval = 16 * time.Second
pollInterval, hasChanged = nextPollInterval(iteration, pollInterval)
require.False(t, hasChanged)
require.Equal(t, 16*time.Second, pollInterval)
}

View file

@ -2,18 +2,15 @@ package morph
import ( import (
"fmt" "fmt"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
scContext "github.com/nspcc-dev/neo-go/pkg/smartcontract/context" scContext "github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
@ -36,11 +33,11 @@ func (c *initializeContext) transferFunds() error {
return err return err
} }
var transfers []transferTarget var transfers []rpcclient.TransferTarget
for _, acc := range c.Accounts { for _, acc := range c.Accounts {
to := acc.Contract.ScriptHash() to := acc.Contract.ScriptHash()
transfers = append(transfers, transfers = append(transfers,
transferTarget{ rpcclient.TransferTarget{
Token: gas.Hash, Token: gas.Hash,
Address: to, Address: to,
Amount: initialAlphabetGASAmount, Amount: initialAlphabetGASAmount,
@ -50,19 +47,25 @@ func (c *initializeContext) transferFunds() error {
// It is convenient to have all funds at the committee account. // It is convenient to have all funds at the committee account.
transfers = append(transfers, transfers = append(transfers,
transferTarget{ rpcclient.TransferTarget{
Token: gas.Hash, Token: gas.Hash,
Address: c.CommitteeAcc.Contract.ScriptHash(), Address: c.CommitteeAcc.Contract.ScriptHash(),
Amount: (gasInitialTotalSupply - initialAlphabetGASAmount*int64(len(c.Wallets))) / 2, Amount: (gasInitialTotalSupply - initialAlphabetGASAmount*int64(len(c.Wallets))) / 2,
}, },
transferTarget{ rpcclient.TransferTarget{
Token: neo.Hash, Token: neo.Hash,
Address: c.CommitteeAcc.Contract.ScriptHash(), Address: c.CommitteeAcc.Contract.ScriptHash(),
Amount: native.NEOTotalSupply, Amount: native.NEOTotalSupply,
}, },
) )
tx, err := createNEP17MultiTransferTx(c.Client, c.ConsensusAcc, transfers) tx, err := createNEP17MultiTransferTx(c.Client, c.ConsensusAcc, 0, transfers, []rpcclient.SignerAccount{{
Signer: transaction.Signer{
Account: c.ConsensusAcc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: c.ConsensusAcc,
}})
if err != nil { if err != nil {
return fmt.Errorf("can't create transfer transaction: %w", err) return fmt.Errorf("can't create transfer transaction: %w", err)
} }
@ -77,9 +80,8 @@ func (c *initializeContext) transferFunds() error {
func (c *initializeContext) transferFundsFinished() (bool, error) { func (c *initializeContext) transferFundsFinished() (bool, error) {
acc := c.Accounts[0] acc := c.Accounts[0]
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash) res, err := c.Client.NEP17BalanceOf(gas.Hash, acc.Contract.ScriptHash())
res, err := r.BalanceOf(acc.Contract.ScriptHash()) return res > initialAlphabetGASAmount/2, err
return res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) == 1, err
} }
func (c *initializeContext) multiSignAndSend(tx *transaction.Transaction, accType string) error { func (c *initializeContext) multiSignAndSend(tx *transaction.Transaction, accType string) error {
@ -91,13 +93,12 @@ func (c *initializeContext) multiSignAndSend(tx *transaction.Transaction, accTyp
} }
func (c *initializeContext) multiSign(tx *transaction.Transaction, accType string) error { func (c *initializeContext) multiSign(tx *transaction.Transaction, accType string) error {
version, err := c.Client.GetVersion() network, err := c.Client.GetNetwork()
if err != nil { if err != nil {
// error appears only if client // error appears only if client
// has not been initialized // has not been initialized
panic(err) panic(err)
} }
network := version.Protocol.Network
// Use parameter context to avoid dealing with signature order. // Use parameter context to avoid dealing with signature order.
pc := scContext.NewParameterContext("", network, tx) pc := scContext.NewParameterContext("", network, tx)
@ -145,17 +146,16 @@ func (c *initializeContext) multiSign(tx *transaction.Transaction, accType strin
func (c *initializeContext) transferGASToProxy() error { func (c *initializeContext) transferGASToProxy() error {
proxyCs := c.getContract(proxyContract) proxyCs := c.getContract(proxyContract)
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash) bal, err := c.Client.NEP17BalanceOf(gas.Hash, proxyCs.Hash)
bal, err := r.BalanceOf(proxyCs.Hash) if err != nil || bal > 0 {
if err != nil || bal.Sign() > 0 {
return err return err
} }
tx, err := createNEP17MultiTransferTx(c.Client, c.CommitteeAcc, []transferTarget{{ tx, err := createNEP17MultiTransferTx(c.Client, c.CommitteeAcc, 0, []rpcclient.TransferTarget{{
Token: gas.Hash, Token: gas.Hash,
Address: proxyCs.Hash, Address: proxyCs.Hash,
Amount: initialProxyGASAmount, Amount: initialProxyGASAmount,
}}) }}, nil)
if err != nil { if err != nil {
return err return err
} }
@ -167,14 +167,8 @@ func (c *initializeContext) transferGASToProxy() error {
return c.awaitTx() return c.awaitTx()
} }
type transferTarget struct { func createNEP17MultiTransferTx(c Client, acc *wallet.Account, netFee int64,
Token util.Uint160 recipients []rpcclient.TransferTarget, cosigners []rpcclient.SignerAccount) (*transaction.Transaction, error) {
Address util.Uint160
Amount int64
Data any
}
func createNEP17MultiTransferTx(c Client, acc *wallet.Account, recipients []transferTarget) (*transaction.Transaction, error) {
from := acc.Contract.ScriptHash() from := acc.Contract.ScriptHash()
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
@ -186,18 +180,11 @@ func createNEP17MultiTransferTx(c Client, acc *wallet.Account, recipients []tran
if w.Err != nil { if w.Err != nil {
return nil, fmt.Errorf("failed to create transfer script: %w", w.Err) return nil, fmt.Errorf("failed to create transfer script: %w", w.Err)
} }
return c.CreateTxFromScript(w.Bytes(), acc, -1, netFee, append([]rpcclient.SignerAccount{{
signers := []actor.SignerAccount{{
Signer: transaction.Signer{ Signer: transaction.Signer{
Account: acc.Contract.ScriptHash(), Account: from,
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
}, },
Account: acc, Account: acc,
}} }}, cosigners...))
act, err := actor.New(c, signers)
if err != nil {
return nil, fmt.Errorf("can't create actor: %w", err)
}
return act.MakeRun(w.Bytes())
} }

View file

@ -10,28 +10,33 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/chaindump" "github.com/nspcc-dev/neo-go/pkg/core/chaindump"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -83,7 +88,7 @@ func newLocalClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet
go bc.Run() go bc.Run()
if cmd.Name() != "init" { if cmd.Name() != "init" {
f, err := os.OpenFile(dumpPath, os.O_RDONLY, 0o600) f, err := os.OpenFile(dumpPath, os.O_RDONLY, 0600)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't open local dump: %w", err) return nil, fmt.Errorf("can't open local dump: %w", err)
} }
@ -114,10 +119,29 @@ func (l *localClient) GetBlockCount() (uint32, error) {
return l.bc.BlockHeight(), nil return l.bc.BlockHeight(), nil
} }
func (l *localClient) GetContractStateByID(id int32) (*state.Contract, error) {
h, err := l.bc.GetContractScriptHash(id)
if err != nil {
return nil, err
}
return l.GetContractStateByHash(h)
}
func (l *localClient) GetContractStateByHash(h util.Uint160) (*state.Contract, error) {
if cs := l.bc.GetContractState(h); cs != nil {
return cs, nil
}
return nil, storage.ErrKeyNotFound
}
func (l *localClient) GetNativeContracts() ([]state.NativeContract, error) { func (l *localClient) GetNativeContracts() ([]state.NativeContract, error) {
return l.bc.GetNatives(), nil return l.bc.GetNatives(), nil
} }
func (l *localClient) GetNetwork() (netmode.Magic, error) {
return l.bc.GetConfig().Magic, nil
}
func (l *localClient) GetApplicationLog(h util.Uint256, t *trigger.Type) (*result.ApplicationLog, error) { func (l *localClient) GetApplicationLog(h util.Uint256, t *trigger.Type) (*result.ApplicationLog, error) {
aer, err := l.bc.GetAppExecResults(h, *t) aer, err := l.bc.GetAppExecResults(h, *t)
if err != nil { if err != nil {
@ -128,6 +152,34 @@ func (l *localClient) GetApplicationLog(h util.Uint256, t *trigger.Type) (*resul
return &a, nil return &a, nil
} }
func (l *localClient) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee int64, netFee int64, cosigners []rpcclient.SignerAccount) (*transaction.Transaction, error) {
signers, accounts, err := getSigners(acc, cosigners)
if err != nil {
return nil, fmt.Errorf("failed to construct tx signers: %w", err)
}
if sysFee < 0 {
res, err := l.InvokeScript(script, signers)
if err != nil {
return nil, fmt.Errorf("can't add system fee to transaction: %w", err)
}
if res.State != "HALT" {
return nil, fmt.Errorf("can't add system fee to transaction: bad vm state: %s due to an error: %s", res.State, res.FaultException)
}
sysFee = res.GasConsumed
}
tx := transaction.New(script, sysFee)
tx.Signers = signers
tx.ValidUntilBlock = l.bc.BlockHeight() + 2
err = l.AddNetworkFee(tx, netFee, accounts...)
if err != nil {
return nil, fmt.Errorf("failed to add network fee: %w", err)
}
return tx, nil
}
func (l *localClient) GetCommittee() (keys.PublicKeys, error) { func (l *localClient) GetCommittee() (keys.PublicKeys, error) {
// not used by `morph init` command // not used by `morph init` command
panic("unexpected call") panic("unexpected call")
@ -148,6 +200,21 @@ func (l *localClient) InvokeFunction(h util.Uint160, method string, sPrm []smart
return invokeFunction(l, h, method, pp, ss) return invokeFunction(l, h, method, pp, ss)
} }
func (l *localClient) CalculateNotaryFee(_ uint8) (int64, error) {
// not used by `morph init` command
panic("unexpected call")
}
func (l *localClient) SignAndPushP2PNotaryRequest(_ *transaction.Transaction, _ []byte, _ int64, _ int64, _ uint32, _ *wallet.Account) (*payload.P2PNotaryRequest, error) {
// not used by `morph init` command
panic("unexpected call")
}
func (l *localClient) SignAndPushInvocationTx(_ []byte, _ *wallet.Account, _ int64, _ fixedn.Fixed8, _ []rpcclient.SignerAccount) (util.Uint256, error) {
// not used by `morph init` command
panic("unexpected call")
}
func (l *localClient) TerminateSession(_ uuid.UUID) (bool, error) { func (l *localClient) TerminateSession(_ uuid.UUID) (bool, error) {
// not used by `morph init` command // not used by `morph init` command
panic("unexpected call") panic("unexpected call")
@ -187,78 +254,112 @@ func (l *localClient) InvokeContractVerify(util.Uint160, []smartcontract.Paramet
// CalculateNetworkFee calculates network fee for the given transaction. // CalculateNetworkFee calculates network fee for the given transaction.
// Copied from neo-go with minor corrections (no need to support non-notary mode): // Copied from neo-go with minor corrections (no need to support non-notary mode):
// https://github.com/nspcc-dev/neo-go/blob/v0.103.0/pkg/services/rpcsrv/server.go#L911 // https://github.com/nspcc-dev/neo-go/blob/v0.99.2/pkg/services/rpcsrv/server.go#L744
func (l *localClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) { func (l *localClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) {
// Avoid setting hash for this tx: server code doesn't touch client transaction.
data := tx.Bytes()
tx, err := transaction.NewTransactionFromBytes(data)
if err != nil {
return 0, err
}
hashablePart, err := tx.EncodeHashableFields() hashablePart, err := tx.EncodeHashableFields()
if err != nil { if err != nil {
return 0, err return 0, fmt.Errorf("failed to compute tx size: %w", err)
} }
size := len(hashablePart) + io.GetVarSize(len(tx.Signers)) size := len(hashablePart) + io.GetVarSize(len(tx.Signers))
var ( ef := l.bc.GetBaseExecFee()
netFee int64
// Verification GAS cost can't exceed this policy. var netFee int64
gasLimit = l.bc.GetMaxVerificationGAS()
)
for i, signer := range tx.Signers { for i, signer := range tx.Signers {
w := tx.Scripts[i] var verificationScript []byte
if len(w.InvocationScript) == 0 { // No invocation provided, try to infer one. for _, w := range tx.Scripts {
var paramz []manifest.Parameter if w.VerificationScript != nil && hash.Hash160(w.VerificationScript).Equals(signer.Account) {
if len(w.VerificationScript) == 0 { // Contract-based verification verificationScript = w.VerificationScript
cs := l.bc.GetContractState(signer.Account) break
if cs == nil {
return 0, fmt.Errorf("signer %d has no verification script and no deployed contract", i)
}
md := cs.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
if md == nil || md.ReturnType != smartcontract.BoolType {
return 0, fmt.Errorf("signer %d has no verify method in deployed contract", i)
}
paramz = md.Parameters // Might as well have none params and it's OK.
} else { // Regular signature verification.
if vm.IsSignatureContract(w.VerificationScript) {
paramz = []manifest.Parameter{{Type: smartcontract.SignatureType}}
} else if nSigs, _, ok := vm.ParseMultiSigContract(w.VerificationScript); ok {
paramz = make([]manifest.Parameter, nSigs)
for j := 0; j < nSigs; j++ {
paramz[j] = manifest.Parameter{Type: smartcontract.SignatureType}
}
}
} }
inv := io.NewBufBinWriter()
for _, p := range paramz {
p.Type.EncodeDefaultValue(inv.BinWriter)
}
if inv.Err != nil {
return 0, fmt.Errorf("failed to create dummy invocation script (signer %d): %s", i, inv.Err.Error())
}
w.InvocationScript = inv.Bytes()
} }
gasConsumed, err := l.bc.VerifyWitness(signer.Account, tx, &w, gasLimit) if verificationScript == nil {
if err != nil && !errors.Is(err, core.ErrInvalidSignature) { gasConsumed, err := l.bc.VerifyWitness(signer.Account, tx, &tx.Scripts[i], l.maxGasInvoke)
return 0, err if err != nil {
} return 0, fmt.Errorf("invalid signature: %w", err)
gasLimit -= gasConsumed }
netFee += gasConsumed netFee += gasConsumed
size += io.GetVarSize(w.VerificationScript) + io.GetVarSize(w.InvocationScript) size += io.GetVarSize([]byte{}) + io.GetVarSize(tx.Scripts[i].InvocationScript)
} continue
if l.bc.P2PSigExtensionsEnabled() {
attrs := tx.GetAttributes(transaction.NotaryAssistedT)
if len(attrs) != 0 {
na := attrs[0].Value.(*transaction.NotaryAssisted)
netFee += (int64(na.NKeys) + 1) * l.bc.GetNotaryServiceFeePerKey()
} }
fee, sizeDelta := fee.Calculate(ef, verificationScript)
netFee += fee
size += sizeDelta
} }
fee := l.bc.FeePerByte() fee := l.bc.FeePerByte()
netFee += int64(size) * fee netFee += int64(size) * fee
return netFee, nil return netFee, nil
} }
// AddNetworkFee adds network fee for each witness script and optional extra
// network fee to transaction. `accs` is an array signer's accounts.
// Copied from neo-go with minor corrections (no need to support contract signers):
// https://github.com/nspcc-dev/neo-go/blob/6ff11baa1b9e4c71ef0d1de43b92a8c541ca732c/pkg/rpc/client/rpc.go#L960
func (l *localClient) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs ...*wallet.Account) error {
if len(tx.Signers) != len(accs) {
return errors.New("number of signers must match number of scripts")
}
size := io.GetVarSize(tx)
ef := l.bc.GetBaseExecFee()
for i := range tx.Signers {
netFee, sizeDelta := fee.Calculate(ef, accs[i].Contract.Script)
tx.NetworkFee += netFee
size += sizeDelta
}
tx.NetworkFee += int64(size)*l.bc.FeePerByte() + extraFee
return nil
}
// getSigners returns an array of transaction signers and corresponding accounts from
// given sender and cosigners. If cosigners list already contains sender, the sender
// will be placed at the start of the list.
// Copied from neo-go with minor corrections:
// https://github.com/nspcc-dev/neo-go/blob/6ff11baa1b9e4c71ef0d1de43b92a8c541ca732c/pkg/rpc/client/rpc.go#L735
func getSigners(sender *wallet.Account, cosigners []rpcclient.SignerAccount) ([]transaction.Signer, []*wallet.Account, error) {
var (
signers []transaction.Signer
accounts []*wallet.Account
)
from := sender.Contract.ScriptHash()
s := transaction.Signer{
Account: from,
Scopes: transaction.None,
}
for _, c := range cosigners {
if c.Signer.Account == from {
s = c.Signer
continue
}
signers = append(signers, c.Signer)
accounts = append(accounts, c.Account)
}
signers = append([]transaction.Signer{s}, signers...)
accounts = append([]*wallet.Account{sender}, accounts...)
return signers, accounts, nil
}
func (l *localClient) NEP17BalanceOf(h util.Uint160, acc util.Uint160) (int64, error) {
res, err := invokeFunction(l, h, "balanceOf", []any{acc}, nil)
if err != nil {
return 0, err
}
if res.State != vmstate.Halt.String() || len(res.Stack) == 0 {
return 0, fmt.Errorf("`balance`: invalid response (empty: %t): %s",
len(res.Stack) == 0, res.FaultException)
}
bi, err := res.Stack[0].TryInteger()
if err != nil || !bi.IsInt64() {
return 0, fmt.Errorf("`balance`: invalid response")
}
return bi.Int64(), nil
}
func (l *localClient) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) { func (l *localClient) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
lastBlock, err := l.bc.GetBlock(l.bc.CurrentBlockHash()) lastBlock, err := l.bc.GetBlock(l.bc.CurrentBlockHash())
if err != nil { if err != nil {

View file

@ -6,10 +6,13 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
@ -26,12 +29,21 @@ type Client interface {
invoker.RPCInvoke invoker.RPCInvoke
GetBlockCount() (uint32, error) GetBlockCount() (uint32, error)
GetContractStateByID(int32) (*state.Contract, error)
GetContractStateByHash(util.Uint160) (*state.Contract, error)
GetNativeContracts() ([]state.NativeContract, error) GetNativeContracts() ([]state.NativeContract, error)
GetNetwork() (netmode.Magic, error)
GetApplicationLog(util.Uint256, *trigger.Type) (*result.ApplicationLog, error) GetApplicationLog(util.Uint256, *trigger.Type) (*result.ApplicationLog, error)
GetVersion() (*result.Version, error) GetVersion() (*result.Version, error)
CreateTxFromScript([]byte, *wallet.Account, int64, int64, []rpcclient.SignerAccount) (*transaction.Transaction, error)
NEP17BalanceOf(util.Uint160, util.Uint160) (int64, error)
SendRawTransaction(*transaction.Transaction) (util.Uint256, error) SendRawTransaction(*transaction.Transaction) (util.Uint256, error)
GetCommittee() (keys.PublicKeys, error) GetCommittee() (keys.PublicKeys, error)
CalculateNotaryFee(uint8) (int64, error)
CalculateNetworkFee(tx *transaction.Transaction) (int64, error) CalculateNetworkFee(tx *transaction.Transaction) (int64, error)
AddNetworkFee(*transaction.Transaction, int64, ...*wallet.Account) error
SignAndPushInvocationTx([]byte, *wallet.Account, int64, fixedn.Fixed8, []rpcclient.SignerAccount) (util.Uint256, error)
SignAndPushP2PNotaryRequest(*transaction.Transaction, []byte, int64, int64, uint32, *wallet.Account) (*payload.P2PNotaryRequest, error)
} }
type hashVUBPair struct { type hashVUBPair struct {

View file

@ -5,7 +5,6 @@ import (
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -15,9 +14,8 @@ func listNetmapCandidatesNodes(cmd *cobra.Command, _ []string) {
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err) commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
inv := invoker.New(c, nil) inv := invoker.New(c, nil)
r := management.NewReader(inv)
cs, err := r.GetContractByID(1) cs, err := c.GetContractStateByID(1)
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err) commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
nmHash, err := nnsResolveHash(inv, cs.Hash, netmapContract+".frostfs") nmHash, err := nnsResolveHash(inv, cs.Hash, netmapContract+".frostfs")

View file

@ -1,15 +1,11 @@
package morph package morph
import ( import (
"bytes"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"text/tabwriter"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/policy" "github.com/nspcc-dev/neo-go/pkg/rpcclient/policy"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
@ -56,32 +52,3 @@ func setPolicyCmd(cmd *cobra.Command, args []string) error {
return wCtx.awaitTx() return wCtx.awaitTx()
} }
func dumpPolicyCmd(cmd *cobra.Command, _ []string) error {
c, err := getN3Client(viper.GetViper())
commonCmd.ExitOnErr(cmd, "can't create N3 client:", err)
inv := invoker.New(c, nil)
policyContract := policy.NewReader(inv)
execFee, err := policyContract.GetExecFeeFactor()
commonCmd.ExitOnErr(cmd, "can't get execution fee factor:", err)
feePerByte, err := policyContract.GetFeePerByte()
commonCmd.ExitOnErr(cmd, "can't get fee per byte:", err)
storagePrice, err := policyContract.GetStoragePrice()
commonCmd.ExitOnErr(cmd, "can't get storage price:", err)
buf := bytes.NewBuffer(nil)
tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
_, _ = tw.Write([]byte(fmt.Sprintf("Execution Fee Factor:\t%d (int)\n", execFee)))
_, _ = tw.Write([]byte(fmt.Sprintf("Fee Per Byte:\t%d (int)\n", feePerByte)))
_, _ = tw.Write([]byte(fmt.Sprintf("Storage Price:\t%d (int)\n", storagePrice)))
_ = tw.Flush()
cmd.Print(buf.String())
return nil
}

View file

@ -7,7 +7,6 @@ import (
netmapcontract "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap" netmapcontract "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -34,8 +33,7 @@ func removeNodesCmd(cmd *cobra.Command, args []string) error {
} }
defer wCtx.close() defer wCtx.close()
r := management.NewReader(wCtx.ReadOnlyInvoker) cs, err := wCtx.Client.GetContractStateByID(1)
cs, err := r.GetContractByID(1)
if err != nil { if err != nil {
return fmt.Errorf("can't get NNS contract info: %w", err) return fmt.Errorf("can't get NNS contract info: %w", err)
} }

View file

@ -146,15 +146,6 @@ var (
}, },
} }
dumpPolicy = &cobra.Command{
Use: "dump-policy",
Short: "Dump FrostFS policy",
PreRun: func(cmd *cobra.Command, _ []string) {
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
},
RunE: dumpPolicyCmd,
}
dumpContractHashesCmd = &cobra.Command{ dumpContractHashesCmd = &cobra.Command{
Use: "dump-hashes", Use: "dump-hashes",
Short: "Dump deployed contract hashes", Short: "Dump deployed contract hashes",
@ -248,7 +239,6 @@ func init() {
initForceNewEpochCmd() initForceNewEpochCmd()
initRemoveNodesCmd() initRemoveNodesCmd()
initSetPolicyCmd() initSetPolicyCmd()
initDumpPolicyCmd()
initDumpContractHashesCmd() initDumpContractHashesCmd()
initDumpNetworkConfigCmd() initDumpNetworkConfigCmd()
initSetConfigCmd() initSetConfigCmd()
@ -330,7 +320,6 @@ func initSetConfigCmd() {
setConfig.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir") setConfig.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
setConfig.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") setConfig.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key") setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key")
setConfig.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
} }
func initDumpNetworkConfigCmd() { func initDumpNetworkConfigCmd() {
@ -348,26 +337,18 @@ func initSetPolicyCmd() {
RootCmd.AddCommand(setPolicy) RootCmd.AddCommand(setPolicy)
setPolicy.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir") setPolicy.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
setPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") setPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
setPolicy.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
}
func initDumpPolicyCmd() {
RootCmd.AddCommand(dumpPolicy)
dumpPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
} }
func initRemoveNodesCmd() { func initRemoveNodesCmd() {
RootCmd.AddCommand(removeNodes) RootCmd.AddCommand(removeNodes)
removeNodes.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir") removeNodes.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
removeNodes.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") removeNodes.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
removeNodes.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
} }
func initForceNewEpochCmd() { func initForceNewEpochCmd() {
RootCmd.AddCommand(forceNewEpoch) RootCmd.AddCommand(forceNewEpoch)
forceNewEpoch.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir") forceNewEpoch.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
forceNewEpoch.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") forceNewEpoch.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
forceNewEpoch.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
} }
func initGenerateStorageCmd() { func initGenerateStorageCmd() {

View file

@ -15,14 +15,16 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var rootCmd = &cobra.Command{ var (
Use: "frostfs-adm", rootCmd = &cobra.Command{
Short: "FrostFS Administrative Tool", Use: "frostfs-adm",
Long: `FrostFS Administrative Tool provides functions to setup and Short: "FrostFS Administrative Tool",
Long: `FrostFS Administrative Tool provides functions to setup and
manage FrostFS network deployment.`, manage FrostFS network deployment.`,
RunE: entryPoint, RunE: entryPoint,
SilenceUsage: true, SilenceUsage: true,
} }
)
func init() { func init() {
cobra.OnInitialize(func() { initConfig(rootCmd) }) cobra.OnInitialize(func() { initConfig(rootCmd) })

View file

@ -145,7 +145,7 @@ func storageConfig(cmd *cobra.Command, args []string) {
} }
out := applyTemplate(c) out := applyTemplate(c)
fatalOnErr(os.WriteFile(outPath, out, 0o644)) fatalOnErr(os.WriteFile(outPath, out, 0644))
cmd.Println("Node is ready for work! Run `frostfs-node -config " + outPath + "`") cmd.Println("Node is ready for work! Run `frostfs-node -config " + outPath + "`")
} }

View file

@ -6,8 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"sort"
"strings"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
@ -71,16 +69,6 @@ func ListContainers(ctx context.Context, prm ListContainersPrm) (res ListContain
return return
} }
// SortedIDList returns sorted list of identifiers of user's containers.
func (x ListContainersRes) SortedIDList() []cid.ID {
list := x.cliRes.Containers()
sort.Slice(list, func(i, j int) bool {
lhs, rhs := list[i].EncodeToString(), list[j].EncodeToString()
return strings.Compare(lhs, rhs) < 0
})
return list
}
// PutContainerPrm groups parameters of PutContainer operation. // PutContainerPrm groups parameters of PutContainer operation.
type PutContainerPrm struct { type PutContainerPrm struct {
Client *client.Client Client *client.Client
@ -387,23 +375,30 @@ func (x *PutObjectPrm) PrepareLocally() {
} }
func (x *PutObjectPrm) convertToSDKPrm(ctx context.Context) (client.PrmObjectPutInit, error) { func (x *PutObjectPrm) convertToSDKPrm(ctx context.Context) (client.PrmObjectPutInit, error) {
putPrm := client.PrmObjectPutInit{ var putPrm client.PrmObjectPutInit
XHeaders: x.xHeaders, if !x.prepareLocally && x.sessionToken != nil {
BearerToken: x.bearerToken, putPrm.WithinSession(*x.sessionToken)
Local: x.local,
CopiesNumber: x.copyNum,
} }
if x.bearerToken != nil {
putPrm.WithBearerToken(*x.bearerToken)
}
if x.local {
putPrm.MarkLocal()
}
putPrm.WithXHeaders(x.xHeaders...)
putPrm.SetCopiesNumberByVectors(x.copyNum)
if x.prepareLocally { if x.prepareLocally {
res, err := x.cli.NetworkInfo(ctx, client.PrmNetworkInfo{}) res, err := x.cli.NetworkInfo(ctx, client.PrmNetworkInfo{})
if err != nil { if err != nil {
return client.PrmObjectPutInit{}, err return client.PrmObjectPutInit{}, err
} }
putPrm.MaxSize = res.Info().MaxObjectSize() putPrm.WithObjectMaxSize(res.Info().MaxObjectSize())
putPrm.EpochSource = epochSource(res.Info().CurrentEpoch()) putPrm.WithEpochSource(epochSource(res.Info().CurrentEpoch()))
putPrm.WithoutHomomorphHash = res.Info().HomomorphicHashingDisabled() putPrm.WithoutHomomorphicHash(res.Info().HomomorphicHashingDisabled())
} else {
putPrm.Session = x.sessionToken
} }
return putPrm, nil return putPrm, nil
} }
@ -689,15 +684,24 @@ func (x SearchObjectsRes) IDList() []oid.ID {
// //
// Returns any error which prevented the operation from completing correctly in error return. // Returns any error which prevented the operation from completing correctly in error return.
func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes, error) { func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes, error) {
cliPrm := client.PrmObjectSearch{ var cliPrm client.PrmObjectSearch
XHeaders: prm.xHeaders, cliPrm.InContainer(prm.cnrID)
Local: prm.local, cliPrm.SetFilters(prm.filters)
BearerToken: prm.bearerToken,
Session: prm.sessionToken, if prm.sessionToken != nil {
ContainerID: &prm.cnrID, cliPrm.WithinSession(*prm.sessionToken)
Filters: prm.filters,
} }
if prm.bearerToken != nil {
cliPrm.WithBearerToken(*prm.bearerToken)
}
if prm.local {
cliPrm.MarkLocal()
}
cliPrm.WithXHeaders(prm.xHeaders...)
rdr, err := prm.cli.ObjectSearchInit(ctx, cliPrm) rdr, err := prm.cli.ObjectSearchInit(ctx, cliPrm)
if err != nil { if err != nil {
return nil, fmt.Errorf("init object search: %w", err) return nil, fmt.Errorf("init object search: %w", err)
@ -723,11 +727,6 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
return nil, fmt.Errorf("read object list: %w", err) return nil, fmt.Errorf("read object list: %w", err)
} }
sort.Slice(list, func(i, j int) bool {
lhs, rhs := list[i].EncodeToString(), list[j].EncodeToString()
return strings.Compare(lhs, rhs) < 0
})
return &SearchObjectsRes{ return &SearchObjectsRes{
ids: list, ids: list,
}, nil }, nil

View file

@ -43,28 +43,27 @@ func getSDKClientByFlag(cmd *cobra.Command, key *ecdsa.PrivateKey, endpointFlag
// GetSDKClient returns default frostfs-sdk-go client. // GetSDKClient returns default frostfs-sdk-go client.
func GetSDKClient(ctx context.Context, cmd *cobra.Command, key *ecdsa.PrivateKey, addr network.Address) (*client.Client, error) { func GetSDKClient(ctx context.Context, cmd *cobra.Command, key *ecdsa.PrivateKey, addr network.Address) (*client.Client, error) {
var c client.Client var (
c client.Client
prmInit client.PrmInit
prmDial client.PrmDial
)
prmInit := client.PrmInit{ prmInit.SetDefaultPrivateKey(*key)
Key: *key, prmInit.ResolveFrostFSFailures()
} prmDial.SetServerURI(addr.URIAddr())
prmDial := client.PrmDial{
Endpoint: addr.URIAddr(),
GRPCDialOptions: []grpc.DialOption{
grpc.WithChainUnaryInterceptor(tracing.NewUnaryClientInteceptor()),
grpc.WithChainStreamInterceptor(tracing.NewStreamClientInterceptor()),
},
}
if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 { if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 {
// In CLI we can only set a timeout for the whole operation. // In CLI we can only set a timeout for the whole operation.
// By also setting stream timeout we ensure that no operation hands // By also setting stream timeout we ensure that no operation hands
// for too long. // for too long.
prmDial.DialTimeout = timeout prmDial.SetTimeout(timeout)
prmDial.StreamTimeout = timeout prmDial.SetStreamTimeout(timeout)
common.PrintVerbose(cmd, "Set request timeout to %s.", timeout) common.PrintVerbose(cmd, "Set request timeout to %s.", timeout)
} }
prmDial.SetGRPCDialOptions(
grpc.WithChainUnaryInterceptor(tracing.NewUnaryClientInteceptor()),
grpc.WithChainStreamInterceptor(tracing.NewStreamClientInterceptor()))
c.Init(prmInit) c.Init(prmInit)

View file

@ -39,7 +39,7 @@ var accountingBalanceCmd = &cobra.Command{
var prm internalclient.BalanceOfPrm var prm internalclient.BalanceOfPrm
prm.SetClient(cli) prm.SetClient(cli)
prm.Account = idUser prm.SetAccount(idUser)
res, err := internalclient.BalanceOf(cmd.Context(), prm) res, err := internalclient.BalanceOf(cmd.Context(), prm)
commonCmd.ExitOnErr(cmd, "rpc error: %w", err) commonCmd.ExitOnErr(cmd, "rpc error: %w", err)

View file

@ -1,6 +1,7 @@
package accounting package accounting
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -17,7 +18,9 @@ var Cmd = &cobra.Command{
_ = viper.BindPFlag(commonflags.WalletPath, flags.Lookup(commonflags.WalletPath)) _ = viper.BindPFlag(commonflags.WalletPath, flags.Lookup(commonflags.WalletPath))
_ = viper.BindPFlag(commonflags.Account, flags.Lookup(commonflags.Account)) _ = viper.BindPFlag(commonflags.Account, flags.Lookup(commonflags.Account))
_ = viper.BindPFlag(commonflags.RPC, flags.Lookup(commonflags.RPC)) _ = viper.BindPFlag(commonflags.RPC, flags.Lookup(commonflags.RPC))
common.StartClientCommandSpan(cmd)
}, },
PersistentPostRun: common.StopClientCommandSpan,
} }
func init() { func init() {

View file

@ -106,7 +106,7 @@ func createEACL(cmd *cobra.Command, _ []string) {
return return
} }
err = os.WriteFile(outArg, buf.Bytes(), 0o644) err = os.WriteFile(outArg, buf.Bytes(), 0644)
if err != nil { if err != nil {
cmd.PrintErrln(err) cmd.PrintErrln(err)
os.Exit(1) os.Exit(1)

View file

@ -130,6 +130,6 @@ func createToken(cmd *cobra.Command, _ []string) {
} }
out, _ := cmd.Flags().GetString(outFlag) out, _ := cmd.Flags().GetString(outFlag)
err = os.WriteFile(out, data, 0o644) err = os.WriteFile(out, data, 0644)
commonCmd.ExitOnErr(cmd, "can't write token to file: %w", err) commonCmd.ExitOnErr(cmd, "can't write token to file: %w", err)
} }

View file

@ -51,7 +51,7 @@ var getContainerInfoCmd = &cobra.Command{
data = cnr.Marshal() data = cnr.Marshal()
} }
err = os.WriteFile(containerPathTo, data, 0o644) err = os.WriteFile(containerPathTo, data, 0644)
commonCmd.ExitOnErr(cmd, "can't write container to file: %w", err) commonCmd.ExitOnErr(cmd, "can't write container to file: %w", err)
} }
}, },

View file

@ -52,7 +52,7 @@ var getExtendedACLCmd = &cobra.Command{
cmd.Println("dumping data to file:", containerPathTo) cmd.Println("dumping data to file:", containerPathTo)
err = os.WriteFile(containerPathTo, data, 0o644) err = os.WriteFile(containerPathTo, data, 0644)
commonCmd.ExitOnErr(cmd, "could not write eACL to file: %w", err) commonCmd.ExitOnErr(cmd, "could not write eACL to file: %w", err)
}, },
} }

View file

@ -47,7 +47,7 @@ var listContainersCmd = &cobra.Command{
var prm internalclient.ListContainersPrm var prm internalclient.ListContainersPrm
prm.SetClient(cli) prm.SetClient(cli)
prm.Account = idUser prm.SetAccount(idUser)
res, err := internalclient.ListContainers(cmd.Context(), prm) res, err := internalclient.ListContainers(cmd.Context(), prm)
commonCmd.ExitOnErr(cmd, "rpc error: %w", err) commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
@ -56,7 +56,7 @@ var listContainersCmd = &cobra.Command{
Client: cli, Client: cli,
} }
containerIDs := res.SortedIDList() containerIDs := res.IDList()
for _, cnrID := range containerIDs { for _, cnrID := range containerIDs {
if flagVarListName == "" && !flagVarListPrintAttr { if flagVarListName == "" && !flagVarListPrintAttr {
cmd.Println(cnrID.String()) cmd.Println(cnrID.String())

View file

@ -20,7 +20,7 @@ var containerNodesCmd = &cobra.Command{
Short: "Show nodes for container", Short: "Show nodes for container",
Long: "Show nodes taking part in a container at the current epoch.", Long: "Show nodes taking part in a container at the current epoch.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cnr, pkey := getContainer(cmd) var cnr, pkey = getContainer(cmd)
if pkey == nil { if pkey == nil {
pkey = key.GetOrGenerate(cmd) pkey = key.GetOrGenerate(cmd)

View file

@ -1,6 +1,7 @@
package container package container
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -15,7 +16,9 @@ var Cmd = &cobra.Command{
// the viper before execution // the viper before execution
commonflags.Bind(cmd) commonflags.Bind(cmd)
commonflags.BindAPI(cmd) commonflags.BindAPI(cmd)
common.StartClientCommandSpan(cmd)
}, },
PersistentPostRun: common.StopClientCommandSpan,
} }
func init() { func init() {

View file

@ -1,94 +0,0 @@
package control
import (
"bytes"
"crypto/sha256"
"encoding/json"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"github.com/spf13/cobra"
)
const (
ruleFlag = "rule"
)
var addRuleCmd = &cobra.Command{
Use: "add-rule",
Short: "Add local override",
Long: "Add local APE rule to a node with following format:\n<action>[:action_detail] <operation> [<condition1> ...] <resource>",
Example: `allow Object.Get *
deny Object.Get EbxzAdz5LB4uqxuz6crWKAumBNtZyK2rKsqQP7TdZvwr/*
deny:QuotaLimitReached Object.Put Object.Resource:Department=HR *
`,
Run: addRule,
}
func prettyJSONFormat(cmd *cobra.Command, serializedChain []byte) string {
wr := bytes.NewBufferString("")
err := json.Indent(wr, serializedChain, "", " ")
commonCmd.ExitOnErr(cmd, "%w", err)
return wr.String()
}
func addRule(cmd *cobra.Command, _ []string) {
pk := key.Get(cmd)
chainID, _ := cmd.Flags().GetString(chainIDFlag)
var cnr cid.ID
cidStr, _ := cmd.Flags().GetString(commonflags.CIDFlag)
commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(cidStr))
rawCID := make([]byte, sha256.Size)
cnr.Encode(rawCID)
rule, _ := cmd.Flags().GetString(ruleFlag)
chain := new(apechain.Chain)
commonCmd.ExitOnErr(cmd, "parser error: %w", util.ParseAPEChain(chain, []string{rule}))
chain.ID = apechain.ID(chainID)
serializedChain := chain.Bytes()
cmd.Println("Container ID: " + cidStr)
cmd.Println("Parsed chain:\n" + prettyJSONFormat(cmd, serializedChain))
req := &control.AddChainLocalOverrideRequest{
Body: &control.AddChainLocalOverrideRequest_Body{
ContainerId: rawCID,
Chain: serializedChain,
},
}
signRequest(cmd, pk, req)
cli := getClient(cmd, pk)
var resp *control.AddChainLocalOverrideResponse
var err error
err = cli.ExecRaw(func(client *client.Client) error {
resp, err = control.AddChainLocalOverride(client, req)
return err
})
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
cmd.Println("Rule has been added. Chain id: ", resp.GetBody().GetChainId())
}
func initControlAddRuleCmd() {
initControlFlags(addRuleCmd)
ff := addRuleCmd.Flags()
ff.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
ff.String(ruleFlag, "", "Rule statement")
ff.String(chainIDFlag, "", "Assign ID to the parsed chain")
}

View file

@ -220,12 +220,12 @@ func appendEstimation(sb *strings.Builder, resp *control.GetShardEvacuationStatu
if resp.GetBody().GetStatus() != control.GetShardEvacuationStatusResponse_Body_RUNNING || if resp.GetBody().GetStatus() != control.GetShardEvacuationStatusResponse_Body_RUNNING ||
resp.GetBody().GetDuration() == nil || resp.GetBody().GetDuration() == nil ||
resp.GetBody().GetTotal() == 0 || resp.GetBody().GetTotal() == 0 ||
resp.GetBody().GetEvacuated()+resp.GetBody().GetFailed()+resp.Body.GetSkipped() == 0 { resp.GetBody().GetEvacuated()+resp.GetBody().GetFailed() == 0 {
return return
} }
durationSeconds := float64(resp.GetBody().GetDuration().GetSeconds()) durationSeconds := float64(resp.GetBody().GetDuration().GetSeconds())
evacuated := float64(resp.GetBody().GetEvacuated() + resp.GetBody().GetFailed() + resp.GetBody().GetSkipped()) evacuated := float64(resp.GetBody().GetEvacuated() + resp.GetBody().GetFailed())
avgObjEvacuationTimeSeconds := durationSeconds / evacuated avgObjEvacuationTimeSeconds := durationSeconds / evacuated
objectsLeft := float64(resp.GetBody().GetTotal()) - evacuated objectsLeft := float64(resp.GetBody().GetTotal()) - evacuated
leftSeconds := avgObjEvacuationTimeSeconds * objectsLeft leftSeconds := avgObjEvacuationTimeSeconds * objectsLeft
@ -285,11 +285,10 @@ func appendShardIDs(sb *strings.Builder, resp *control.GetShardEvacuationStatusR
} }
func appendCounts(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) { func appendCounts(sb *strings.Builder, resp *control.GetShardEvacuationStatusResponse) {
sb.WriteString(fmt.Sprintf(" Evacuated %d objects out of %d, failed to evacuate: %d, skipped: %d.", sb.WriteString(fmt.Sprintf(" Evacuated %d object out of %d, failed to evacuate %d objects.",
resp.GetBody().GetEvacuated(), resp.GetBody().GetEvacuated(),
resp.GetBody().GetTotal(), resp.Body.GetTotal(),
resp.GetBody().GetFailed(), resp.Body.GetFailed()))
resp.GetBody().GetSkipped()))
} }
func initControlEvacuationShardCmd() { func initControlEvacuationShardCmd() {

View file

@ -1,69 +0,0 @@
package control
import (
"crypto/sha256"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"github.com/spf13/cobra"
)
var getRuleCmd = &cobra.Command{
Use: "get-rule",
Short: "Get local override",
Long: "Get local APE override of the node",
Run: getRule,
}
func getRule(cmd *cobra.Command, _ []string) {
pk := key.Get(cmd)
var cnr cid.ID
cidStr, _ := cmd.Flags().GetString(commonflags.CIDFlag)
commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(cidStr))
rawCID := make([]byte, sha256.Size)
cnr.Encode(rawCID)
chainID, _ := cmd.Flags().GetString(chainIDFlag)
req := &control.GetChainLocalOverrideRequest{
Body: &control.GetChainLocalOverrideRequest_Body{
ContainerId: rawCID,
ChainId: chainID,
},
}
signRequest(cmd, pk, req)
cli := getClient(cmd, pk)
var resp *control.GetChainLocalOverrideResponse
var err error
err = cli.ExecRaw(func(client *client.Client) error {
resp, err = control.GetChainLocalOverride(client, req)
return err
})
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
var chain apechain.Chain
commonCmd.ExitOnErr(cmd, "decode error: %w", chain.DecodeBytes(resp.GetBody().GetChain()))
// TODO (aarifullin): make pretty-formatted output for chains.
cmd.Println("Parsed chain:\n" + prettyJSONFormat(cmd, chain.Bytes()))
}
func initControGetRuleCmd() {
initControlFlags(getRuleCmd)
ff := getRuleCmd.Flags()
ff.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
ff.String(chainIDFlag, "", "Chain id")
}

View file

@ -1,9 +1,6 @@
package control package control
import ( import "github.com/spf13/cobra"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"github.com/spf13/cobra"
)
var irCmd = &cobra.Command{ var irCmd = &cobra.Command{
Use: "ir", Use: "ir",
@ -22,13 +19,3 @@ func initControlIRCmd() {
initControlIRHealthCheckCmd() initControlIRHealthCheckCmd()
initControlIRRemoveContainerCmd() initControlIRRemoveContainerCmd()
} }
func printVUB(cmd *cobra.Command, vub uint32) {
cmd.Printf("Transaction's valid until block is %d\n", vub)
}
func parseVUB(cmd *cobra.Command) uint32 {
vub, err := cmd.Flags().GetUint32(irFlagNameVUB)
commonCmd.ExitOnErr(cmd, "invalid valid until block value: %w", err)
return vub
}

View file

@ -1,6 +1,8 @@
package control package control
import ( import (
"errors"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
@ -27,13 +29,12 @@ To check removal status "frostfs-cli container list" command can be used.`,
} }
func initControlIRRemoveContainerCmd() { func initControlIRRemoveContainerCmd() {
initControlIRFlags(removeContainerCmd) initControlFlags(removeContainerCmd)
flags := removeContainerCmd.Flags() flags := removeContainerCmd.Flags()
flags.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage) flags.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
flags.String(ownerFlag, "", "Container owner's wallet address.") flags.String(ownerFlag, "", "Container owner's wallet address.")
removeContainerCmd.MarkFlagsMutuallyExclusive(commonflags.CIDFlag, ownerFlag) removeContainerCmd.MarkFlagsMutuallyExclusive(commonflags.CIDFlag, ownerFlag)
removeContainerCmd.MarkFlagsOneRequired(commonflags.CIDFlag, ownerFlag)
} }
func removeContainer(cmd *cobra.Command, _ []string) { func removeContainer(cmd *cobra.Command, _ []string) {
@ -59,7 +60,6 @@ func removeContainer(cmd *cobra.Command, _ []string) {
} else { } else {
cmd.Println("User containers sheduled to removal") cmd.Println("User containers sheduled to removal")
} }
printVUB(cmd, resp.GetBody().GetVub())
} }
func prepareRemoveContainerRequest(cmd *cobra.Command) *ircontrol.RemoveContainerRequest { func prepareRemoveContainerRequest(cmd *cobra.Command) *ircontrol.RemoveContainerRequest {
@ -73,6 +73,10 @@ func prepareRemoveContainerRequest(cmd *cobra.Command) *ircontrol.RemoveContaine
ownerStr, err := cmd.Flags().GetString(ownerFlag) ownerStr, err := cmd.Flags().GetString(ownerFlag)
commonCmd.ExitOnErr(cmd, "failed to get owner: ", err) commonCmd.ExitOnErr(cmd, "failed to get owner: ", err)
if len(ownerStr) == 0 && len(cidStr) == 0 {
commonCmd.ExitOnErr(cmd, "invalid usage: %w", errors.New("neither owner's wallet address nor container ID are specified"))
}
if len(ownerStr) > 0 { if len(ownerStr) > 0 {
var owner user.ID var owner user.ID
commonCmd.ExitOnErr(cmd, "invalid owner ID: %w", owner.DecodeString(ownerStr)) commonCmd.ExitOnErr(cmd, "invalid owner ID: %w", owner.DecodeString(ownerStr))
@ -86,8 +90,5 @@ func prepareRemoveContainerRequest(cmd *cobra.Command) *ircontrol.RemoveContaine
commonCmd.ExitOnErr(cmd, "invalid container ID: %w", containerID.DecodeString(cidStr)) commonCmd.ExitOnErr(cmd, "invalid container ID: %w", containerID.DecodeString(cidStr))
req.Body.ContainerId = containerID[:] req.Body.ContainerId = containerID[:]
} }
req.Body.Vub = parseVUB(cmd)
return req return req
} }

View file

@ -20,7 +20,7 @@ var removeNodeCmd = &cobra.Command{
} }
func initControlIRRemoveNodeCmd() { func initControlIRRemoveNodeCmd() {
initControlIRFlags(removeNodeCmd) initControlFlags(removeNodeCmd)
flags := removeNodeCmd.Flags() flags := removeNodeCmd.Flags()
flags.String("node", "", "Node public key as a hex string") flags.String("node", "", "Node public key as a hex string")
@ -41,7 +41,6 @@ func removeNode(cmd *cobra.Command, _ []string) {
req := new(ircontrol.RemoveNodeRequest) req := new(ircontrol.RemoveNodeRequest)
req.SetBody(&ircontrol.RemoveNodeRequest_Body{ req.SetBody(&ircontrol.RemoveNodeRequest_Body{
Key: nodeKey, Key: nodeKey,
Vub: parseVUB(cmd),
}) })
commonCmd.ExitOnErr(cmd, "could not sign request: %w", ircontrolsrv.SignMessage(pk, req)) commonCmd.ExitOnErr(cmd, "could not sign request: %w", ircontrolsrv.SignMessage(pk, req))
@ -56,5 +55,4 @@ func removeNode(cmd *cobra.Command, _ []string) {
verifyResponse(cmd, resp.GetSignature(), resp.GetBody()) verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
cmd.Println("Node removed") cmd.Println("Node removed")
printVUB(cmd, resp.GetBody().GetVub())
} }

View file

@ -17,7 +17,7 @@ var tickEpochCmd = &cobra.Command{
} }
func initControlIRTickEpochCmd() { func initControlIRTickEpochCmd() {
initControlIRFlags(tickEpochCmd) initControlFlags(tickEpochCmd)
} }
func tickEpoch(cmd *cobra.Command, _ []string) { func tickEpoch(cmd *cobra.Command, _ []string) {
@ -25,9 +25,7 @@ func tickEpoch(cmd *cobra.Command, _ []string) {
c := getClient(cmd, pk) c := getClient(cmd, pk)
req := new(ircontrol.TickEpochRequest) req := new(ircontrol.TickEpochRequest)
req.SetBody(&ircontrol.TickEpochRequest_Body{ req.SetBody(new(ircontrol.TickEpochRequest_Body))
Vub: parseVUB(cmd),
})
err := ircontrolsrv.SignMessage(pk, req) err := ircontrolsrv.SignMessage(pk, req)
commonCmd.ExitOnErr(cmd, "could not sign request: %w", err) commonCmd.ExitOnErr(cmd, "could not sign request: %w", err)
@ -42,5 +40,4 @@ func tickEpoch(cmd *cobra.Command, _ []string) {
verifyResponse(cmd, resp.GetSignature(), resp.GetBody()) verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
cmd.Println("Epoch tick requested") cmd.Println("Epoch tick requested")
printVUB(cmd, resp.GetBody().GetVub())
} }

View file

@ -1,72 +0,0 @@
package control
import (
"crypto/sha256"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"github.com/spf13/cobra"
)
var listRulesCmd = &cobra.Command{
Use: "list-rules",
Short: "List local overrides",
Long: "List local APE overrides of the node",
Run: listRules,
}
func listRules(cmd *cobra.Command, _ []string) {
pk := key.Get(cmd)
var cnr cid.ID
cidStr, _ := cmd.Flags().GetString(commonflags.CIDFlag)
commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(cidStr))
rawCID := make([]byte, sha256.Size)
cnr.Encode(rawCID)
req := &control.ListChainLocalOverridesRequest{
Body: &control.ListChainLocalOverridesRequest_Body{
ContainerId: rawCID,
},
}
signRequest(cmd, pk, req)
cli := getClient(cmd, pk)
var resp *control.ListChainLocalOverridesResponse
var err error
err = cli.ExecRaw(func(client *client.Client) error {
resp, err = control.ListChainLocalOverrides(client, req)
return err
})
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
chains := resp.GetBody().GetChains()
if len(chains) == 0 {
cmd.Println("Local overrides are not defined for the container.")
return
}
for _, c := range chains {
// TODO (aarifullin): make pretty-formatted output for chains.
var chain apechain.Chain
commonCmd.ExitOnErr(cmd, "decode error: %w", chain.DecodeBytes(c))
cmd.Println("Parsed chain:\n" + prettyJSONFormat(cmd, chain.Bytes()))
}
}
func initControlListRulesCmd() {
initControlFlags(listRulesCmd)
ff := listRulesCmd.Flags()
ff.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
}

View file

@ -1,72 +0,0 @@
package control
import (
"crypto/sha256"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"github.com/spf13/cobra"
)
const (
chainIDFlag = "chain-id"
)
var removeRuleCmd = &cobra.Command{
Use: "remove-rule",
Short: "Remove local override",
Long: "Remove local APE override of the node",
Run: removeRule,
}
func removeRule(cmd *cobra.Command, _ []string) {
pk := key.Get(cmd)
var cnr cid.ID
cidStr, _ := cmd.Flags().GetString(commonflags.CIDFlag)
commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(cidStr))
rawCID := make([]byte, sha256.Size)
cnr.Encode(rawCID)
chainID, _ := cmd.Flags().GetString(chainIDFlag)
req := &control.RemoveChainLocalOverrideRequest{
Body: &control.RemoveChainLocalOverrideRequest_Body{
ContainerId: rawCID,
ChainId: chainID,
},
}
signRequest(cmd, pk, req)
cli := getClient(cmd, pk)
var resp *control.RemoveChainLocalOverrideResponse
var err error
err = cli.ExecRaw(func(client *client.Client) error {
resp, err = control.RemoveChainLocalOverride(client, req)
return err
})
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
if resp.GetBody().Removed {
cmd.Println("Rule has been removed.")
} else {
cmd.Println("Rule has not been removed.")
}
}
func initControlRemoveRuleCmd() {
initControlFlags(removeRuleCmd)
ff := removeRuleCmd.Flags()
ff.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
ff.String(chainIDFlag, "", "Chain id")
}

View file

@ -34,10 +34,6 @@ func init() {
shardsCmd, shardsCmd,
synchronizeTreeCmd, synchronizeTreeCmd,
irCmd, irCmd,
addRuleCmd,
removeRuleCmd,
listRulesCmd,
getRuleCmd,
) )
initControlHealthCheckCmd() initControlHealthCheckCmd()
@ -46,8 +42,4 @@ func init() {
initControlShardsCmd() initControlShardsCmd()
initControlSynchronizeTreeCmd() initControlSynchronizeTreeCmd()
initControlIRCmd() initControlIRCmd()
initControlAddRuleCmd()
initControlRemoveRuleCmd()
initControlListRulesCmd()
initControGetRuleCmd()
} }

View file

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"sort"
"strings" "strings"
rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
@ -50,14 +49,11 @@ func listShards(cmd *cobra.Command, _ []string) {
verifyResponse(cmd, resp.GetSignature(), resp.GetBody()) verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
shards := resp.GetBody().GetShards()
sortShardsByID(shards)
isJSON, _ := cmd.Flags().GetBool(commonflags.JSON) isJSON, _ := cmd.Flags().GetBool(commonflags.JSON)
if isJSON { if isJSON {
prettyPrintShardsJSON(cmd, shards) prettyPrintShardsJSON(cmd, resp.GetBody().GetShards())
} else { } else {
prettyPrintShards(cmd, shards) prettyPrintShards(cmd, resp.GetBody().GetShards())
} }
} }
@ -119,9 +115,3 @@ func shardModeToString(m control.ShardMode) string {
return "unknown" return "unknown"
} }
func sortShardsByID(ii []*control.ShardInfo) {
sort.Slice(ii, func(i, j int) bool {
return bytes.Compare(ii[i].Shard_ID, ii[j].Shard_ID) < 0
})
}

View file

@ -1,9 +1,7 @@
package control package control
import ( import (
"bytes"
"fmt" "fmt"
"sort"
"strings" "strings"
rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
@ -169,9 +167,5 @@ func getShardIDList(cmd *cobra.Command) [][]byte {
res = append(res, raw) res = append(res, raw)
} }
sort.Slice(res, func(i, j int) bool {
return bytes.Compare(res[i], res[j]) < 0
})
return res return res
} }

View file

@ -14,10 +14,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const (
irFlagNameVUB = "vub"
)
func initControlFlags(cmd *cobra.Command) { func initControlFlags(cmd *cobra.Command) {
ff := cmd.Flags() ff := cmd.Flags()
ff.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage) ff.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
@ -26,13 +22,6 @@ func initControlFlags(cmd *cobra.Command) {
ff.DurationP(commonflags.Timeout, commonflags.TimeoutShorthand, commonflags.TimeoutDefault, commonflags.TimeoutUsage) ff.DurationP(commonflags.Timeout, commonflags.TimeoutShorthand, commonflags.TimeoutDefault, commonflags.TimeoutUsage)
} }
func initControlIRFlags(cmd *cobra.Command) {
initControlFlags(cmd)
ff := cmd.Flags()
ff.Uint32(irFlagNameVUB, 0, "Valid until block value for notary transaction")
}
func signRequest(cmd *cobra.Command, pk *ecdsa.PrivateKey, req controlSvc.SignedMessage) { func signRequest(cmd *cobra.Command, pk *ecdsa.PrivateKey, req controlSvc.SignedMessage) {
err := controlSvc.SignMessage(pk, req) err := controlSvc.SignMessage(pk, req)
commonCmd.ExitOnErr(cmd, "could not sign request: %w", err) commonCmd.ExitOnErr(cmd, "could not sign request: %w", err)

View file

@ -1,6 +1,7 @@
package netmap package netmap
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -14,7 +15,9 @@ var Cmd = &cobra.Command{
// the viper before execution // the viper before execution
commonflags.Bind(cmd) commonflags.Bind(cmd)
commonflags.BindAPI(cmd) commonflags.BindAPI(cmd)
common.StartClientCommandSpan(cmd)
}, },
PersistentPostRun: common.StopClientCommandSpan,
} }
func init() { func init() {

View file

@ -132,7 +132,7 @@ func createOutWriter(cmd *cobra.Command, filename string) (out io.Writer, closer
out = os.Stdout out = os.Stdout
closer = func() {} closer = func() {}
} else { } else {
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644) f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil { if err != nil {
commonCmd.ExitOnErr(cmd, "", fmt.Errorf("can't open file '%s': %w", filename, err)) commonCmd.ExitOnErr(cmd, "", fmt.Errorf("can't open file '%s': %w", filename, err))
} }

View file

@ -94,7 +94,7 @@ var objectLockCmd = &cobra.Command{
obj := objectSDK.New() obj := objectSDK.New()
obj.SetContainerID(cnr) obj.SetContainerID(cnr)
obj.SetOwnerID(idOwner) obj.SetOwnerID(&idOwner)
obj.SetType(objectSDK.TypeLock) obj.SetType(objectSDK.TypeLock)
obj.SetAttributes(expirationAttr) obj.SetAttributes(expirationAttr)
obj.SetPayload(lock.Marshal()) obj.SetPayload(lock.Marshal())

View file

@ -31,10 +31,10 @@ const (
) )
type objectNodesInfo struct { type objectNodesInfo struct {
containerID cid.ID containerID cid.ID
objectID oid.ID objectID oid.ID
relatedObjectIDs []oid.ID relatedObjectIDs []oid.ID
isLockOrTombstone bool isLock bool
} }
type boolError struct { type boolError struct {
@ -101,9 +101,9 @@ func getObjectInfo(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.C
res, err := internalclient.HeadObject(cmd.Context(), prmHead) res, err := internalclient.HeadObject(cmd.Context(), prmHead)
if err == nil { if err == nil {
return &objectNodesInfo{ return &objectNodesInfo{
containerID: cnrID, containerID: cnrID,
objectID: objID, objectID: objID,
isLockOrTombstone: res.Header().Type() == objectSDK.TypeLock || res.Header().Type() == objectSDK.TypeTombstone, isLock: res.Header().Type() == objectSDK.TypeLock,
} }
} }
@ -191,7 +191,7 @@ func getRequiredPlacement(cmd *cobra.Command, objInfo *objectNodesInfo, placemen
numOfReplicas := placementPolicy.ReplicaNumberByIndex(repIdx) numOfReplicas := placementPolicy.ReplicaNumberByIndex(repIdx)
var nodeIdx uint32 var nodeIdx uint32
for _, n := range rep { for _, n := range rep {
if !objInfo.isLockOrTombstone && nodeIdx == numOfReplicas { // lock and tombstone objects should be on all container nodes if !objInfo.isLock && nodeIdx == numOfReplicas { //lock object should be on all container nodes
break break
} }
nodes[n.Hash()] = n nodes[n.Hash()] = n
@ -213,8 +213,7 @@ func getRequiredPlacement(cmd *cobra.Command, objInfo *objectNodesInfo, placemen
} }
func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, requiredPlacement map[uint64]netmapSDK.NodeInfo, func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, requiredPlacement map[uint64]netmapSDK.NodeInfo,
pk *ecdsa.PrivateKey, objInfo *objectNodesInfo, pk *ecdsa.PrivateKey, objInfo *objectNodesInfo) map[uint64]boolError {
) map[uint64]boolError {
result := make(map[uint64]boolError) result := make(map[uint64]boolError)
resultMtx := &sync.Mutex{} resultMtx := &sync.Mutex{}

View file

@ -93,7 +93,7 @@ func putObject(cmd *cobra.Command, _ []string) {
attrs := getAllObjectAttributes(cmd) attrs := getAllObjectAttributes(cmd)
obj.SetContainerID(cnr) obj.SetContainerID(cnr)
obj.SetOwnerID(ownerID) obj.SetOwnerID(&ownerID)
obj.SetAttributes(attrs...) obj.SetAttributes(attrs...)
notificationInfo, err := parseObjectNotifications(cmd) notificationInfo, err := parseObjectNotifications(cmd)
@ -160,7 +160,7 @@ func readFilePayload(filename string, cmd *cobra.Command) (io.Reader, cid.ID, us
commonCmd.ExitOnErr(cmd, "can't unmarshal object from given file: %w", objTemp.Unmarshal(buf)) commonCmd.ExitOnErr(cmd, "can't unmarshal object from given file: %w", objTemp.Unmarshal(buf))
payloadReader := bytes.NewReader(objTemp.Payload()) payloadReader := bytes.NewReader(objTemp.Payload())
cnr, _ := objTemp.ContainerID() cnr, _ := objTemp.ContainerID()
ownerID := objTemp.OwnerID() ownerID := *objTemp.OwnerID()
return payloadReader, cnr, ownerID return payloadReader, cnr, ownerID
} }

View file

@ -1,6 +1,7 @@
package object package object
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -15,7 +16,9 @@ var Cmd = &cobra.Command{
// the viper before execution // the viper before execution
commonflags.Bind(cmd) commonflags.Bind(cmd)
commonflags.BindAPI(cmd) commonflags.BindAPI(cmd)
common.StartClientCommandSpan(cmd)
}, },
PersistentPostRun: common.StopClientCommandSpan,
} }
func init() { func init() {
@ -28,8 +31,7 @@ func init() {
objectHashCmd, objectHashCmd,
objectRangeCmd, objectRangeCmd,
objectLockCmd, objectLockCmd,
objectNodesCmd, objectNodesCmd}
}
Cmd.AddCommand(objectChildCommands...) Cmd.AddCommand(objectChildCommands...)

View file

@ -46,10 +46,6 @@ of frostfs-api and some useful utilities for compiling ACL rules from JSON
notation, managing container access through protocol gates, querying network map notation, managing container access through protocol gates, querying network map
and much more!`, and much more!`,
Run: entryPoint, Run: entryPoint,
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
common.StartClientCommandSpan(cmd)
},
PersistentPostRun: common.StopClientCommandSpan,
} }
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.
@ -61,7 +57,6 @@ func Execute() {
func init() { func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
cobra.EnableTraverseRunHooks = true
// use stdout as default output for cmd.Print() // use stdout as default output for cmd.Print()
rootCmd.SetOut(os.Stdout) rootCmd.SetOut(os.Stdout)

View file

@ -6,6 +6,7 @@ import (
"os" "os"
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client" internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
@ -32,7 +33,9 @@ var createCmd = &cobra.Command{
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
_ = viper.BindPFlag(commonflags.WalletPath, cmd.Flags().Lookup(commonflags.WalletPath)) _ = viper.BindPFlag(commonflags.WalletPath, cmd.Flags().Lookup(commonflags.WalletPath))
_ = viper.BindPFlag(commonflags.Account, cmd.Flags().Lookup(commonflags.Account)) _ = viper.BindPFlag(commonflags.Account, cmd.Flags().Lookup(commonflags.Account))
common.StartClientCommandSpan(cmd)
}, },
PersistentPostRun: common.StopClientCommandSpan,
} }
func init() { func init() {
@ -78,7 +81,7 @@ func createSession(cmd *cobra.Command, _ []string) {
} }
filename, _ := cmd.Flags().GetString(outFlag) filename, _ := cmd.Flags().GetString(outFlag)
err = os.WriteFile(filename, data, 0o644) err = os.WriteFile(filename, data, 0644)
commonCmd.ExitOnErr(cmd, "can't write token to file: %w", err) commonCmd.ExitOnErr(cmd, "can't write token to file: %w", err)
} }

View file

@ -1,181 +0,0 @@
package util
import (
"errors"
"fmt"
"strings"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/flynn-archive/go-shlex"
)
var (
errInvalidStatementFormat = errors.New("invalid statement format")
errInvalidConditionFormat = errors.New("invalid condition format")
errUnknownAction = errors.New("action is not recognized")
errUnknownOperation = errors.New("operation is not recognized")
errUnknownActionDetail = errors.New("action detail is not recognized")
errUnknownBinaryOperator = errors.New("binary operator is not recognized")
errUnknownCondObjectType = errors.New("condition object type is not recognized")
)
// ParseAPEChain parses APE chain rules.
func ParseAPEChain(chain *apechain.Chain, rules []string) error {
if len(rules) == 0 {
return errors.New("no APE rules provided")
}
for _, rule := range rules {
r := new(apechain.Rule)
if err := ParseAPERule(r, rule); err != nil {
return err
}
chain.Rules = append(chain.Rules, *r)
}
return nil
}
// ParseAPERule parses access-policy-engine statement from the following form:
// <action>[:action_detail] <operation> [<condition1> ...] <resource>
//
// Examples:
// deny Object.Put *
// deny:QuotaLimitReached Object.Put *
// allow Object.Put *
// allow Object.Get Object.Resource:Department=HR Object.Request:Actor=ownerA *
//
//nolint:godot
func ParseAPERule(r *apechain.Rule, rule string) error {
lexemes, err := shlex.Split(rule)
if err != nil {
return fmt.Errorf("can't parse rule '%s': %v", rule, err)
}
return parseRuleLexemes(r, lexemes)
}
func parseRuleLexemes(r *apechain.Rule, lexemes []string) error {
if len(lexemes) < 2 {
return errInvalidStatementFormat
}
var err error
r.Status, err = parseStatus(lexemes[0])
if err != nil {
return err
}
r.Actions, err = parseAction(lexemes[1])
if err != nil {
return err
}
r.Condition, err = parseConditions(lexemes[2 : len(lexemes)-1])
if err != nil {
return err
}
r.Resources, err = parseResource(lexemes[len(lexemes)-1])
return err
}
func parseStatus(lexeme string) (apechain.Status, error) {
action, expression, found := strings.Cut(lexeme, ":")
switch action = strings.ToLower(action); action {
case "deny":
if !found {
return apechain.AccessDenied, nil
} else if strings.EqualFold(expression, "QuotaLimitReached") {
return apechain.QuotaLimitReached, nil
} else {
return 0, fmt.Errorf("%w: %s", errUnknownActionDetail, expression)
}
case "allow":
if found {
return 0, errUnknownActionDetail
}
return apechain.Allow, nil
default:
return 0, errUnknownAction
}
}
func parseAction(lexeme string) (apechain.Actions, error) {
switch strings.ToLower(lexeme) {
case "object.put":
return apechain.Actions{Names: []string{nativeschema.MethodPutObject}}, nil
case "object.get":
return apechain.Actions{Names: []string{nativeschema.MethodGetObject}}, nil
case "object.head":
return apechain.Actions{Names: []string{nativeschema.MethodHeadObject}}, nil
case "object.delete":
return apechain.Actions{Names: []string{nativeschema.MethodDeleteObject}}, nil
case "object.search":
return apechain.Actions{Names: []string{nativeschema.MethodSearchObject}}, nil
case "object.range":
return apechain.Actions{Names: []string{nativeschema.MethodRangeObject}}, nil
case "object.hash":
return apechain.Actions{Names: []string{nativeschema.MethodHashObject}}, nil
default:
}
return apechain.Actions{}, fmt.Errorf("%w: %s", errUnknownOperation, lexeme)
}
func parseResource(lexeme string) (apechain.Resources, error) {
if lexeme == "*" {
return apechain.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}}, nil
}
return apechain.Resources{Names: []string{fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, lexeme)}}, nil
}
const (
ObjectResource = "object.resource"
ObjectRequest = "object.request"
)
var typeToCondObject = map[string]apechain.ObjectType{
ObjectResource: apechain.ObjectResource,
ObjectRequest: apechain.ObjectRequest,
}
func parseConditions(lexemes []string) ([]apechain.Condition, error) {
conds := make([]apechain.Condition, 0)
for _, lexeme := range lexemes {
typ, expression, found := strings.Cut(lexeme, ":")
typ = strings.ToLower(typ)
objType, ok := typeToCondObject[typ]
if ok {
if !found {
return nil, fmt.Errorf("%w: %s", errInvalidConditionFormat, lexeme)
}
var lhs, rhs string
var binExpFound bool
var cond apechain.Condition
cond.Object = objType
lhs, rhs, binExpFound = strings.Cut(expression, "!=")
if !binExpFound {
lhs, rhs, binExpFound = strings.Cut(expression, "=")
if !binExpFound {
return nil, fmt.Errorf("%w: %s", errUnknownBinaryOperator, expression)
}
cond.Op = apechain.CondStringEquals
} else {
cond.Op = apechain.CondStringNotEquals
}
cond.Key, cond.Value = lhs, rhs
conds = append(conds, cond)
} else {
return nil, fmt.Errorf("%w: %s", errUnknownCondObjectType, typ)
}
}
return conds, nil
}

View file

@ -1,130 +0,0 @@
package util
import (
"testing"
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/stretchr/testify/require"
)
func TestParseAPERule(t *testing.T) {
tests := [...]struct {
name string
rule string
expectErr error
expectRule policyengine.Rule
}{
{
name: "Valid allow rule",
rule: "allow Object.Put *",
expectRule: policyengine.Rule{
Status: policyengine.Allow,
Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}},
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
Condition: []policyengine.Condition{},
},
},
{
name: "Valid deny rule",
rule: "deny Object.Put *",
expectRule: policyengine.Rule{
Status: policyengine.AccessDenied,
Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}},
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
Condition: []policyengine.Condition{},
},
},
{
name: "Valid deny rule with action detail",
rule: "deny:QuotaLimitReached Object.Put *",
expectRule: policyengine.Rule{
Status: policyengine.QuotaLimitReached,
Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}},
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
Condition: []policyengine.Condition{},
},
},
{
name: "Valid allow rule with conditions",
rule: "allow Object.Get Object.Resource:Department=HR Object.Request:Actor!=ownerA *",
expectRule: policyengine.Rule{
Status: policyengine.Allow,
Actions: policyengine.Actions{Names: []string{nativeschema.MethodGetObject}},
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
Condition: []policyengine.Condition{
{
Op: policyengine.CondStringEquals,
Object: policyengine.ObjectResource,
Key: "Department",
Value: "HR",
},
{
Op: policyengine.CondStringNotEquals,
Object: policyengine.ObjectRequest,
Key: "Actor",
Value: "ownerA",
},
},
},
},
{
name: "Valid rule with conditions with action detail",
rule: "deny:QuotaLimitReached Object.Get Object.Resource:Department=HR Object.Request:Actor!=ownerA *",
expectRule: policyengine.Rule{
Status: policyengine.QuotaLimitReached,
Actions: policyengine.Actions{Names: []string{nativeschema.MethodGetObject}},
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
Condition: []policyengine.Condition{
{
Op: policyengine.CondStringEquals,
Object: policyengine.ObjectResource,
Key: "Department",
Value: "HR",
},
{
Op: policyengine.CondStringNotEquals,
Object: policyengine.ObjectRequest,
Key: "Actor",
Value: "ownerA",
},
},
},
},
{
name: "Invalid rule with unknown action",
rule: "permit Object.Put *",
expectErr: errUnknownAction,
},
{
name: "Invalid rule with unknown operation",
rule: "allow Object.PutOut *",
expectErr: errUnknownOperation,
},
{
name: "Invalid rule with unknown action detail",
rule: "deny:UnknownActionDetail Object.Put *",
expectErr: errUnknownActionDetail,
},
{
name: "Invalid rule with unknown condition binary operator",
rule: "deny Object.Put Object.Resource:Department<HR *",
expectErr: errUnknownBinaryOperator,
},
{
name: "Invalid rule with unknown condition object type",
rule: "deny Object.Put Object.ResourZe:Department=HR *",
expectErr: errUnknownCondObjectType,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
r := new(policyengine.Rule)
err := ParseAPERule(r, test.rule)
require.ErrorIs(t, err, test.expectErr)
if test.expectErr == nil {
require.Equal(t, test.expectRule, *r)
}
})
}
}

View file

@ -48,7 +48,7 @@ func convertEACLTable(cmd *cobra.Command, _ []string) {
return return
} }
err = os.WriteFile(to, data, 0o644) err = os.WriteFile(to, data, 0644)
commonCmd.ExitOnErr(cmd, "can't write exteded ACL table to file: %w", err) commonCmd.ExitOnErr(cmd, "can't write exteded ACL table to file: %w", err)
cmd.Printf("extended ACL table was successfully dumped to %s\n", to) cmd.Printf("extended ACL table was successfully dumped to %s\n", to)

View file

@ -78,7 +78,7 @@ func keyerGenerate(filename string, d *keyer.Dashboard) error {
} }
if filename != "" { if filename != "" {
return os.WriteFile(filename, key, 0o600) return os.WriteFile(filename, key, 0600)
} }
return nil return nil

View file

@ -56,7 +56,7 @@ func signBearerToken(cmd *cobra.Command, _ []string) {
return return
} }
err = os.WriteFile(to, data, 0o644) err = os.WriteFile(to, data, 0644)
commonCmd.ExitOnErr(cmd, "can't write signed bearer token to file: %w", err) commonCmd.ExitOnErr(cmd, "can't write signed bearer token to file: %w", err)
cmd.Printf("signed bearer token was successfully dumped to %s\n", to) cmd.Printf("signed bearer token was successfully dumped to %s\n", to)

View file

@ -76,7 +76,7 @@ func signSessionToken(cmd *cobra.Command, _ []string) {
return return
} }
err = os.WriteFile(to, data, 0o644) err = os.WriteFile(to, data, 0644)
if err != nil { if err != nil {
commonCmd.ExitOnErr(cmd, "", fmt.Errorf("can't write signed session token to %s: %w", to, err)) commonCmd.ExitOnErr(cmd, "", fmt.Errorf("can't write signed session token to %s: %w", to, err))
} }

View file

@ -13,7 +13,7 @@ import (
func newConfig() (*viper.Viper, error) { func newConfig() (*viper.Viper, error) {
var err error var err error
dv := viper.New() var dv = viper.New()
defaultConfiguration(dv) defaultConfiguration(dv)

View file

@ -10,7 +10,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/misc" "git.frostfs.info/TrueCloudLab/frostfs-node/misc"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
irMetrics "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
@ -62,13 +61,12 @@ func main() {
cfg, err = newConfig() cfg, err = newConfig()
exitErr(err) exitErr(err)
metrics := irMetrics.NewInnerRingMetrics() logPrm.MetricsNamespace = "frostfs_ir"
err = logPrm.SetLevelString( err = logPrm.SetLevelString(
cfg.GetString("logger.level"), cfg.GetString("logger.level"),
) )
exitErr(err) exitErr(err)
logPrm.SamplingHook = metrics.LogMetrics().GetSamplingHook()
log, err = logger.NewLogger(logPrm) log, err = logger.NewLogger(logPrm)
exitErr(err) exitErr(err)
@ -80,7 +78,7 @@ func main() {
metricsCmp = newMetricsComponent() metricsCmp = newMetricsComponent()
metricsCmp.init() metricsCmp.init()
innerRing, err = innerring.New(ctx, log, cfg, intErr, metrics) innerRing, err = innerring.New(ctx, log, cfg, intErr)
exitErr(err) exitErr(err)
pprofCmp.start() pprofCmp.start()

View file

@ -59,7 +59,7 @@ func WriteObjectToFile(cmd *cobra.Command, path string, data []byte) {
} }
ExitOnErr(cmd, Errf("could not write file: %w", ExitOnErr(cmd, Errf("could not write file: %w",
os.WriteFile(path, data, 0o644))) os.WriteFile(path, data, 0644)))
cmd.Printf("\nSaved payload to '%s' file\n", path) cmd.Printf("\nSaved payload to '%s' file\n", path)
} }

View file

@ -6,35 +6,46 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
putsvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/put" putsvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/put"
utilSync "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sync" utilSync "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sync"
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"
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
lru "github.com/hashicorp/golang-lru/v2" lru "github.com/hashicorp/golang-lru/v2"
"github.com/hashicorp/golang-lru/v2/expirable"
) )
type netValueReader[K any, V any] func(K) (V, error) type netValueReader[K any, V any] func(K) (V, error)
type valueWithError[V any] struct { type valueWithTime[V any] struct {
v V v V
t time.Time
// cached error in order to not repeat failed request for some time // cached error in order to not repeat failed request for some time
e error e error
} }
// entity that provides TTL cache interface. // entity that provides TTL cache interface.
type ttlNetCache[K comparable, V any] struct { type ttlNetCache[K comparable, V any] struct {
cache *expirable.LRU[K, *valueWithError[V]] ttl time.Duration
netRdr netValueReader[K, V]
sz int
cache *lru.Cache[K, *valueWithTime[V]]
netRdr netValueReader[K, V]
keyLocker *utilSync.KeyLocker[K] keyLocker *utilSync.KeyLocker[K]
} }
// complicates netValueReader with TTL caching mechanism. // complicates netValueReader with TTL caching mechanism.
func newNetworkTTLCache[K comparable, V any](sz int, ttl time.Duration, netRdr netValueReader[K, V]) *ttlNetCache[K, V] { func newNetworkTTLCache[K comparable, V any](sz int, ttl time.Duration, netRdr netValueReader[K, V]) *ttlNetCache[K, V] {
cache := expirable.NewLRU[K, *valueWithError[V]](sz, nil, ttl) cache, err := lru.New[K, *valueWithTime[V]](sz)
fatalOnErr(err)
return &ttlNetCache[K, V]{ return &ttlNetCache[K, V]{
ttl: ttl,
sz: sz,
cache: cache, cache: cache,
netRdr: netRdr, netRdr: netRdr,
keyLocker: utilSync.NewKeyLocker[K](), keyLocker: utilSync.NewKeyLocker[K](),
@ -48,7 +59,7 @@ func newNetworkTTLCache[K comparable, V any](sz int, ttl time.Duration, netRdr n
// returned value should not be modified. // returned value should not be modified.
func (c *ttlNetCache[K, V]) get(key K) (V, error) { func (c *ttlNetCache[K, V]) get(key K) (V, error) {
val, ok := c.cache.Peek(key) val, ok := c.cache.Peek(key)
if ok { if ok && time.Since(val.t) < c.ttl {
return val.v, val.e return val.v, val.e
} }
@ -56,14 +67,15 @@ func (c *ttlNetCache[K, V]) get(key K) (V, error) {
defer c.keyLocker.Unlock(key) defer c.keyLocker.Unlock(key)
val, ok = c.cache.Peek(key) val, ok = c.cache.Peek(key)
if ok { if ok && time.Since(val.t) < c.ttl {
return val.v, val.e return val.v, val.e
} }
v, err := c.netRdr(key) v, err := c.netRdr(key)
c.cache.Add(key, &valueWithError[V]{ c.cache.Add(key, &valueWithTime[V]{
v: v, v: v,
t: time.Now(),
e: err, e: err,
}) })
@ -74,8 +86,9 @@ func (c *ttlNetCache[K, V]) set(k K, v V, e error) {
c.keyLocker.Lock(k) c.keyLocker.Lock(k)
defer c.keyLocker.Unlock(k) defer c.keyLocker.Unlock(k)
c.cache.Add(k, &valueWithError[V]{ c.cache.Add(k, &valueWithTime[V]{
v: v, v: v,
t: time.Now(),
e: e, e: e,
}) })
} }
@ -231,6 +244,117 @@ func (s *lruNetmapSource) Epoch() (uint64, error) {
return s.netState.CurrentEpoch(), nil return s.netState.CurrentEpoch(), nil
} }
// wrapper over TTL cache of values read from the network
// that implements container lister.
type ttlContainerLister struct {
inner *ttlNetCache[string, *cacheItemContainerList]
client *cntClient.Client
}
// value type for ttlNetCache used by ttlContainerLister.
type cacheItemContainerList struct {
// protects list from concurrent add/remove ops
mtx sync.RWMutex
// actual list of containers owner by the particular user
list []cid.ID
}
func newCachedContainerLister(c *cntClient.Client, ttl time.Duration) ttlContainerLister {
const containerListerCacheSize = 100
lruCnrListerCache := newNetworkTTLCache(containerListerCacheSize, ttl, func(strID string) (*cacheItemContainerList, error) {
var id *user.ID
if strID != "" {
id = new(user.ID)
err := id.DecodeString(strID)
if err != nil {
return nil, err
}
}
list, err := c.ContainersOf(id)
if err != nil {
return nil, err
}
return &cacheItemContainerList{
list: list,
}, nil
})
return ttlContainerLister{inner: lruCnrListerCache, client: c}
}
// List returns list of container IDs from the cache. If list is missing in the
// cache or expired, then it returns container IDs from side chain and updates
// the cache.
func (s ttlContainerLister) List(id *user.ID) ([]cid.ID, error) {
if id == nil {
return s.client.ContainersOf(nil)
}
item, err := s.inner.get(id.EncodeToString())
if err != nil {
return nil, err
}
item.mtx.RLock()
res := make([]cid.ID, len(item.list))
copy(res, item.list)
item.mtx.RUnlock()
return res, nil
}
// updates cached list of owner's containers: cnr is added if flag is true, otherwise it's removed.
// Concurrent calls can lead to some races:
// - two parallel additions to missing owner's cache can lead to only one container to be cached
// - async cache value eviction can lead to idle addition
//
// All described race cases aren't critical since cache values expire anyway, we just try
// to increase cache actuality w/o huge overhead on synchronization.
func (s *ttlContainerLister) update(owner user.ID, cnr cid.ID, add bool) {
strOwner := owner.EncodeToString()
val, ok := s.inner.cache.Peek(strOwner)
if !ok {
// we could cache the single cnr but in this case we will disperse
// with the Sidechain a lot
return
}
if s.inner.ttl <= time.Since(val.t) {
return
}
item := val.v
item.mtx.Lock()
{
found := false
for i := range item.list {
if found = item.list[i].Equals(cnr); found {
if !add {
item.list = append(item.list[:i], item.list[i+1:]...)
// if list became empty we don't remove the value from the cache
// since empty list is a correct value, and we don't want to insta
// re-request it from the Sidechain
}
break
}
}
if add && !found {
item.list = append(item.list, cnr)
}
}
item.mtx.Unlock()
}
type cachedIRFetcher struct { type cachedIRFetcher struct {
*ttlNetCache[struct{}, [][]byte] *ttlNetCache[struct{}, [][]byte]
} }

View file

@ -1,56 +0,0 @@
package main
import (
"errors"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestTTLNetCache(t *testing.T) {
ttlDuration := time.Millisecond * 50
cache := newNetworkTTLCache[string, time.Time](10, ttlDuration, testNetValueReader)
key := "key"
t.Run("Test Add and Get", func(t *testing.T) {
ti := time.Now()
cache.set(key, ti, nil)
val, err := cache.get(key)
require.NoError(t, err)
require.Equal(t, ti, val)
})
t.Run("Test TTL", func(t *testing.T) {
ti := time.Now()
cache.set(key, ti, nil)
time.Sleep(2 * ttlDuration)
val, err := cache.get(key)
require.NoError(t, err)
require.NotEqual(t, val, ti)
})
t.Run("Test Remove", func(t *testing.T) {
ti := time.Now()
cache.set(key, ti, nil)
cache.remove(key)
val, err := cache.get(key)
require.NoError(t, err)
require.NotEqual(t, val, ti)
})
t.Run("Test Cache Error", func(t *testing.T) {
cache.set("error", time.Now(), errors.New("mock error"))
_, err := cache.get("error")
require.Error(t, err)
require.Equal(t, "mock error", err.Error())
})
}
func testNetValueReader(key string) (time.Time, error) {
if key == "error" {
return time.Now(), errors.New("mock error")
}
return time.Now(), nil
}

View file

@ -62,7 +62,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/state" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/state"
"git.frostfs.info/TrueCloudLab/frostfs-observability/logging/lokicore"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -73,7 +72,6 @@ import (
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@ -107,10 +105,7 @@ type applicationConfiguration struct {
} }
type shardCfg struct { type shardCfg struct {
compress bool compress bool
estimateCompressibility bool
estimateCompressibilityThreshold float64
smallSizeObjectLimit uint64 smallSizeObjectLimit uint64
uncompressableContentType []string uncompressableContentType []string
refillMetabase bool refillMetabase bool
@ -126,10 +121,10 @@ type shardCfg struct {
subStorages []subStorageCfg subStorages []subStorageCfg
gcCfg struct { gcCfg struct {
removerBatchSize int removerBatchSize int
removerSleepInterval time.Duration removerSleepInterval time.Duration
expiredCollectorBatchSize int expiredCollectorBatchSize int
expiredCollectorWorkerCount int expiredCollectorWorkersCount int
} }
writecacheCfg struct { writecacheCfg struct {
@ -222,8 +217,6 @@ func (a *applicationConfiguration) updateShardConfig(c *config.Config, oldConfig
newConfig.refillMetabase = oldConfig.RefillMetabase() newConfig.refillMetabase = oldConfig.RefillMetabase()
newConfig.mode = oldConfig.Mode() newConfig.mode = oldConfig.Mode()
newConfig.compress = oldConfig.Compress() newConfig.compress = oldConfig.Compress()
newConfig.estimateCompressibility = oldConfig.EstimateCompressibility()
newConfig.estimateCompressibilityThreshold = oldConfig.EstimateCompressibilityThreshold()
newConfig.uncompressableContentType = oldConfig.UncompressableContentTypes() newConfig.uncompressableContentType = oldConfig.UncompressableContentTypes()
newConfig.smallSizeObjectLimit = oldConfig.SmallSizeLimit() newConfig.smallSizeObjectLimit = oldConfig.SmallSizeLimit()
@ -256,7 +249,7 @@ func (a *applicationConfiguration) setShardWriteCacheConfig(newConfig *shardCfg,
wc.maxBatchDelay = writeCacheCfg.BoltDB().MaxBatchDelay() wc.maxBatchDelay = writeCacheCfg.BoltDB().MaxBatchDelay()
wc.maxObjSize = writeCacheCfg.MaxObjectSize() wc.maxObjSize = writeCacheCfg.MaxObjectSize()
wc.smallObjectSize = writeCacheCfg.SmallObjectSize() wc.smallObjectSize = writeCacheCfg.SmallObjectSize()
wc.flushWorkerCount = writeCacheCfg.WorkerCount() wc.flushWorkerCount = writeCacheCfg.WorkersNumber()
wc.sizeLimit = writeCacheCfg.SizeLimit() wc.sizeLimit = writeCacheCfg.SizeLimit()
wc.noSync = writeCacheCfg.NoSync() wc.noSync = writeCacheCfg.NoSync()
wc.gcInterval = writeCacheCfg.GCInterval() wc.gcInterval = writeCacheCfg.GCInterval()
@ -328,7 +321,7 @@ func (a *applicationConfiguration) setGCConfig(newConfig *shardCfg, oldConfig *s
newConfig.gcCfg.removerBatchSize = gcCfg.RemoverBatchSize() newConfig.gcCfg.removerBatchSize = gcCfg.RemoverBatchSize()
newConfig.gcCfg.removerSleepInterval = gcCfg.RemoverSleepInterval() newConfig.gcCfg.removerSleepInterval = gcCfg.RemoverSleepInterval()
newConfig.gcCfg.expiredCollectorBatchSize = gcCfg.ExpiredCollectorBatchSize() newConfig.gcCfg.expiredCollectorBatchSize = gcCfg.ExpiredCollectorBatchSize()
newConfig.gcCfg.expiredCollectorWorkerCount = gcCfg.ExpiredCollectorWorkerCount() newConfig.gcCfg.expiredCollectorWorkersCount = gcCfg.ExpiredCollectorWorkersCount()
} }
// internals contains application-specific internals that are created // internals contains application-specific internals that are created
@ -512,11 +505,6 @@ type cfgObject struct {
eaclSource container.EACLSource eaclSource container.EACLSource
// Access policy chain source is used by object service to
// check for operation permissions but this source is also shared with
// control service that dispatches local overrides.
apeChainSource container.AccessPolicyEngineChainSource
pool cfgObjectRoutines pool cfgObjectRoutines
cfgLocalStorage cfgLocalStorage cfgLocalStorage cfgLocalStorage
@ -566,28 +554,22 @@ func initCfg(appCfg *config.Config) *cfg {
key := nodeconfig.Key(appCfg) key := nodeconfig.Key(appCfg)
logPrm, err := c.loggerPrm()
fatalOnErr(err)
logPrm.MetricsNamespace = "frostfs_node"
log, err := logger.NewLogger(logPrm)
fatalOnErr(err)
c.internals = initInternals(appCfg, log)
relayOnly := nodeconfig.Relay(appCfg) relayOnly := nodeconfig.Relay(appCfg)
netState := newNetworkState() netState := newNetworkState()
c.shared = initShared(appCfg, key, netState, relayOnly) c.shared = initShared(appCfg, key, netState, relayOnly)
netState.metrics = c.metricsCollector
logPrm, err := c.loggerPrm()
fatalOnErr(err)
logPrm.SamplingHook = c.metricsCollector.LogMetrics().GetSamplingHook()
log, err := logger.NewLogger(logPrm)
fatalOnErr(err)
if loggerconfig.ToLokiConfig(appCfg).Enabled {
log.Logger = log.Logger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
lokiCore := lokicore.New(core, loggerconfig.ToLokiConfig(appCfg))
return lokiCore
}))
}
c.internals = initInternals(appCfg, log)
c.cfgAccounting = cfgAccounting{ c.cfgAccounting = cfgAccounting{
scriptHash: contractsconfig.Balance(appCfg), scriptHash: contractsconfig.Balance(appCfg),
} }
@ -604,6 +586,9 @@ func initCfg(appCfg *config.Config) *cfg {
user.IDFromKey(&c.ownerIDFromKey, key.PrivateKey.PublicKey) user.IDFromKey(&c.ownerIDFromKey, key.PrivateKey.PublicKey)
c.metricsCollector = metrics.NewNodeMetrics()
netState.metrics = c.metricsCollector
c.onShutdown(c.clientCache.CloseAll) // clean up connections c.onShutdown(c.clientCache.CloseAll) // clean up connections
c.onShutdown(c.bgClientCache.CloseAll) // clean up connections c.onShutdown(c.bgClientCache.CloseAll) // clean up connections
c.onShutdown(c.putClientCache.CloseAll) // clean up connections c.onShutdown(c.putClientCache.CloseAll) // clean up connections
@ -645,15 +630,14 @@ func initShared(appCfg *config.Config, key *keys.PrivateKey, netState *networkSt
} }
return shared{ return shared{
key: key, key: key,
binPublicKey: key.PublicKey().Bytes(), binPublicKey: key.PublicKey().Bytes(),
localAddr: netAddr, localAddr: netAddr,
respSvc: response.NewService(netState), respSvc: response.NewService(netState),
clientCache: cache.NewSDKClientCache(cacheOpts), clientCache: cache.NewSDKClientCache(cacheOpts),
bgClientCache: cache.NewSDKClientCache(cacheOpts), bgClientCache: cache.NewSDKClientCache(cacheOpts),
putClientCache: cache.NewSDKClientCache(cacheOpts), putClientCache: cache.NewSDKClientCache(cacheOpts),
persistate: persistate, persistate: persistate,
metricsCollector: metrics.NewNodeMetrics(),
} }
} }
@ -818,7 +802,6 @@ func (c *cfg) getSubstorageOpts(shCfg shardCfg) []blobstor.SubStorage {
fstree.WithPerm(sRead.perm), fstree.WithPerm(sRead.perm),
fstree.WithDepth(sRead.depth), fstree.WithDepth(sRead.depth),
fstree.WithNoSync(sRead.noSync), fstree.WithNoSync(sRead.noSync),
fstree.WithLogger(c.log),
} }
if c.metricsCollector != nil { if c.metricsCollector != nil {
fstreeOpts = append(fstreeOpts, fstreeOpts = append(fstreeOpts,
@ -850,8 +833,6 @@ func (c *cfg) getShardOpts(shCfg shardCfg) shardOptsWithID {
blobstoreOpts := []blobstor.Option{ blobstoreOpts := []blobstor.Option{
blobstor.WithCompressObjects(shCfg.compress), blobstor.WithCompressObjects(shCfg.compress),
blobstor.WithUncompressableContentTypes(shCfg.uncompressableContentType), blobstor.WithUncompressableContentTypes(shCfg.uncompressableContentType),
blobstor.WithCompressibilityEstimate(shCfg.estimateCompressibility),
blobstor.WithCompressibilityEstimateThreshold(shCfg.estimateCompressibilityThreshold),
blobstor.WithStorages(ss), blobstor.WithStorages(ss),
blobstor.WithLogger(c.log), blobstor.WithLogger(c.log),
} }
@ -888,7 +869,7 @@ func (c *cfg) getShardOpts(shCfg shardCfg) shardOptsWithID {
shard.WithRemoverBatchSize(shCfg.gcCfg.removerBatchSize), shard.WithRemoverBatchSize(shCfg.gcCfg.removerBatchSize),
shard.WithGCRemoverSleepInterval(shCfg.gcCfg.removerSleepInterval), shard.WithGCRemoverSleepInterval(shCfg.gcCfg.removerSleepInterval),
shard.WithExpiredCollectorBatchSize(shCfg.gcCfg.expiredCollectorBatchSize), shard.WithExpiredCollectorBatchSize(shCfg.gcCfg.expiredCollectorBatchSize),
shard.WithExpiredCollectorWorkerCount(shCfg.gcCfg.expiredCollectorWorkerCount), shard.WithExpiredCollectorWorkersCount(shCfg.gcCfg.expiredCollectorWorkersCount),
shard.WithGCWorkerPoolInitializer(func(sz int) util.WorkerPool { shard.WithGCWorkerPoolInitializer(func(sz int) util.WorkerPool {
pool, err := ants.NewPool(sz) pool, err := ants.NewPool(sz)
fatalOnErr(err) fatalOnErr(err)
@ -1062,6 +1043,7 @@ func (c *cfg) signalWatcher(ctx context.Context) {
c.reloadConfig(ctx) c.reloadConfig(ctx)
case syscall.SIGTERM, syscall.SIGINT: case syscall.SIGTERM, syscall.SIGINT:
c.log.Info(logs.FrostFSNodeTerminationSignalHasBeenReceivedStopping) c.log.Info(logs.FrostFSNodeTerminationSignalHasBeenReceivedStopping)
// TODO (@acid-ant): #49 need to cover case when stuck at the middle(node health UNDEFINED or STARTING)
c.shutdown() c.shutdown()
@ -1083,12 +1065,6 @@ func (c *cfg) signalWatcher(ctx context.Context) {
func (c *cfg) reloadConfig(ctx context.Context) { func (c *cfg) reloadConfig(ctx context.Context) {
c.log.Info(logs.FrostFSNodeSIGHUPHasBeenReceivedRereadingConfiguration) c.log.Info(logs.FrostFSNodeSIGHUPHasBeenReceivedRereadingConfiguration)
if !c.compareAndSwapHealthStatus(control.HealthStatus_READY, control.HealthStatus_RECONFIGURING) {
c.log.Info(logs.FrostFSNodeSIGHUPSkip)
return
}
defer c.compareAndSwapHealthStatus(control.HealthStatus_RECONFIGURING, control.HealthStatus_READY)
err := c.readConfig(c.appCfg) err := c.readConfig(c.appCfg)
if err != nil { if err != nil {
c.log.Error(logs.FrostFSNodeConfigurationReading, zap.Error(err)) c.log.Error(logs.FrostFSNodeConfigurationReading, zap.Error(err))
@ -1169,14 +1145,7 @@ func (c *cfg) createTombstoneSource() *tombstone.ExpirationChecker {
} }
func (c *cfg) shutdown() { func (c *cfg) shutdown() {
old := c.swapHealthStatus(control.HealthStatus_SHUTTING_DOWN) c.setHealthStatus(control.HealthStatus_SHUTTING_DOWN)
if old == control.HealthStatus_SHUTTING_DOWN {
c.log.Info(logs.FrostFSNodeShutdownSkip)
return
}
if old == control.HealthStatus_STARTING {
c.log.Warn(logs.FrostFSNodeShutdownWhenNotReady)
}
c.ctxCancel() c.ctxCancel()
c.done <- struct{}{} c.done <- struct{}{}

View file

@ -22,7 +22,7 @@ func TestApiclientSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
require.Equal(t, 15*time.Second, apiclientconfig.DialTimeout(c)) require.Equal(t, 15*time.Second, apiclientconfig.DialTimeout(c))
require.Equal(t, 20*time.Second, apiclientconfig.StreamTimeout(c)) require.Equal(t, 20*time.Second, apiclientconfig.StreamTimeout(c))
require.Equal(t, 30*time.Second, apiclientconfig.ReconnectTimeout(c)) require.Equal(t, 30*time.Second, apiclientconfig.ReconnectTimeout(c))

View file

@ -223,15 +223,3 @@ func parseSizeInBytes(sizeStr string) uint64 {
size := cast.ToFloat64(sizeStr) size := cast.ToFloat64(sizeStr)
return safeMul(size, multiplier) return safeMul(size, multiplier)
} }
// FloatOrDefault reads a configuration value
// from c by name and casts it to float64.
//
// Returns defaultValue if the value can not be casted.
func FloatOrDefault(c *Config, name string, defaultValue float64) float64 {
v, err := cast.ToFloat64E(c.Value(name))
if err != nil {
return defaultValue
}
return v
}

View file

@ -38,6 +38,7 @@ func New(configFile, configDir, envPrefix string) *Config {
configViper.WithConfigFile(configFile), configViper.WithConfigFile(configFile),
configViper.WithConfigDir(configDir), configViper.WithConfigDir(configDir),
configViper.WithEnvPrefix(envPrefix)) configViper.WithEnvPrefix(envPrefix))
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -15,8 +15,8 @@ func TestConfigDir(t *testing.T) {
cfgFileName0 := path.Join(dir, "cfg_00.json") cfgFileName0 := path.Join(dir, "cfg_00.json")
cfgFileName1 := path.Join(dir, "cfg_01.yml") cfgFileName1 := path.Join(dir, "cfg_01.yml")
require.NoError(t, os.WriteFile(cfgFileName0, []byte(`{"storage":{"shard_pool_size":15}}`), 0o777)) require.NoError(t, os.WriteFile(cfgFileName0, []byte(`{"storage":{"shard_pool_size":15}}`), 0777))
require.NoError(t, os.WriteFile(cfgFileName1, []byte("logger:\n level: debug"), 0o777)) require.NoError(t, os.WriteFile(cfgFileName1, []byte("logger:\n level: debug"), 0777))
c := New("", dir, "") c := New("", dir, "")
require.Equal(t, "debug", cast.ToString(c.Sub("logger").Value("level"))) require.Equal(t, "debug", cast.ToString(c.Sub("logger").Value("level")))

View file

@ -35,7 +35,7 @@ func TestContractsSection(t *testing.T) {
expProxy, err := util.Uint160DecodeStringLE("ad7c6b55b737b696e5c82c85445040964a03e97f") expProxy, err := util.Uint160DecodeStringLE("ad7c6b55b737b696e5c82c85445040964a03e97f")
require.NoError(t, err) require.NoError(t, err)
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
balance := contractsconfig.Balance(c) balance := contractsconfig.Balance(c)
container := contractsconfig.Container(c) container := contractsconfig.Container(c)
netmap := contractsconfig.Netmap(c) netmap := contractsconfig.Netmap(c)

View file

@ -24,7 +24,7 @@ func TestControlSection(t *testing.T) {
pubs[0], _ = keys.NewPublicKeyFromString("035839e45d472a3b7769a2a1bd7d54c4ccd4943c3b40f547870e83a8fcbfb3ce11") pubs[0], _ = keys.NewPublicKeyFromString("035839e45d472a3b7769a2a1bd7d54c4ccd4943c3b40f547870e83a8fcbfb3ce11")
pubs[1], _ = keys.NewPublicKeyFromString("028f42cfcb74499d7b15b35d9bff260a1c8d27de4f446a627406a382d8961486d6") pubs[1], _ = keys.NewPublicKeyFromString("028f42cfcb74499d7b15b35d9bff260a1c8d27de4f446a627406a382d8961486d6")
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
require.Equal(t, pubs, controlconfig.AuthorizedKeys(c)) require.Equal(t, pubs, controlconfig.AuthorizedKeys(c))
require.Equal(t, "localhost:8090", controlconfig.GRPC(c).Endpoint()) require.Equal(t, "localhost:8090", controlconfig.GRPC(c).Endpoint())
} }

View file

@ -42,7 +42,7 @@ func TestEngineSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
num := 0 num := 0
require.EqualValues(t, 100, engineconfig.ShardErrorThreshold(c)) require.EqualValues(t, 100, engineconfig.ShardErrorThreshold(c))
@ -74,24 +74,22 @@ func TestEngineSection(t *testing.T) {
require.Equal(t, "tmp/0/cache", wc.Path()) require.Equal(t, "tmp/0/cache", wc.Path())
require.EqualValues(t, 16384, wc.SmallObjectSize()) require.EqualValues(t, 16384, wc.SmallObjectSize())
require.EqualValues(t, 134217728, wc.MaxObjectSize()) require.EqualValues(t, 134217728, wc.MaxObjectSize())
require.EqualValues(t, 30, wc.WorkerCount()) require.EqualValues(t, 30, wc.WorkersNumber())
require.EqualValues(t, 3221225472, wc.SizeLimit()) require.EqualValues(t, 3221225472, wc.SizeLimit())
require.Equal(t, "tmp/0/meta", meta.Path()) require.Equal(t, "tmp/0/meta", meta.Path())
require.Equal(t, fs.FileMode(0o644), meta.BoltDB().Perm()) require.Equal(t, fs.FileMode(0644), meta.BoltDB().Perm())
require.Equal(t, 100, meta.BoltDB().MaxBatchSize()) require.Equal(t, 100, meta.BoltDB().MaxBatchSize())
require.Equal(t, 10*time.Millisecond, meta.BoltDB().MaxBatchDelay()) require.Equal(t, 10*time.Millisecond, meta.BoltDB().MaxBatchDelay())
require.Equal(t, true, sc.Compress()) require.Equal(t, true, sc.Compress())
require.Equal(t, []string{"audio/*", "video/*"}, sc.UncompressableContentTypes()) require.Equal(t, []string{"audio/*", "video/*"}, sc.UncompressableContentTypes())
require.Equal(t, true, sc.EstimateCompressibility())
require.Equal(t, float64(0.7), sc.EstimateCompressibilityThreshold())
require.EqualValues(t, 102400, sc.SmallSizeLimit()) require.EqualValues(t, 102400, sc.SmallSizeLimit())
require.Equal(t, 2, len(ss)) require.Equal(t, 2, len(ss))
blz := blobovniczaconfig.From((*config.Config)(ss[0])) blz := blobovniczaconfig.From((*config.Config)(ss[0]))
require.Equal(t, "tmp/0/blob/blobovnicza", ss[0].Path()) require.Equal(t, "tmp/0/blob/blobovnicza", ss[0].Path())
require.EqualValues(t, 0o644, blz.BoltDB().Perm()) require.EqualValues(t, 0644, blz.BoltDB().Perm())
require.EqualValues(t, 4194304, blz.Size()) require.EqualValues(t, 4194304, blz.Size())
require.EqualValues(t, 1, blz.ShallowDepth()) require.EqualValues(t, 1, blz.ShallowDepth())
require.EqualValues(t, 4, blz.ShallowWidth()) require.EqualValues(t, 4, blz.ShallowWidth())
@ -99,7 +97,7 @@ func TestEngineSection(t *testing.T) {
require.EqualValues(t, 10, blz.LeafWidth()) require.EqualValues(t, 10, blz.LeafWidth())
require.Equal(t, "tmp/0/blob", ss[1].Path()) require.Equal(t, "tmp/0/blob", ss[1].Path())
require.EqualValues(t, 0o644, ss[1].Perm()) require.EqualValues(t, 0644, ss[1].Perm())
fst := fstreeconfig.From((*config.Config)(ss[1])) fst := fstreeconfig.From((*config.Config)(ss[1]))
require.EqualValues(t, 5, fst.Depth()) require.EqualValues(t, 5, fst.Depth())
@ -108,13 +106,13 @@ func TestEngineSection(t *testing.T) {
require.EqualValues(t, 150, gc.RemoverBatchSize()) require.EqualValues(t, 150, gc.RemoverBatchSize())
require.Equal(t, 2*time.Minute, gc.RemoverSleepInterval()) require.Equal(t, 2*time.Minute, gc.RemoverSleepInterval())
require.Equal(t, 1500, gc.ExpiredCollectorBatchSize()) require.Equal(t, 1500, gc.ExpiredCollectorBatchSize())
require.Equal(t, 15, gc.ExpiredCollectorWorkerCount()) require.Equal(t, 15, gc.ExpiredCollectorWorkersCount())
require.Equal(t, false, sc.RefillMetabase()) require.Equal(t, false, sc.RefillMetabase())
require.Equal(t, mode.ReadOnly, sc.Mode()) require.Equal(t, mode.ReadOnly, sc.Mode())
case 1: case 1:
require.Equal(t, "tmp/1/blob/pilorama.db", pl.Path()) require.Equal(t, "tmp/1/blob/pilorama.db", pl.Path())
require.Equal(t, fs.FileMode(0o644), pl.Perm()) require.Equal(t, fs.FileMode(0644), pl.Perm())
require.True(t, pl.NoSync()) require.True(t, pl.NoSync())
require.Equal(t, 5*time.Millisecond, pl.MaxBatchDelay()) require.Equal(t, 5*time.Millisecond, pl.MaxBatchDelay())
require.Equal(t, 100, pl.MaxBatchSize()) require.Equal(t, 100, pl.MaxBatchSize())
@ -125,11 +123,11 @@ func TestEngineSection(t *testing.T) {
require.Equal(t, "tmp/1/cache", wc.Path()) require.Equal(t, "tmp/1/cache", wc.Path())
require.EqualValues(t, 16384, wc.SmallObjectSize()) require.EqualValues(t, 16384, wc.SmallObjectSize())
require.EqualValues(t, 134217728, wc.MaxObjectSize()) require.EqualValues(t, 134217728, wc.MaxObjectSize())
require.EqualValues(t, 30, wc.WorkerCount()) require.EqualValues(t, 30, wc.WorkersNumber())
require.EqualValues(t, 4294967296, wc.SizeLimit()) require.EqualValues(t, 4294967296, wc.SizeLimit())
require.Equal(t, "tmp/1/meta", meta.Path()) require.Equal(t, "tmp/1/meta", meta.Path())
require.Equal(t, fs.FileMode(0o644), meta.BoltDB().Perm()) require.Equal(t, fs.FileMode(0644), meta.BoltDB().Perm())
require.Equal(t, 200, meta.BoltDB().MaxBatchSize()) require.Equal(t, 200, meta.BoltDB().MaxBatchSize())
require.Equal(t, 20*time.Millisecond, meta.BoltDB().MaxBatchDelay()) require.Equal(t, 20*time.Millisecond, meta.BoltDB().MaxBatchDelay())
@ -148,7 +146,7 @@ func TestEngineSection(t *testing.T) {
require.EqualValues(t, 10, blz.LeafWidth()) require.EqualValues(t, 10, blz.LeafWidth())
require.Equal(t, "tmp/1/blob", ss[1].Path()) require.Equal(t, "tmp/1/blob", ss[1].Path())
require.EqualValues(t, 0o644, ss[1].Perm()) require.EqualValues(t, 0644, ss[1].Perm())
fst := fstreeconfig.From((*config.Config)(ss[1])) fst := fstreeconfig.From((*config.Config)(ss[1]))
require.EqualValues(t, 5, fst.Depth()) require.EqualValues(t, 5, fst.Depth())
@ -157,7 +155,7 @@ func TestEngineSection(t *testing.T) {
require.EqualValues(t, 200, gc.RemoverBatchSize()) require.EqualValues(t, 200, gc.RemoverBatchSize())
require.Equal(t, 5*time.Minute, gc.RemoverSleepInterval()) require.Equal(t, 5*time.Minute, gc.RemoverSleepInterval())
require.Equal(t, gcconfig.ExpiredCollectorBatchSizeDefault, gc.ExpiredCollectorBatchSize()) require.Equal(t, gcconfig.ExpiredCollectorBatchSizeDefault, gc.ExpiredCollectorBatchSize())
require.Equal(t, gcconfig.ExpiredCollectorWorkersCountDefault, gc.ExpiredCollectorWorkerCount()) require.Equal(t, gcconfig.ExpiredCollectorWorkersCountDefault, gc.ExpiredCollectorWorkersCount())
require.Equal(t, true, sc.RefillMetabase()) require.Equal(t, true, sc.RefillMetabase())
require.Equal(t, mode.ReadWrite, sc.Mode()) require.Equal(t, mode.ReadWrite, sc.Mode())

View file

@ -9,7 +9,7 @@ import (
type Config config.Config type Config config.Config
// PermDefault are default permission bits for BlobStor data. // PermDefault are default permission bits for BlobStor data.
const PermDefault = 0o660 const PermDefault = 0660
func From(x *config.Config) *Config { func From(x *config.Config) *Config {
return (*Config)(x) return (*Config)(x)

View file

@ -13,7 +13,7 @@ type Config config.Config
const ( const (
// PermDefault is a default permission bits for metabase file. // PermDefault is a default permission bits for metabase file.
PermDefault = 0o660 PermDefault = 0660
) )
// Perm returns the value of "perm" config parameter as a fs.FileMode. // Perm returns the value of "perm" config parameter as a fs.FileMode.

View file

@ -16,11 +16,8 @@ import (
// which provides access to Shard configurations. // which provides access to Shard configurations.
type Config config.Config type Config config.Config
const ( // SmallSizeLimitDefault is a default limit of small objects payload in bytes.
// SmallSizeLimitDefault is a default limit of small objects payload in bytes. const SmallSizeLimitDefault = 1 << 20
SmallSizeLimitDefault = 1 << 20
EstimateCompressibilityThresholdDefault = 0.1
)
// From wraps config section into Config. // From wraps config section into Config.
func From(c *config.Config) *Config { func From(c *config.Config) *Config {
@ -46,30 +43,6 @@ func (x *Config) UncompressableContentTypes() []string {
"compression_exclude_content_types") "compression_exclude_content_types")
} }
// EstimateCompressibility returns the value of "estimate_compressibility" config parameter.
//
// Returns false if the value is not a valid bool.
func (x *Config) EstimateCompressibility() bool {
return config.BoolSafe(
(*config.Config)(x),
"compression_estimate_compressibility",
)
}
// EstimateCompressibilityThreshold returns the value of "estimate_compressibility_threshold" config parameter.
//
// Returns EstimateCompressibilityThresholdDefault if the value is not defined, not valid float or not in range [0.0; 1.0].
func (x *Config) EstimateCompressibilityThreshold() float64 {
v := config.FloatOrDefault(
(*config.Config)(x),
"compression_estimate_compressibility_threshold",
EstimateCompressibilityThresholdDefault)
if v < 0.0 || v > 1.0 {
return EstimateCompressibilityThresholdDefault
}
return v
}
// SmallSizeLimit returns the value of "small_object_size" config parameter. // SmallSizeLimit returns the value of "small_object_size" config parameter.
// //
// Returns SmallSizeLimitDefault if the value is not a positive number. // Returns SmallSizeLimitDefault if the value is not a positive number.

View file

@ -63,14 +63,14 @@ func (x *Config) RemoverSleepInterval() time.Duration {
return RemoverSleepIntervalDefault return RemoverSleepIntervalDefault
} }
// ExpiredCollectorWorkerCount returns the value of "expired_collector_worker_count" // ExpiredCollectorWorkersCount returns the value of "expired_collector_workers_count"
// config parameter. // config parameter.
// //
// Returns ExpiredCollectorWorkersCountDefault if the value is not a positive number. // Returns ExpiredCollectorWorkersCountDefault if the value is not a positive number.
func (x *Config) ExpiredCollectorWorkerCount() int { func (x *Config) ExpiredCollectorWorkersCount() int {
s := config.IntSafe( s := config.IntSafe(
(*config.Config)(x), (*config.Config)(x),
"expired_collector_worker_count", "expired_collector_workers_count",
) )
if s > 0 { if s > 0 {

View file

@ -13,7 +13,7 @@ type Config config.Config
const ( const (
// PermDefault is a default permission bits for metabase file. // PermDefault is a default permission bits for metabase file.
PermDefault = 0o660 PermDefault = 0660
) )
// From wraps config section into Config. // From wraps config section into Config.

View file

@ -106,13 +106,13 @@ func (x *Config) MaxObjectSize() uint64 {
return MaxSizeDefault return MaxSizeDefault
} }
// WorkerCount returns the value of "flush_worker_count" config parameter. // WorkersNumber returns the value of "workers_number" config parameter.
// //
// Returns WorkersNumberDefault if the value is not a positive number. // Returns WorkersNumberDefault if the value is not a positive number.
func (x *Config) WorkerCount() int { func (x *Config) WorkersNumber() int {
c := config.IntSafe( c := config.IntSafe(
(*config.Config)(x), (*config.Config)(x),
"flush_worker_count", "workers_number",
) )
if c > 0 { if c > 0 {

View file

@ -17,7 +17,7 @@ func TestGRPCSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
num := 0 num := 0
IterateEndpoints(c, func(sc *Config) { IterateEndpoints(c, func(sc *Config) {

View file

@ -1,21 +1,12 @@
package loggerconfig package loggerconfig
import ( import (
"os"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
"git.frostfs.info/TrueCloudLab/frostfs-observability/logging/lokicore/loki"
) )
const ( const (
// LevelDefault is a default logger level. // LevelDefault is a default logger level.
LevelDefault = "info" LevelDefault = "info"
subsection = "logger"
lokiSubsection = "loki"
AddressDefault = "localhost:3100"
BatchEntriesNumberDefault = 100
BatchWaitDefault = time.Second
) )
// Level returns the value of "level" config parameter // Level returns the value of "level" config parameter
@ -24,7 +15,7 @@ const (
// Returns LevelDefault if the value is not a non-empty string. // Returns LevelDefault if the value is not a non-empty string.
func Level(c *config.Config) string { func Level(c *config.Config) string {
v := config.StringSafe( v := config.StringSafe(
c.Sub(subsection), c.Sub("logger"),
"level", "level",
) )
if v != "" { if v != "" {
@ -33,44 +24,3 @@ func Level(c *config.Config) string {
return LevelDefault return LevelDefault
} }
// ToLokiConfig extracts loki config.
func ToLokiConfig(c *config.Config) loki.Config {
hostname, _ := os.Hostname()
return loki.Config{
Enabled: config.BoolSafe(c.Sub(subsection).Sub(lokiSubsection), "enabled"),
BatchWait: getBatchWait(c),
BatchEntriesNumber: getBatchEntriesNumber(c),
Endpoint: getEndpoint(c),
Labels: map[string]string{
"hostname": hostname,
},
}
}
func getBatchWait(c *config.Config) time.Duration {
v := config.DurationSafe(c.Sub(subsection).Sub(lokiSubsection), "max_batch_delay")
if v > 0 {
return v
}
return BatchWaitDefault
}
func getBatchEntriesNumber(c *config.Config) int {
v := config.IntSafe(c.Sub(subsection).Sub(lokiSubsection), "max_batch_size")
if v > 0 {
return int(v)
}
return BatchEntriesNumberDefault
}
func getEndpoint(c *config.Config) string {
v := config.StringSafe(c.Sub(subsection).Sub(lokiSubsection), "endpoint")
if v != "" {
return v
}
return AddressDefault
}

View file

@ -17,7 +17,7 @@ func TestLoggerSection_Level(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
v := loggerconfig.Level(c) v := loggerconfig.Level(c)
require.Equal(t, "debug", v) require.Equal(t, "debug", v)
} }

View file

@ -22,7 +22,7 @@ func TestMetricsSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
to := metricsconfig.ShutdownTimeout(c) to := metricsconfig.ShutdownTimeout(c)
addr := metricsconfig.Address(c) addr := metricsconfig.Address(c)

View file

@ -23,12 +23,14 @@ func TestMorphSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
rpcs := []client.Endpoint{ var (
{"wss://rpc1.morph.frostfs.info:40341/ws", 1}, rpcs = []client.Endpoint{
{"wss://rpc2.morph.frostfs.info:40341/ws", 2}, {"wss://rpc1.morph.frostfs.info:40341/ws", 1},
} {"wss://rpc2.morph.frostfs.info:40341/ws", 2},
}
)
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
require.Equal(t, rpcs, morphconfig.RPCEndpoint(c)) require.Equal(t, rpcs, morphconfig.RPCEndpoint(c))
require.Equal(t, 30*time.Second, morphconfig.DialTimeout(c)) require.Equal(t, 30*time.Second, morphconfig.DialTimeout(c))
require.Equal(t, 15*time.Second, morphconfig.CacheTTL(c)) require.Equal(t, 15*time.Second, morphconfig.CacheTTL(c))

View file

@ -56,7 +56,7 @@ func TestNodeSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
key := Key(c) key := Key(c)
addrs := BootstrapAddresses(c) addrs := BootstrapAddresses(c)
attributes := Attributes(c) attributes := Attributes(c)

View file

@ -28,11 +28,11 @@ func Put(c *config.Config) PutConfig {
} }
} }
// PoolSizeRemote returns the value of "remote_pool_size" config parameter. // PoolSizeRemote returns the value of "pool_size_remote" config parameter.
// //
// Returns PutPoolSizeDefault if the value is not a positive number. // Returns PutPoolSizeDefault if the value is not a positive number.
func (g PutConfig) PoolSizeRemote() int { func (g PutConfig) PoolSizeRemote() int {
v := config.Int(g.cfg, "remote_pool_size") v := config.Int(g.cfg, "pool_size_remote")
if v > 0 { if v > 0 {
return int(v) return int(v)
} }
@ -40,11 +40,11 @@ func (g PutConfig) PoolSizeRemote() int {
return PutPoolSizeDefault return PutPoolSizeDefault
} }
// PoolSizeLocal returns the value of "local_pool_size" config parameter. // PoolSizeLocal returns the value of "pool_size_local" config parameter.
// //
// Returns PutPoolSizeDefault if the value is not a positive number. // Returns PutPoolSizeDefault if the value is not a positive number.
func (g PutConfig) PoolSizeLocal() int { func (g PutConfig) PoolSizeLocal() int {
v := config.Int(g.cfg, "local_pool_size") v := config.Int(g.cfg, "pool_size_local")
if v > 0 { if v > 0 {
return int(v) return int(v)
} }

View file

@ -21,7 +21,7 @@ func TestObjectSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
require.Equal(t, 100, objectconfig.Put(c).PoolSizeRemote()) require.Equal(t, 100, objectconfig.Put(c).PoolSizeRemote())
require.Equal(t, 200, objectconfig.Put(c).PoolSizeLocal()) require.Equal(t, 200, objectconfig.Put(c).PoolSizeLocal())
require.EqualValues(t, 10, objectconfig.TombstoneLifetime(c)) require.EqualValues(t, 10, objectconfig.TombstoneLifetime(c))

View file

@ -19,7 +19,7 @@ func TestPolicerSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
require.Equal(t, 15*time.Second, policerconfig.HeadTimeout(c)) require.Equal(t, 15*time.Second, policerconfig.HeadTimeout(c))
} }

View file

@ -25,7 +25,7 @@ func TestProfilerSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
to := profilerconfig.ShutdownTimeout(c) to := profilerconfig.ShutdownTimeout(c)
addr := profilerconfig.Address(c) addr := profilerconfig.Address(c)

View file

@ -20,7 +20,7 @@ func TestReplicatorSection(t *testing.T) {
const path = "../../../../config/example/node" const path = "../../../../config/example/node"
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
require.Equal(t, 15*time.Second, replicatorconfig.PutTimeout(c)) require.Equal(t, 15*time.Second, replicatorconfig.PutTimeout(c))
require.Equal(t, 10, replicatorconfig.PoolSize(c)) require.Equal(t, 10, replicatorconfig.PoolSize(c))
} }

View file

@ -35,7 +35,7 @@ func TestTreeSection(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
expectedKeys = append(expectedKeys, key) expectedKeys = append(expectedKeys, key)
fileConfigTest := func(c *config.Config) { var fileConfigTest = func(c *config.Config) {
treeSec := treeconfig.Tree(c) treeSec := treeconfig.Tree(c)
require.True(t, treeSec.Enabled()) require.True(t, treeSec.Enabled())

View file

@ -63,6 +63,7 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c
// use RPC node as source of Container contract items (with caching) // use RPC node as source of Container contract items (with caching)
cachedContainerStorage := newCachedContainerStorage(cnrSrc, c.cfgMorph.cacheTTL) cachedContainerStorage := newCachedContainerStorage(cnrSrc, c.cfgMorph.cacheTTL)
cachedEACLStorage := newCachedEACLStorage(eACLFetcher, c.cfgMorph.cacheTTL) cachedEACLStorage := newCachedEACLStorage(eACLFetcher, c.cfgMorph.cacheTTL)
cachedContainerLister := newCachedContainerLister(client, c.cfgMorph.cacheTTL)
subscribeToContainerCreation(c, func(e event.Event) { subscribeToContainerCreation(c, func(e event.Event) {
ev := e.(containerEvent.PutSuccess) ev := e.(containerEvent.PutSuccess)
@ -73,6 +74,7 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c
// creation success are most commonly tracked by polling GET op. // creation success are most commonly tracked by polling GET op.
cnr, err := cnrSrc.Get(ev.ID) cnr, err := cnrSrc.Get(ev.ID)
if err == nil { if err == nil {
cachedContainerLister.update(cnr.Value.Owner(), ev.ID, true)
cachedContainerStorage.containerCache.set(ev.ID, cnr, nil) cachedContainerStorage.containerCache.set(ev.ID, cnr, nil)
} else { } else {
// unlike removal, we expect successful receive of the container // unlike removal, we expect successful receive of the container
@ -90,6 +92,16 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c
subscribeToContainerRemoval(c, func(e event.Event) { subscribeToContainerRemoval(c, func(e event.Event) {
ev := e.(containerEvent.DeleteSuccess) ev := e.(containerEvent.DeleteSuccess)
// read owner of the removed container in order to update the listing cache.
// It's strange to read already removed container, but we can successfully hit
// the cache.
// TODO: use owner directly from the event after neofs-contract#256 will become resolved
cnr, err := cachedContainerStorage.Get(ev.ID)
if err == nil {
cachedContainerLister.update(cnr.Value.Owner(), ev.ID, false)
}
cachedContainerStorage.handleRemoval(ev.ID) cachedContainerStorage.handleRemoval(ev.ID)
c.log.Debug(logs.FrostFSNodeContainerRemovalEventsReceipt, c.log.Debug(logs.FrostFSNodeContainerRemovalEventsReceipt,
zap.Stringer("id", ev.ID), zap.Stringer("id", ev.ID),
@ -99,7 +111,7 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c
c.cfgObject.eaclSource = cachedEACLStorage c.cfgObject.eaclSource = cachedEACLStorage
c.cfgObject.cnrSource = cachedContainerStorage c.cfgObject.cnrSource = cachedContainerStorage
cnrRdr.lister = client cnrRdr.lister = cachedContainerLister
cnrRdr.eacl = c.cfgObject.eaclSource cnrRdr.eacl = c.cfgObject.eaclSource
cnrRdr.src = c.cfgObject.cnrSource cnrRdr.src = c.cfgObject.cnrSource

View file

@ -14,8 +14,6 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
const serviceNameControl = "control"
type treeSynchronizer struct { type treeSynchronizer struct {
treeSvc *tree.Service treeSvc *tree.Service
} }
@ -51,7 +49,6 @@ func initControlService(c *cfg) {
controlSvc.WithTreeService(treeSynchronizer{ controlSvc.WithTreeService(treeSynchronizer{
c.treeService, c.treeService,
}), }),
controlSvc.WithAPEChainSource(c.cfgObject.apeChainSource),
) )
lis, err := net.Listen("tcp", endpoint) lis, err := net.Listen("tcp", endpoint)
@ -69,10 +66,7 @@ func initControlService(c *cfg) {
control.RegisterControlServiceServer(c.cfgControlService.server, ctlSvc) control.RegisterControlServiceServer(c.cfgControlService.server, ctlSvc)
c.workers = append(c.workers, newWorkerFromFunc(func(ctx context.Context) { c.workers = append(c.workers, newWorkerFromFunc(func(ctx context.Context) {
runAndLog(ctx, c, serviceNameControl, false, func(context.Context, *cfg) { runAndLog(ctx, c, "control", false, func(context.Context, *cfg) {
c.log.Info(logs.FrostFSNodeStartListeningEndpoint,
zap.String("service", serviceNameControl),
zap.String("endpoint", endpoint))
fatalOnErr(c.cfgControlService.server.Serve(lis)) fatalOnErr(c.cfgControlService.server.Serve(lis))
}) })
})) }))
@ -84,20 +78,10 @@ func (c *cfg) NetmapStatus() control.NetmapStatus {
func (c *cfg) setHealthStatus(st control.HealthStatus) { func (c *cfg) setHealthStatus(st control.HealthStatus) {
c.healthStatus.Store(int32(st)) c.healthStatus.Store(int32(st))
c.metricsCollector.State().SetHealth(int32(st))
}
func (c *cfg) compareAndSwapHealthStatus(oldSt, newSt control.HealthStatus) (swapped bool) { if c.metricsCollector != nil {
if swapped = c.healthStatus.CompareAndSwap(int32(oldSt), int32(newSt)); swapped { c.metricsCollector.State().SetHealth(int32(st))
c.metricsCollector.State().SetHealth(int32(newSt))
} }
return
}
func (c *cfg) swapHealthStatus(st control.HealthStatus) (old control.HealthStatus) {
old = control.HealthStatus(c.healthStatus.Swap(int32(st)))
c.metricsCollector.State().SetHealth(int32(st))
return
} }
func (c *cfg) HealthStatus() control.HealthStatus { func (c *cfg) HealthStatus() control.HealthStatus {

View file

@ -110,8 +110,7 @@ func serveGRPC(c *cfg) {
c.wg.Done() c.wg.Done()
}() }()
c.log.Info(logs.FrostFSNodeStartListeningEndpoint, c.log.Info(logs.FrostFSNodeStartListeningGRPCEndpoint,
zap.String("service", "gRPC"),
zap.Stringer("endpoint", lis.Addr()), zap.Stringer("endpoint", lis.Addr()),
) )

View file

@ -6,9 +6,7 @@ import (
"net/http" "net/http"
"time" "time"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
httputil "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/http" httputil "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/http"
"go.uber.org/zap"
) )
type httpComponent struct { type httpComponent struct {
@ -44,9 +42,6 @@ func (cmp *httpComponent) init(c *cfg) {
cmp.name, cmp.name,
func(ctx context.Context) { func(ctx context.Context) {
runAndLog(ctx, c, cmp.name, false, func(context.Context, *cfg) { runAndLog(ctx, c, cmp.name, false, func(context.Context, *cfg) {
c.log.Info(logs.FrostFSNodeStartListeningEndpoint,
zap.String("service", cmp.name),
zap.String("endpoint", cmp.address))
fatalOnErr(srv.Serve()) fatalOnErr(srv.Serve())
}) })
}, },

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