forked from TrueCloudLab/frostfs-node
Compare commits
5 commits
master
...
bugfix/set
Author | SHA1 | Date | |
---|---|---|---|
81f1e48786 | |||
512a2fa4ce | |||
e05deb6dea | |||
a9b93676e0 | |||
4331bd1143 |
616 changed files with 9966 additions and 18020 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.22 AS builder
|
FROM golang:1.22 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.22 AS builder
|
FROM golang:1.22 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.22 AS builder
|
FROM golang:1.22 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.22 AS builder
|
FROM golang:1.22 as builder
|
||||||
ARG BUILD=now
|
ARG BUILD=now
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
ARG REPO=repository
|
ARG REPO=repository
|
||||||
|
|
|
@ -8,7 +8,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go_versions: [ '1.22', '1.23' ]
|
go_versions: [ '1.21', '1.22' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -9,14 +9,14 @@ jobs:
|
||||||
SKIP: make-lint,go-staticcheck-repo-mod,go-unit-tests,gofumpt
|
SKIP: make-lint,go-staticcheck-repo-mod,go-unit-tests,gofumpt
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
# If we use actions/setup-python from either Github or Gitea,
|
# If we use actions/setup-python from either Github or Gitea,
|
||||||
# the line above fails with a cryptic error about not being able to find python.
|
# the line above fails with a cryptic error about not being able to found python.
|
||||||
# So install everything manually.
|
# So install everything manually.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.23
|
go-version: 1.22
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
run: |
|
run: |
|
||||||
apt update
|
apt update
|
||||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Install linters
|
- name: Install linters
|
||||||
|
@ -25,7 +25,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go_versions: [ '1.22', '1.23' ]
|
go_versions: [ '1.21', '1.22' ]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
@ -48,7 +48,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.22'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
@ -63,7 +63,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Install staticcheck
|
- name: Install staticcheck
|
||||||
|
@ -81,7 +81,7 @@ jobs:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.22'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Install gopls
|
- name: Install gopls
|
||||||
|
@ -89,23 +89,3 @@ jobs:
|
||||||
|
|
||||||
- name: Run gopls
|
- name: Run gopls
|
||||||
run: make gopls-run
|
run: make gopls-run
|
||||||
|
|
||||||
fumpt:
|
|
||||||
name: Run gofumpt
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.23'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install gofumpt
|
|
||||||
run: make fumpt-install
|
|
||||||
|
|
||||||
- name: Run gofumpt
|
|
||||||
run: |
|
|
||||||
make fumpt
|
|
||||||
git diff --exit-code --quiet
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.22'
|
||||||
|
|
||||||
- name: Install govulncheck
|
- name: Install govulncheck
|
||||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
|
|
@ -12,8 +12,7 @@ run:
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||||
formats:
|
format: tab
|
||||||
- format: tab
|
|
||||||
|
|
||||||
# all available settings of specific linters
|
# all available settings of specific linters
|
||||||
linters-settings:
|
linters-settings:
|
||||||
|
@ -38,10 +37,6 @@ linters-settings:
|
||||||
alias:
|
alias:
|
||||||
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
||||||
alias: objectSDK
|
alias: objectSDK
|
||||||
unused:
|
|
||||||
field-writes-are-uses: false
|
|
||||||
exported-fields-are-used: false
|
|
||||||
local-variables-are-used: false
|
|
||||||
custom:
|
custom:
|
||||||
truecloudlab-linters:
|
truecloudlab-linters:
|
||||||
path: bin/linters/external_linters.so
|
path: bin/linters/external_linters.so
|
||||||
|
@ -71,7 +66,7 @@ linters:
|
||||||
- bidichk
|
- bidichk
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- copyloopvar
|
- exportloopref
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
- misspell
|
- misspell
|
||||||
|
|
35
CHANGELOG.md
35
CHANGELOG.md
|
@ -9,41 +9,6 @@ Changelog for FrostFS Node
|
||||||
### Removed
|
### Removed
|
||||||
### Updated
|
### Updated
|
||||||
|
|
||||||
## [v0.42.0]
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Add audit logs for gRPC requests (#1184)
|
|
||||||
- Add CLI command to convert eACL to APE (#1189)
|
|
||||||
- Add `--await` flag to `control set-status` (#60)
|
|
||||||
- `app_info` metric for binary version (#1154)
|
|
||||||
- `--quiet` flag for healthcheck command (#1209)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Deprecate Container.SetEACL RPC (#1219)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Take groups into account during APE processing (#1190)
|
|
||||||
- Handle double SIGHUP correctly (#1145)
|
|
||||||
- Handle empty filenames in tree listing (#1074)
|
|
||||||
- Handle duplicate tree nodes in the split-brain scenario (#1234, #1251)
|
|
||||||
- Remove APE pre-check in Object.GET/HEAD/RANGE RPC (#1249)
|
|
||||||
- Delete EC gc marks and split info (#1257)
|
|
||||||
- Do not search for non-existent objects on deletion (#1261)
|
|
||||||
|
|
||||||
### Updated
|
|
||||||
- Make putting EC chunks more robust (#1233)
|
|
||||||
|
|
||||||
## [v0.41.0]
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Support mTLS for morph client (#1170)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Update shard state metric during shard init (#1174)
|
|
||||||
- Handle ENOSPC in blobovnicza (#1166)
|
|
||||||
- Handle multiple split-infos for EC objects (#1163)
|
|
||||||
- Set `Disabled` mode as the default for components (#1168)
|
|
||||||
|
|
||||||
## [v0.40.0]
|
## [v0.40.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
33
Makefile
33
Makefile
|
@ -4,19 +4,20 @@ SHELL = bash
|
||||||
REPO ?= $(shell go list -m)
|
REPO ?= $(shell go list -m)
|
||||||
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
|
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
|
||||||
|
|
||||||
HUB_IMAGE ?= git.frostfs.info/truecloudlab/frostfs
|
HUB_IMAGE ?= truecloudlab/frostfs
|
||||||
HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
|
HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
|
||||||
|
|
||||||
GO_VERSION ?= 1.22
|
GO_VERSION ?= 1.22
|
||||||
LINT_VERSION ?= 1.60.3
|
LINT_VERSION ?= 1.56.1
|
||||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.7
|
TRUECLOUDLAB_LINT_VERSION ?= 0.0.5
|
||||||
PROTOC_VERSION ?= 25.0
|
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)
|
PROTOGEN_FROSTFS_VERSION ?= $(shell go list -f '{{.Version}}' -m git.frostfs.info/TrueCloudLab/frostfs-api-go/v2)
|
||||||
PROTOC_OS_VERSION=osx-x86_64
|
PROTOC_OS_VERSION=osx-x86_64
|
||||||
ifeq ($(shell uname), Linux)
|
ifeq ($(shell uname), Linux)
|
||||||
PROTOC_OS_VERSION=linux-x86_64
|
PROTOC_OS_VERSION=linux-x86_64
|
||||||
endif
|
endif
|
||||||
STATICCHECK_VERSION ?= 2024.1.1
|
STATICCHECK_VERSION ?= 2023.1.6
|
||||||
ARCH = amd64
|
ARCH = amd64
|
||||||
|
|
||||||
BIN = bin
|
BIN = bin
|
||||||
|
@ -38,16 +39,13 @@ LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT
|
||||||
TMP_DIR := .cache
|
TMP_DIR := .cache
|
||||||
PROTOBUF_DIR ?= $(abspath $(BIN))/protobuf
|
PROTOBUF_DIR ?= $(abspath $(BIN))/protobuf
|
||||||
PROTOC_DIR ?= $(PROTOBUF_DIR)/protoc-v$(PROTOC_VERSION)
|
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)
|
PROTOGEN_FROSTFS_DIR ?= $(PROTOBUF_DIR)/protogen-$(PROTOGEN_FROSTFS_VERSION)
|
||||||
STATICCHECK_DIR ?= $(abspath $(BIN))/staticcheck
|
STATICCHECK_DIR ?= $(abspath $(BIN))/staticcheck
|
||||||
STATICCHECK_VERSION_DIR ?= $(STATICCHECK_DIR)/$(STATICCHECK_VERSION)
|
STATICCHECK_VERSION_DIR ?= $(STATICCHECK_DIR)/$(STATICCHECK_VERSION)
|
||||||
|
|
||||||
SOURCES = $(shell find . -type f -name "*.go" -print)
|
SOURCES = $(shell find . -type f -name "*.go" -print)
|
||||||
|
|
||||||
GOFUMPT_VERSION ?= v0.7.0
|
|
||||||
GOFUMPT_DIR ?= $(abspath $(BIN))/gofumpt
|
|
||||||
GOFUMPT_VERSION_DIR ?= $(GOFUMPT_DIR)/$(GOFUMPT_VERSION)
|
|
||||||
|
|
||||||
GOPLS_VERSION ?= v0.15.1
|
GOPLS_VERSION ?= v0.15.1
|
||||||
GOPLS_DIR ?= $(abspath $(BIN))/gopls
|
GOPLS_DIR ?= $(abspath $(BIN))/gopls
|
||||||
GOPLS_VERSION_DIR ?= $(GOPLS_DIR)/$(GOPLS_VERSION)
|
GOPLS_VERSION_DIR ?= $(GOPLS_DIR)/$(GOPLS_VERSION)
|
||||||
|
@ -105,15 +103,17 @@ export-metrics: dep
|
||||||
|
|
||||||
# Regenerate proto files:
|
# Regenerate proto files:
|
||||||
protoc:
|
protoc:
|
||||||
@if [ ! -d "$(PROTOC_DIR)" ] || [ ! -d "$(PROTOGEN_FROSTFS_DIR)" ]; then \
|
@if [ ! -d "$(PROTOC_DIR)" ] || [ ! -d "$(PROTOC_GEN_GO_DIR)" ] || [ ! -d "$(PROTOGEN_FROSTFS_DIR)" ]; then \
|
||||||
make protoc-install; \
|
make protoc-install; \
|
||||||
fi
|
fi
|
||||||
@for f in `find . -type f -name '*.proto' -not -path './bin/*'`; do \
|
@for f in `find . -type f -name '*.proto' -not -path './bin/*'`; do \
|
||||||
echo "⇒ Processing $$f "; \
|
echo "⇒ Processing $$f "; \
|
||||||
$(PROTOC_DIR)/bin/protoc \
|
$(PROTOC_DIR)/bin/protoc \
|
||||||
--proto_path=.:$(PROTOC_DIR)/include:/usr/local/include \
|
--proto_path=.:$(PROTOC_DIR)/include:/usr/local/include \
|
||||||
|
--plugin=protoc-gen-go=$(PROTOC_GEN_GO_DIR)/protoc-gen-go \
|
||||||
--plugin=protoc-gen-go-frostfs=$(PROTOGEN_FROSTFS_DIR)/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-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
|
||||||
|
@ -126,6 +126,8 @@ protoc-install:
|
||||||
@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'
|
@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)
|
@unzip -q -o $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip -d $(PROTOC_DIR)
|
||||||
@rm $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip
|
@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..."
|
@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)
|
@GOBIN=$(PROTOGEN_FROSTFS_DIR) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/protogen@$(PROTOGEN_FROSTFS_VERSION)
|
||||||
|
|
||||||
|
@ -163,19 +165,10 @@ imports:
|
||||||
@echo "⇒ Processing goimports check"
|
@echo "⇒ Processing goimports check"
|
||||||
@goimports -w cmd/ pkg/ misc/
|
@goimports -w cmd/ pkg/ misc/
|
||||||
|
|
||||||
# Install gofumpt
|
|
||||||
fumpt-install:
|
|
||||||
@rm -rf $(GOFUMPT_DIR)
|
|
||||||
@mkdir $(GOFUMPT_DIR)
|
|
||||||
@GOBIN=$(GOFUMPT_VERSION_DIR) go install mvdan.cc/gofumpt@$(GOFUMPT_VERSION)
|
|
||||||
|
|
||||||
# Run gofumpt
|
# Run gofumpt
|
||||||
fumpt:
|
fumpt:
|
||||||
@if [ ! -d "$(GOFUMPT_VERSION_DIR)" ]; then \
|
|
||||||
make fumpt-install; \
|
|
||||||
fi
|
|
||||||
@echo "⇒ Processing gofumpt check"
|
@echo "⇒ Processing gofumpt check"
|
||||||
$(GOFUMPT_VERSION_DIR)/gofumpt -l -w cmd/ pkg/ misc/
|
@gofumpt -l -w cmd/ pkg/ misc/
|
||||||
|
|
||||||
# Run Unit Test with go test
|
# Run Unit Test with go test
|
||||||
test: GOFLAGS ?= "-count=1"
|
test: GOFLAGS ?= "-count=1"
|
||||||
|
@ -197,7 +190,7 @@ lint-install:
|
||||||
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
||||||
@rm -rf $(TMP_DIR)/linters
|
@rm -rf $(TMP_DIR)/linters
|
||||||
@rmdir $(TMP_DIR) 2>/dev/null || true
|
@rmdir $(TMP_DIR) 2>/dev/null || true
|
||||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install -trimpath github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
||||||
|
|
||||||
# Run linters
|
# Run linters
|
||||||
lint:
|
lint:
|
||||||
|
|
17
README.md
17
README.md
|
@ -7,8 +7,9 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[![Report](https://goreportcard.com/badge/git.frostfs.info/TrueCloudLab/frostfs-node)](https://goreportcard.com/report/git.frostfs.info/TrueCloudLab/frostfs-node)
|
[![Report](https://goreportcard.com/badge/github.com/TrueCloudLab/frostfs-node)](https://goreportcard.com/report/github.com/TrueCloudLab/frostfs-node)
|
||||||
![Release (latest)](https://git.frostfs.info/TrueCloudLab/frostfs-node/badges/release.svg)
|
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/TrueCloudLab/frostfs-node?sort=semver)
|
||||||
|
![License](https://img.shields.io/github/license/TrueCloudLab/frostfs-node.svg?style=popout)
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
|
@ -32,8 +33,8 @@ manipulate large amounts of data without paying a prohibitive price.
|
||||||
|
|
||||||
FrostFS has a native [gRPC API](https://git.frostfs.info/TrueCloudLab/frostfs-api) and has
|
FrostFS has a native [gRPC API](https://git.frostfs.info/TrueCloudLab/frostfs-api) and has
|
||||||
protocol gateways for popular protocols such as [AWS
|
protocol gateways for popular protocols such as [AWS
|
||||||
S3](https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw),
|
S3](https://github.com/TrueCloudLab/frostfs-s3-gw),
|
||||||
[HTTP](https://git.frostfs.info/TrueCloudLab/frostfs-http-gw),
|
[HTTP](https://github.com/TrueCloudLab/frostfs-http-gw),
|
||||||
[FUSE](https://wikipedia.org/wiki/Filesystem_in_Userspace) and
|
[FUSE](https://wikipedia.org/wiki/Filesystem_in_Userspace) and
|
||||||
[sFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) allowing
|
[sFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) allowing
|
||||||
developers to integrate applications without rewriting their code.
|
developers to integrate applications without rewriting their code.
|
||||||
|
@ -44,11 +45,11 @@ Now, we only support GNU/Linux on amd64 CPUs with AVX/AVX2 instructions. More
|
||||||
platforms will be officially supported after release `1.0`.
|
platforms will be officially supported after release `1.0`.
|
||||||
|
|
||||||
The latest version of frostfs-node works with frostfs-contract
|
The latest version of frostfs-node works with frostfs-contract
|
||||||
[v0.19.2](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases/tag/v0.19.2).
|
[v0.16.0](https://github.com/TrueCloudLab/frostfs-contract/releases/tag/v0.16.0).
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
To make all binaries you need Go 1.22+ and `make`:
|
To make all binaries you need Go 1.21+ and `make`:
|
||||||
```
|
```
|
||||||
make all
|
make all
|
||||||
```
|
```
|
||||||
|
@ -70,7 +71,7 @@ make docker/bin/frostfs-<name> # build a specific binary
|
||||||
|
|
||||||
## Docker images
|
## Docker images
|
||||||
|
|
||||||
To make docker images suitable for use in [frostfs-dev-env](https://git.frostfs.info/TrueCloudLab/frostfs-dev-env/) use:
|
To make docker images suitable for use in [frostfs-dev-env](https://github.com/TrueCloudLab/frostfs-dev-env/) use:
|
||||||
```
|
```
|
||||||
make images
|
make images
|
||||||
```
|
```
|
||||||
|
@ -124,7 +125,7 @@ the feature/topic you are going to implement.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
FrostFS is maintained by [True Cloud Lab](https://git.frostfs.info/TrueCloudLab/) with the help and
|
FrostFS is maintained by [True Cloud Lab](https://github.com/TrueCloudLab/) with the help and
|
||||||
contributions from community members.
|
contributions from community members.
|
||||||
|
|
||||||
Please see [CREDITS](CREDITS.md) for details.
|
Please see [CREDITS](CREDITS.md) for details.
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
v0.42.0
|
v0.40.0
|
||||||
|
|
|
@ -56,8 +56,7 @@ credentials: # passwords for consensus node / alphabet wallets
|
||||||
#### Network deployment
|
#### Network deployment
|
||||||
|
|
||||||
- `generate-alphabet` generates a set of wallets for consensus and
|
- `generate-alphabet` generates a set of wallets for consensus and
|
||||||
Alphabet nodes. The list of the name for alphabet wallets(no gaps between names allowed, order is important):
|
Alphabet nodes.
|
||||||
- az, buky, vedi, glagoli, dobro, yest, zhivete, dzelo, zemlja, izhe, izhei, gerv, kako, ljudi, mislete, nash, on, pokoj, rtsi, slovo, tverdo, uk
|
|
||||||
|
|
||||||
- `init` initializes the sidechain by deploying smart contracts and
|
- `init` initializes the sidechain by deploying smart contracts and
|
||||||
setting provided FrostFS network configuration.
|
setting provided FrostFS network configuration.
|
||||||
|
|
|
@ -9,8 +9,8 @@ related configuration details.
|
||||||
|
|
||||||
To follow this guide you need:
|
To follow this guide you need:
|
||||||
- latest released version of [neo-go](https://github.com/nspcc-dev/neo-go/releases) (v0.97.2 at the moment),
|
- latest released version of [neo-go](https://github.com/nspcc-dev/neo-go/releases) (v0.97.2 at the moment),
|
||||||
- latest released version of [frostfs-adm](https://git.frostfs.info/TrueCloudLab/frostfs-node/releases) utility (v0.42.9 at the moment),
|
- latest released version of [frostfs-adm](https://github.com/TrueCloudLab/frostfs-node/releases) utility (v0.25.1 at the moment),
|
||||||
- latest released version of compiled [frostfs-contract](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases) (v0.19.2 at the moment).
|
- latest released version of compiled [frostfs-contract](https://github.com/TrueCloudLab/frostfs-contract/releases) (v0.11.0 at the moment).
|
||||||
|
|
||||||
## Step 1: Prepare network configuration
|
## Step 1: Prepare network configuration
|
||||||
|
|
||||||
|
@ -64,11 +64,6 @@ alphabet-wallets: /home/user/deploy/alphabet-wallets
|
||||||
wallet[0]: hunter2
|
wallet[0]: hunter2
|
||||||
```
|
```
|
||||||
|
|
||||||
This command generates wallets with the following names:
|
|
||||||
- az, buky, vedi, glagoli, dobro, yest, zhivete, dzelo, zemlja, izhe, izhei, gerv, kako, ljudi, mislete, nash, on, pokoj, rtsi, slovo, tverdo, uk
|
|
||||||
|
|
||||||
No gaps between names allowed, order is important.
|
|
||||||
|
|
||||||
Do not lose wallet files and network config. Store it in an encrypted backed up
|
Do not lose wallet files and network config. Store it in an encrypted backed up
|
||||||
storage.
|
storage.
|
||||||
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package metabase
|
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
|
||||||
|
|
||||||
// RootCmd is a root command of config section.
|
|
||||||
var RootCmd = &cobra.Command{
|
|
||||||
Use: "metabase",
|
|
||||||
Short: "Section for metabase commands",
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RootCmd.AddCommand(UpgradeCmd)
|
|
||||||
|
|
||||||
initUpgradeCommand()
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
package metabase
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
|
||||||
engineconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine"
|
|
||||||
shardconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/engine/shard"
|
|
||||||
morphconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/morph"
|
|
||||||
nodeconfig "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/node"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
|
||||||
morphcontainer "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
noCompactFlag = "no-compact"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errNoPathsFound = errors.New("no metabase paths found")
|
|
||||||
errNoMorphEndpointsFound = errors.New("no morph endpoints found")
|
|
||||||
)
|
|
||||||
|
|
||||||
var UpgradeCmd = &cobra.Command{
|
|
||||||
Use: "upgrade",
|
|
||||||
Short: "Upgrade metabase to latest version",
|
|
||||||
RunE: upgrade,
|
|
||||||
}
|
|
||||||
|
|
||||||
func upgrade(cmd *cobra.Command, _ []string) error {
|
|
||||||
configFile, err := cmd.Flags().GetString(commonflags.ConfigFlag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
configDir, err := cmd.Flags().GetString(commonflags.ConfigDirFlag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
appCfg := config.New(configFile, configDir, config.EnvPrefix)
|
|
||||||
paths, err := getMetabasePaths(appCfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(paths) == 0 {
|
|
||||||
return errNoPathsFound
|
|
||||||
}
|
|
||||||
cmd.Println("found", len(paths), "metabases:")
|
|
||||||
for i, path := range paths {
|
|
||||||
cmd.Println(i+1, ":", path)
|
|
||||||
}
|
|
||||||
mc, err := createMorphClient(cmd.Context(), appCfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer mc.Close()
|
|
||||||
civ, err := createContainerInfoProvider(mc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
noCompact, _ := cmd.Flags().GetBool(noCompactFlag)
|
|
||||||
result := make(map[string]bool)
|
|
||||||
var resultGuard sync.Mutex
|
|
||||||
eg, ctx := errgroup.WithContext(cmd.Context())
|
|
||||||
for _, path := range paths {
|
|
||||||
eg.Go(func() error {
|
|
||||||
var success bool
|
|
||||||
cmd.Println("upgrading metabase", path, "...")
|
|
||||||
if err := meta.Upgrade(ctx, path, !noCompact, civ, func(a ...any) {
|
|
||||||
cmd.Println(append([]any{time.Now().Format(time.RFC3339), ":", path, ":"}, a...)...)
|
|
||||||
}); err != nil {
|
|
||||||
cmd.Println("error: failed to upgrade metabase", path, ":", err)
|
|
||||||
} else {
|
|
||||||
success = true
|
|
||||||
cmd.Println("metabase", path, "upgraded successfully")
|
|
||||||
}
|
|
||||||
resultGuard.Lock()
|
|
||||||
result[path] = success
|
|
||||||
resultGuard.Unlock()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := eg.Wait(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for mb, ok := range result {
|
|
||||||
if ok {
|
|
||||||
cmd.Println(mb, ": success")
|
|
||||||
} else {
|
|
||||||
cmd.Println(mb, ": failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMetabasePaths(appCfg *config.Config) ([]string, error) {
|
|
||||||
var paths []string
|
|
||||||
if err := engineconfig.IterateShards(appCfg, false, func(sc *shardconfig.Config) error {
|
|
||||||
paths = append(paths, sc.Metabase().Path())
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return nil, fmt.Errorf("get metabase paths: %w", err)
|
|
||||||
}
|
|
||||||
return paths, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMorphClient(ctx context.Context, appCfg *config.Config) (*client.Client, error) {
|
|
||||||
addresses := morphconfig.RPCEndpoint(appCfg)
|
|
||||||
if len(addresses) == 0 {
|
|
||||||
return nil, errNoMorphEndpointsFound
|
|
||||||
}
|
|
||||||
key := nodeconfig.Key(appCfg)
|
|
||||||
cli, err := client.New(ctx,
|
|
||||||
key,
|
|
||||||
client.WithDialTimeout(morphconfig.DialTimeout(appCfg)),
|
|
||||||
client.WithEndpoints(addresses...),
|
|
||||||
client.WithSwitchInterval(morphconfig.SwitchInterval(appCfg)),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("create morph client:%w", err)
|
|
||||||
}
|
|
||||||
return cli, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createContainerInfoProvider(cli *client.Client) (container.InfoProvider, error) {
|
|
||||||
sh, err := cli.NNSContractAddress(client.NNSContainerContractName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("resolve container contract hash: %w", err)
|
|
||||||
}
|
|
||||||
cc, err := morphcontainer.NewFromMorph(cli, sh, 0, morphcontainer.TryNotary())
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("create morph container client: %w", err)
|
|
||||||
}
|
|
||||||
return container.NewInfoProvider(func() (container.Source, error) {
|
|
||||||
return morphcontainer.AsContainerSource(cc), nil
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initUpgradeCommand() {
|
|
||||||
flags := UpgradeCmd.Flags()
|
|
||||||
flags.Bool(noCompactFlag, false, "Do not compact upgraded metabase file")
|
|
||||||
}
|
|
|
@ -38,12 +38,6 @@ var (
|
||||||
func parseTarget(cmd *cobra.Command) policyengine.Target {
|
func parseTarget(cmd *cobra.Command) policyengine.Target {
|
||||||
name, _ := cmd.Flags().GetString(targetNameFlag)
|
name, _ := cmd.Flags().GetString(targetNameFlag)
|
||||||
typ, err := parseTargetType(cmd)
|
typ, err := parseTargetType(cmd)
|
||||||
|
|
||||||
// interpret "root" namespace as empty
|
|
||||||
if typ == policyengine.Namespace && name == "root" {
|
|
||||||
name = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
commonCmd.ExitOnErr(cmd, "read target type error: %w", err)
|
commonCmd.ExitOnErr(cmd, "read target type error: %w", err)
|
||||||
|
|
||||||
return policyengine.Target{
|
return policyengine.Target{
|
||||||
|
@ -121,7 +115,7 @@ func newPolicyContractReaderInterface(cmd *cobra.Command) (*morph.ContractStorag
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
var ch util.Uint160
|
var ch util.Uint160
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
nnsCs, err := helper.GetContractByID(r, 1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
|
||||||
ch, err = helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
ch, err = helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||||
|
@ -144,7 +138,7 @@ func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *he
|
||||||
|
|
||||||
var ch util.Uint160
|
var ch util.Uint160
|
||||||
r := management.NewReader(ac.Invoker)
|
r := management.NewReader(ac.Invoker)
|
||||||
nnsCs, err := helper.GetContractByID(r, 1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
|
||||||
ch, err = helper.NNSResolveHash(ac.Invoker, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
ch, err = helper.NNSResolveHash(ac.Invoker, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||||
|
|
|
@ -60,7 +60,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
if dumpStorage || dumpAlphabet || dumpProxy {
|
if dumpStorage || dumpAlphabet || dumpProxy {
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
nnsCs, err = helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
cs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func SetConfigCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func getContainerContractHash(cmd *cobra.Command, inv *invoker.Invoker) (util.Ui
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
nnsCs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
@ -139,12 +139,13 @@ func dumpContainers(cmd *cobra.Command, _ []string) error {
|
||||||
func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invoker, id []byte) (*Container, error) {
|
func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invoker, id []byte) (*Container, error) {
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, id)
|
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, id)
|
||||||
|
emit.AppCall(bw.BinWriter, ch, "eACL", callflag.All, id)
|
||||||
res, err := inv.Run(bw.Bytes())
|
res, err := inv.Run(bw.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't get container info: %w", err)
|
return nil, fmt.Errorf("can't get container info: %w", err)
|
||||||
}
|
}
|
||||||
if len(res.Stack) != 1 {
|
if len(res.Stack) != 2 {
|
||||||
return nil, fmt.Errorf("%w: expected 1 items on stack", errInvalidContainerResponse)
|
return nil, fmt.Errorf("%w: expected 2 items on stack", errInvalidContainerResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt := new(Container)
|
cnt := new(Container)
|
||||||
|
@ -153,6 +154,14 @@ func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invo
|
||||||
return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ea := new(EACL)
|
||||||
|
err = ea.FromStackItem(res.Stack[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
|
}
|
||||||
|
if len(ea.Value) != 0 {
|
||||||
|
cnt.EACL = ea
|
||||||
|
}
|
||||||
return cnt, nil
|
return cnt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +258,10 @@ func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd
|
||||||
func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
||||||
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
||||||
cnt.Value, cnt.Signature, cnt.PublicKey, cnt.Token)
|
cnt.Value, cnt.Signature, cnt.PublicKey, cnt.Token)
|
||||||
|
if ea := cnt.EACL; ea != nil {
|
||||||
|
emit.AppCall(bw.BinWriter, ch, "setEACL", callflag.All,
|
||||||
|
ea.Value, ea.Signature, ea.PublicKey, ea.Token)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isContainerRestored(cmd *cobra.Command, wCtx *helper.InitializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
func isContainerRestored(cmd *cobra.Command, wCtx *helper.InitializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
||||||
|
@ -291,7 +304,7 @@ func parseContainers(filename string) ([]Container, error) {
|
||||||
|
|
||||||
func fetchContainerContractHash(wCtx *helper.InitializeContext) (util.Uint160, error) {
|
func fetchContainerContractHash(wCtx *helper.InitializeContext) (util.Uint160, error) {
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
nnsCs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
@ -309,6 +322,15 @@ type Container struct {
|
||||||
Signature []byte `json:"signature"`
|
Signature []byte `json:"signature"`
|
||||||
PublicKey []byte `json:"public_key"`
|
PublicKey []byte `json:"public_key"`
|
||||||
Token []byte `json:"token"`
|
Token []byte `json:"token"`
|
||||||
|
EACL *EACL `json:"eacl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EACL represents extended ACL struct in contract storage.
|
||||||
|
type EACL struct {
|
||||||
|
Value []byte `json:"value"`
|
||||||
|
Signature []byte `json:"signature"`
|
||||||
|
PublicKey []byte `json:"public_key"`
|
||||||
|
Token []byte `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStackItem implements stackitem.Convertible.
|
// ToStackItem implements stackitem.Convertible.
|
||||||
|
@ -355,6 +377,50 @@ func (c *Container) FromStackItem(item stackitem.Item) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToStackItem implements stackitem.Convertible.
|
||||||
|
func (c *EACL) ToStackItem() (stackitem.Item, error) {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{
|
||||||
|
stackitem.NewByteArray(c.Value),
|
||||||
|
stackitem.NewByteArray(c.Signature),
|
||||||
|
stackitem.NewByteArray(c.PublicKey),
|
||||||
|
stackitem.NewByteArray(c.Token),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem implements stackitem.Convertible.
|
||||||
|
func (c *EACL) FromStackItem(item stackitem.Item) error {
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok || len(arr) != 4 {
|
||||||
|
return errors.New("invalid stack item type")
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := arr[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("invalid eACL value")
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := arr[1].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("invalid eACL signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := arr[2].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("invalid eACL public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := arr[3].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("invalid eACL token")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Value = value
|
||||||
|
c.Signature = sig
|
||||||
|
c.PublicKey = pub
|
||||||
|
c.Token = tok
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// getCIDFilterFunc returns filtering function for container IDs.
|
// getCIDFilterFunc returns filtering function for container IDs.
|
||||||
// Raw byte slices are used because it works with structures returned
|
// Raw byte slices are used because it works with structures returned
|
||||||
// from contract.
|
// from contract.
|
||||||
|
|
|
@ -79,7 +79,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
nnsCs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(invoker.New(c, nil))
|
r := management.NewReader(invoker.New(c, nil))
|
||||||
cs, err := helper.GetContractByID(r, 1)
|
cs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
if irSize != 0 {
|
if irSize != 0 {
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
for i := range irSize {
|
for i := 0; i < irSize; i++ {
|
||||||
emit.AppCall(bw.BinWriter, cs.Hash, "resolve", callflag.ReadOnly,
|
emit.AppCall(bw.BinWriter, cs.Hash, "resolve", callflag.ReadOnly,
|
||||||
helper.GetAlphabetNNSDomain(i),
|
helper.GetAlphabetNNSDomain(i),
|
||||||
int64(nns.TXT))
|
int64(nns.TXT))
|
||||||
|
@ -79,7 +79,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("can't fetch info from NNS: %w", err)
|
return fmt.Errorf("can't fetch info from NNS: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range irSize {
|
for i := 0; i < irSize; i++ {
|
||||||
info := contractDumpInfo{name: fmt.Sprintf("alphabet %d", i)}
|
info := contractDumpInfo{name: fmt.Sprintf("alphabet %d", i)}
|
||||||
if h, err := helper.ParseNNSResolveResult(alphaRes.Stack[i]); err == nil {
|
if h, err := helper.ParseNNSResolveResult(alphaRes.Stack[i]); err == nil {
|
||||||
info.hash = h
|
info.hash = h
|
||||||
|
|
|
@ -3,32 +3,27 @@ package frostfsid
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
frostfsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
frostfsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
||||||
frostfsidrpclient "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/frostfsid"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/google/uuid"
|
|
||||||
"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/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"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/rpcclient/invoker"
|
|
||||||
"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/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"
|
||||||
"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/stackitem"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const iteratorBatchSize = 1
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
namespaceFlag = "namespace"
|
namespaceFlag = "namespace"
|
||||||
subjectNameFlag = "subject-name"
|
subjectNameFlag = "subject-name"
|
||||||
|
@ -258,15 +253,21 @@ func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func frostfsidListNamespaces(cmd *cobra.Command, _ []string) {
|
func frostfsidListNamespaces(cmd *cobra.Command, _ []string) {
|
||||||
inv, _, hash := initInvoker(cmd)
|
c, err := helper.GetN3Client(viper.GetViper())
|
||||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
|
||||||
sessionID, it, err := reader.ListNamespaces()
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
|
||||||
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
|
||||||
|
|
||||||
namespaces, err := frostfsidclient.ParseNamespaces(items)
|
inv := invoker.New(c, nil)
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse namespace: %w", err)
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
|
cs, err := r.GetContractByID(1)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
||||||
|
|
||||||
|
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract))
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err)
|
||||||
|
|
||||||
|
item, err := commonclient.ReadIteratorItems(inv, 100, nmHash, "listNamespaces")
|
||||||
|
commonCmd.ExitOnErr(cmd, "list namespaces: %w", err)
|
||||||
|
namespaces, _ := frostfsidclient.ParseNamespaces(item)
|
||||||
sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name })
|
sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name })
|
||||||
|
|
||||||
for _, namespace := range namespaces {
|
for _, namespace := range namespaces {
|
||||||
|
@ -308,14 +309,20 @@ func frostfsidDeleteSubject(cmd *cobra.Command, _ []string) {
|
||||||
|
|
||||||
func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
||||||
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
||||||
ns := getFrostfsIDNamespace(cmd)
|
c, err := helper.GetN3Client(viper.GetViper())
|
||||||
inv, _, hash := initInvoker(cmd)
|
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
|
||||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
|
||||||
sessionID, it, err := reader.ListNamespaceSubjects(ns)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
|
||||||
|
|
||||||
subAddresses, err := frostfsidclient.UnwrapArrayOfUint160(readIterator(inv, &it, iteratorBatchSize, sessionID))
|
inv := invoker.New(c, nil)
|
||||||
commonCmd.ExitOnErr(cmd, "can't unwrap: %w", err)
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
|
cs, err := r.GetContractByID(1)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
||||||
|
|
||||||
|
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract))
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err)
|
||||||
|
|
||||||
|
ns := getFrostfsIDNamespace(cmd)
|
||||||
|
subAddresses, _ := frostfsidclient.UnwrapArrayOfUint160(commonclient.ReadIteratorItems(inv, 100, nmHash, "listNamespaceSubjects", ns))
|
||||||
|
|
||||||
sort.Slice(subAddresses, func(i, j int) bool { return subAddresses[i].Less(subAddresses[j]) })
|
sort.Slice(subAddresses, func(i, j int) bool { return subAddresses[i].Less(subAddresses[j]) })
|
||||||
|
|
||||||
|
@ -325,14 +332,11 @@ func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionID, it, err := reader.ListSubjects()
|
items, err := unwrap.Array(inv.Call(nmHash, "getSubject", addr))
|
||||||
commonCmd.ExitOnErr(cmd, "can't get subject: %w", err)
|
commonCmd.ExitOnErr(cmd, "get subject: %w", err)
|
||||||
|
|
||||||
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
|
||||||
|
|
||||||
subj, err := frostfsidclient.ParseSubject(items)
|
subj, err := frostfsidclient.ParseSubject(items)
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse subject: %w", err)
|
commonCmd.ExitOnErr(cmd, "get subject: %w", err)
|
||||||
|
|
||||||
cmd.Printf("%s (%s)\n", address.Uint160ToString(addr), subj.Name)
|
cmd.Printf("%s (%s)\n", address.Uint160ToString(addr), subj.Name)
|
||||||
}
|
}
|
||||||
|
@ -367,17 +371,24 @@ func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func frostfsidListGroups(cmd *cobra.Command, _ []string) {
|
func frostfsidListGroups(cmd *cobra.Command, _ []string) {
|
||||||
inv, _, hash := initInvoker(cmd)
|
c, err := helper.GetN3Client(viper.GetViper())
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
|
||||||
|
|
||||||
|
inv := invoker.New(c, nil)
|
||||||
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
|
cs, err := r.GetContractByID(1)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
||||||
|
|
||||||
|
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract))
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err)
|
||||||
|
|
||||||
ns := getFrostfsIDNamespace(cmd)
|
ns := getFrostfsIDNamespace(cmd)
|
||||||
|
items, err := commonclient.ReadIteratorItems(inv, 100, nmHash, "listGroups", ns)
|
||||||
|
commonCmd.ExitOnErr(cmd, "list groups: %w", err)
|
||||||
|
|
||||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
|
||||||
sessionID, it, err := reader.ListGroups(ns)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
|
||||||
|
|
||||||
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't list groups: %w", err)
|
|
||||||
groups, err := frostfsidclient.ParseGroups(items)
|
groups, err := frostfsidclient.ParseGroups(items)
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse groups: %w", err)
|
commonCmd.ExitOnErr(cmd, "list groups: %w", err)
|
||||||
|
|
||||||
sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name })
|
sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name })
|
||||||
|
|
||||||
|
@ -416,19 +427,12 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) {
|
||||||
ns := getFrostfsIDNamespace(cmd)
|
ns := getFrostfsIDNamespace(cmd)
|
||||||
groupID := getFrostfsIDGroupID(cmd)
|
groupID := getFrostfsIDGroupID(cmd)
|
||||||
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
||||||
inv, cs, hash := initInvoker(cmd)
|
|
||||||
_, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract))
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err)
|
|
||||||
|
|
||||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
ffsid, err := newFrostfsIDClient(cmd)
|
||||||
sessionID, it, err := reader.ListGroupSubjects(ns, big.NewInt(groupID))
|
commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
|
||||||
commonCmd.ExitOnErr(cmd, "can't list groups: %w", err)
|
|
||||||
|
|
||||||
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
subjects, err := ffsid.roCli.ListGroupSubjects(ns, groupID)
|
||||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
commonCmd.ExitOnErr(cmd, "list group subjects: %w", err)
|
||||||
|
|
||||||
subjects, err := frostfsidclient.UnwrapArrayOfUint160(items, err)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't unwrap: %w", err)
|
|
||||||
|
|
||||||
sort.Slice(subjects, func(i, j int) bool { return subjects[i].Less(subjects[j]) })
|
sort.Slice(subjects, func(i, j int) bool { return subjects[i].Less(subjects[j]) })
|
||||||
|
|
||||||
|
@ -438,10 +442,9 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err := reader.GetSubject(subjAddr)
|
subj, err := ffsid.roCli.GetSubject(subjAddr)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get subject: %w", err)
|
commonCmd.ExitOnErr(cmd, "get subject: %w", err)
|
||||||
subj, err := frostfsidclient.ParseSubject(items)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse subject: %w", err)
|
|
||||||
cmd.Printf("%s (%s)\n", address.Uint160ToString(subjAddr), subj.Name)
|
cmd.Printf("%s (%s)\n", address.Uint160ToString(subjAddr), subj.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,11 +459,11 @@ type frostfsidClient struct {
|
||||||
func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) {
|
func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't initialize context: %w", err)
|
return nil, fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := helper.GetContractByID(r, 1)
|
cs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't get NNS contract info: %w", err)
|
return nil, fmt.Errorf("can't get NNS contract info: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -504,35 +507,3 @@ func (f *frostfsidClient) sendWaitRes() (*state.AppExecResult, error) {
|
||||||
f.wCtx.Command.Println("Waiting for transactions to persist...")
|
f.wCtx.Command.Println("Waiting for transactions to persist...")
|
||||||
return f.roCli.Wait(f.wCtx.SentTxs[0].Hash, f.wCtx.SentTxs[0].Vub, nil)
|
return f.roCli.Wait(f.wCtx.SentTxs[0].Hash, f.wCtx.SentTxs[0].Vub, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readIterator(inv *invoker.Invoker, iter *result.Iterator, batchSize int, sessionID uuid.UUID) ([]stackitem.Item, error) {
|
|
||||||
var shouldStop bool
|
|
||||||
res := make([]stackitem.Item, 0)
|
|
||||||
for !shouldStop {
|
|
||||||
items, err := inv.TraverseIterator(sessionID, iter, batchSize)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, items...)
|
|
||||||
shouldStop = len(items) < batchSize
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initInvoker(cmd *cobra.Command) (*invoker.Invoker, *state.Contract, util.Uint160) {
|
|
||||||
c, err := helper.GetN3Client(viper.GetViper())
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't create N3 client: %w", err)
|
|
||||||
|
|
||||||
inv := invoker.New(c, nil)
|
|
||||||
r := management.NewReader(inv)
|
|
||||||
|
|
||||||
cs, err := r.GetContractByID(1)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
|
||||||
|
|
||||||
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.FrostfsIDContract))
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't get netmap contract hash: %w", err)
|
|
||||||
|
|
||||||
return inv, cs, nmHash
|
|
||||||
}
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, er
|
||||||
return nil, fmt.Errorf("can't fetch password: %w", err)
|
return nil, fmt.Errorf("can't fetch password: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i := i
|
||||||
errG.Go(func() error {
|
errG.Go(func() error {
|
||||||
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, 0o644)
|
||||||
|
@ -106,6 +107,7 @@ func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, er
|
||||||
// Create consensus account with 2*N/3+1 multi-signature.
|
// Create consensus account with 2*N/3+1 multi-signature.
|
||||||
bftCount := smartcontract.GetDefaultHonestNodeCount(size)
|
bftCount := smartcontract.GetDefaultHonestNodeCount(size)
|
||||||
for i := range wallets {
|
for i := range wallets {
|
||||||
|
i := i
|
||||||
ps := pubs.Copy()
|
ps := pubs.Copy()
|
||||||
errG.Go(func() error {
|
errG.Go(func() error {
|
||||||
if err := addMultisigAccount(wallets[i], majCount, constants.CommitteeAccountName, passwords[i], ps); err != nil {
|
if err := addMultisigAccount(wallets[i], majCount, constants.CommitteeAccountName, passwords[i], ps); err != nil {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
func getFrostfsIDAdminFromContract(roInvoker *invoker.Invoker) (util.Uint160, bool, error) {
|
func getFrostfsIDAdminFromContract(roInvoker *invoker.Invoker) (util.Uint160, bool, error) {
|
||||||
r := management.NewReader(roInvoker)
|
r := management.NewReader(roInvoker)
|
||||||
cs, err := GetContractByID(r, 1)
|
cs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.Uint160{}, false, fmt.Errorf("get nns contract: %w", err)
|
return util.Uint160{}, false, fmt.Errorf("get nns contract: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ func GetContractDeployData(c *InitializeContext, ctrName string, keysParam []any
|
||||||
// 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)
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
nnsCs, err := GetContractByID(r, 1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get nns contract: %w", err)
|
return nil, fmt.Errorf("get nns contract: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ func (l *LocalClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, e
|
||||||
paramz = []manifest.Parameter{{Type: smartcontract.SignatureType}}
|
paramz = []manifest.Parameter{{Type: smartcontract.SignatureType}}
|
||||||
} else if nSigs, _, ok := vm.ParseMultiSigContract(w.VerificationScript); ok {
|
} else if nSigs, _, ok := vm.ParseMultiSigContract(w.VerificationScript); ok {
|
||||||
paramz = make([]manifest.Parameter, nSigs)
|
paramz = make([]manifest.Parameter, nSigs)
|
||||||
for j := range nSigs {
|
for j := 0; j < nSigs; j++ {
|
||||||
paramz[j] = manifest.Parameter{Type: smartcontract.SignatureType}
|
paramz[j] = manifest.Parameter{Type: smartcontract.SignatureType}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
@ -61,23 +60,9 @@ func GetN3Client(v *viper.Viper) (Client, error) {
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
return nil, errors.New("missing endpoint")
|
return nil, errors.New("missing endpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg *tls.Config
|
|
||||||
if rootCAs := v.GetStringSlice("tls.trusted_ca_list"); len(rootCAs) != 0 {
|
|
||||||
certFile := v.GetString("tls.certificate")
|
|
||||||
keyFile := v.GetString("tls.key")
|
|
||||||
|
|
||||||
tlsConfig, err := rpcclient.TLSClientConfig(rootCAs, certFile, keyFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg = tlsConfig
|
|
||||||
}
|
|
||||||
c, err := rpcclient.New(ctx, endpoint, rpcclient.Options{
|
c, err := rpcclient.New(ctx, endpoint, rpcclient.Options{
|
||||||
MaxConnsPerHost: maxConnsPerHost,
|
MaxConnsPerHost: maxConnsPerHost,
|
||||||
RequestTimeout: requestTimeout,
|
RequestTimeout: requestTimeout,
|
||||||
TLSClientConfig: cfg,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -72,17 +72,13 @@ func InvalidConfigValueErr(key string) error {
|
||||||
return fmt.Errorf("invalid %s config value from netmap contract", key)
|
return fmt.Errorf("invalid %s config value from netmap contract", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160, countEpoch int64) error {
|
func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160) error {
|
||||||
if countEpoch <= 0 {
|
|
||||||
return errors.New("number of epochs cannot be less than 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch"))
|
curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("can't fetch current epoch from the netmap contract")
|
return errors.New("can't fetch current epoch from the netmap contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
newEpoch := curr + countEpoch
|
newEpoch := curr + 1
|
||||||
wCtx.Command.Printf("Current epoch: %d, increase to %d.\n", curr, newEpoch)
|
wCtx.Command.Printf("Current epoch: %d, increase to %d.\n", curr, newEpoch)
|
||||||
|
|
||||||
// In NeoFS this is done via Notary contract. Here, however, we can form the
|
// In NeoFS this is done via Notary contract. Here, however, we can form the
|
||||||
|
@ -93,7 +89,7 @@ func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.
|
||||||
|
|
||||||
func GetNetConfigFromNetmapContract(roInvoker *invoker.Invoker) ([]stackitem.Item, error) {
|
func GetNetConfigFromNetmapContract(roInvoker *invoker.Invoker) ([]stackitem.Item, error) {
|
||||||
r := management.NewReader(roInvoker)
|
r := management.NewReader(roInvoker)
|
||||||
cs, err := GetContractByID(r, 1)
|
cs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("get nns contract: %w", err)
|
return nil, fmt.Errorf("get nns contract: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,10 @@ import (
|
||||||
"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/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
"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/encoding/fixedn"
|
||||||
"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/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -42,48 +40,45 @@ func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, er
|
||||||
return nil, fmt.Errorf("can't read alphabet wallets dir: %w", err)
|
return nil, fmt.Errorf("can't read alphabet wallets dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var wallets []*wallet.Wallet
|
var size int
|
||||||
var letter string
|
loop:
|
||||||
for i := range constants.MaxAlphabetNodes {
|
for i := 0; i < len(walletFiles); i++ {
|
||||||
letter = innerring.GlagoliticLetter(i).String()
|
name := innerring.GlagoliticLetter(i).String() + ".json"
|
||||||
p := filepath.Join(walletDir, letter+".json")
|
for j := range walletFiles {
|
||||||
var w *wallet.Wallet
|
if walletFiles[j].Name() == name {
|
||||||
w, err = wallet.NewWalletFromFile(p)
|
size++
|
||||||
if err != nil {
|
continue loop
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("can't open wallet: %w", err)
|
|
||||||
}
|
}
|
||||||
break
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
return nil, errors.New("alphabet wallets dir is empty (run `generate-alphabet` command first)")
|
||||||
|
}
|
||||||
|
|
||||||
|
wallets := make([]*wallet.Wallet, size)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
letter := innerring.GlagoliticLetter(i).String()
|
||||||
|
p := filepath.Join(walletDir, letter+".json")
|
||||||
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't open wallet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var password string
|
password, err := config.GetPassword(v, letter)
|
||||||
password, err = config.GetPassword(v, letter)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("can't fetch password: %w", err)
|
return nil, fmt.Errorf("can't fetch password: %w", err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range w.Accounts {
|
for i := range w.Accounts {
|
||||||
if err = w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
if err := w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
||||||
err = fmt.Errorf("can't unlock wallet: %w", err)
|
return nil, fmt.Errorf("can't unlock wallet: %w", err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wallets = append(wallets, w)
|
wallets[i] = w
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't read wallet for letter '%s': %w", letter, err)
|
|
||||||
}
|
|
||||||
if len(wallets) == 0 {
|
|
||||||
err = errors.New("there are no alphabet wallets in dir (run `generate-alphabet` command first)")
|
|
||||||
if len(walletFiles) > 0 {
|
|
||||||
err = fmt.Errorf("use glagolitic names for wallets(run `print-alphabet`): %w", err)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallets, nil
|
return wallets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,11 +122,11 @@ func readContractsFromArchive(file io.Reader, names []string) (map[string]*Contr
|
||||||
}
|
}
|
||||||
|
|
||||||
r := tar.NewReader(gr)
|
r := tar.NewReader(gr)
|
||||||
var h *tar.Header
|
for h, err := r.Next(); ; h, err = r.Next() {
|
||||||
for h, err = r.Next(); err == nil && h != nil; h, err = r.Next() {
|
if err != nil {
|
||||||
if h.Typeflag != tar.TypeReg {
|
break
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, _ := filepath.Split(h.Name)
|
dir, _ := filepath.Split(h.Name)
|
||||||
ctrName := filepath.Base(dir)
|
ctrName := filepath.Base(dir)
|
||||||
|
|
||||||
|
@ -154,9 +149,6 @@ func readContractsFromArchive(file io.Reader, names []string) (map[string]*Contr
|
||||||
}
|
}
|
||||||
m[ctrName] = cs
|
m[ctrName] = cs
|
||||||
}
|
}
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return nil, fmt.Errorf("can't read contracts from archive: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for ctrName, cs := range m {
|
for ctrName, cs := range m {
|
||||||
if cs.RawNEF == nil {
|
if cs.RawNEF == nil {
|
||||||
|
@ -183,18 +175,3 @@ func ParseGASAmount(s string) (fixedn.Fixed8, error) {
|
||||||
}
|
}
|
||||||
return gasAmount, nil
|
return gasAmount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContractByID retrieves a contract by its ID using the standard GetContractByID method.
|
|
||||||
// However, if the returned state.Contract is nil, it returns an error indicating that the contract was not found.
|
|
||||||
// See https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/1210
|
|
||||||
func GetContractByID(r *management.ContractReader, id int32) (*state.Contract, error) {
|
|
||||||
cs, err := r.GetContractByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cs == nil {
|
|
||||||
return nil, errors.New("contract not found")
|
|
||||||
}
|
|
||||||
return cs, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
|
|
||||||
func setNNS(c *helper.InitializeContext) error {
|
func setNNS(c *helper.InitializeContext) error {
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
nnsCs, err := helper.GetContractByID(r, 1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package initialize
|
package initialize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
"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/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -31,14 +29,10 @@ func setNotaryAndAlphabetNodes(c *helper.InitializeContext) error {
|
||||||
callflag.States|callflag.AllowNotify, int64(noderoles.NeoFSAlphabet), pubs)
|
callflag.States|callflag.AllowNotify, int64(noderoles.NeoFSAlphabet), pubs)
|
||||||
|
|
||||||
if err := c.SendCommitteeTx(w.Bytes(), false); err != nil {
|
if err := c.SendCommitteeTx(w.Bytes(), false); err != nil {
|
||||||
return fmt.Errorf("send committee transaction: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.AwaitTx()
|
return c.AwaitTx()
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("await committee transaction: %w", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRolesFinished(c *helper.InitializeContext) (bool, error) {
|
func setRolesFinished(c *helper.InitializeContext) (bool, error) {
|
||||||
|
|
|
@ -113,7 +113,7 @@ func generateTestData(dir string, size int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubs []string
|
var pubs []string
|
||||||
for i := range size {
|
for i := 0; i < size; i++ {
|
||||||
p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json")
|
p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json")
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -148,7 +148,7 @@ func generateTestData(dir string, size int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTestCredentials(v *viper.Viper, size int) {
|
func setTestCredentials(v *viper.Viper, size int) {
|
||||||
for i := range size {
|
for i := 0; i < size; i++ {
|
||||||
v.Set("credentials."+innerring.GlagoliticLetter(i).String(), strconv.FormatUint(uint64(i), 10))
|
v.Set("credentials."+innerring.GlagoliticLetter(i).String(), strconv.FormatUint(uint64(i), 10))
|
||||||
}
|
}
|
||||||
v.Set("credentials.contract", constants.TestContractPassword)
|
v.Set("credentials.contract", constants.TestContractPassword)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package initialize
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
|
@ -29,10 +28,6 @@ const (
|
||||||
initialProxyGASAmount = 50_000 * native.GASFactor
|
initialProxyGASAmount = 50_000 * native.GASFactor
|
||||||
)
|
)
|
||||||
|
|
||||||
func initialCommitteeGASAmount(c *helper.InitializeContext) int64 {
|
|
||||||
return (gasInitialTotalSupply - initialAlphabetGASAmount*int64(len(c.Wallets))) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
func transferFunds(c *helper.InitializeContext) error {
|
func transferFunds(c *helper.InitializeContext) error {
|
||||||
ok, err := transferFundsFinished(c)
|
ok, err := transferFundsFinished(c)
|
||||||
if ok || err != nil {
|
if ok || err != nil {
|
||||||
|
@ -59,7 +54,7 @@ func transferFunds(c *helper.InitializeContext) error {
|
||||||
transferTarget{
|
transferTarget{
|
||||||
Token: gas.Hash,
|
Token: gas.Hash,
|
||||||
Address: c.CommitteeAcc.Contract.ScriptHash(),
|
Address: c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
Amount: initialCommitteeGASAmount(c),
|
Amount: (gasInitialTotalSupply - initialAlphabetGASAmount*int64(len(c.Wallets))) / 2,
|
||||||
},
|
},
|
||||||
transferTarget{
|
transferTarget{
|
||||||
Token: neo.Hash,
|
Token: neo.Hash,
|
||||||
|
@ -80,19 +75,12 @@ func transferFunds(c *helper.InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
// transferFundsFinished checks balances of accounts we transfer GAS to.
|
|
||||||
// The stage is considered finished if the balance is greater than the half of what we need to transfer.
|
|
||||||
func transferFundsFinished(c *helper.InitializeContext) (bool, error) {
|
func transferFundsFinished(c *helper.InitializeContext) (bool, error) {
|
||||||
acc := c.Accounts[0]
|
acc := c.Accounts[0]
|
||||||
|
|
||||||
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
||||||
res, err := r.BalanceOf(acc.Contract.ScriptHash())
|
res, err := r.BalanceOf(acc.Contract.ScriptHash())
|
||||||
if err != nil || res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) != 1 {
|
return res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) == 1, err
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err = r.BalanceOf(c.CommitteeAcc.ScriptHash())
|
|
||||||
return res != nil && res.Cmp(big.NewInt(initialCommitteeGASAmount(c)/2)) == 1, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func transferGASToProxy(c *helper.InitializeContext) error {
|
func transferGASToProxy(c *helper.InitializeContext) error {
|
||||||
|
@ -152,17 +140,5 @@ func createNEP17MultiTransferTx(c helper.Client, acc *wallet.Account, recipients
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create actor: %w", err)
|
return nil, fmt.Errorf("can't create actor: %w", err)
|
||||||
}
|
}
|
||||||
tx, err := act.MakeRun(w.Bytes())
|
return act.MakeRun(w.Bytes())
|
||||||
if err != nil {
|
|
||||||
sum := make(map[util.Uint160]int64)
|
|
||||||
for _, recipient := range recipients {
|
|
||||||
sum[recipient.Token] += recipient.Amount
|
|
||||||
}
|
|
||||||
detail := make([]string, 0, len(sum))
|
|
||||||
for _, value := range sum {
|
|
||||||
detail = append(detail, fmt.Sprintf("amount=%v", value))
|
|
||||||
}
|
|
||||||
err = fmt.Errorf("transfer failed: from=%s(%s) %s: %w", acc.Label, acc.Address, strings.Join(detail, " "), err)
|
|
||||||
}
|
|
||||||
return tx, err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,14 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const deltaFlag = "delta"
|
|
||||||
|
|
||||||
func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't initialize context: %w", err)
|
return fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
@ -32,8 +30,7 @@ func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
delta, _ := cmd.Flags().GetInt64(deltaFlag)
|
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash); err != nil {
|
||||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash, delta); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ func listNetmapCandidatesNodes(cmd *cobra.Command, _ []string) {
|
||||||
inv := invoker.New(c, nil)
|
inv := invoker.New(c, nil)
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(inv)
|
||||||
|
|
||||||
cs, err := helper.GetContractByID(r, 1)
|
cs, err := r.GetContractByID(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 := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.NetmapContract))
|
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.NetmapContract))
|
||||||
|
|
|
@ -35,7 +35,6 @@ func initForceNewEpochCmd() {
|
||||||
ForceNewEpoch.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
ForceNewEpoch.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
ForceNewEpoch.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
ForceNewEpoch.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
ForceNewEpoch.Flags().String(commonflags.LocalDumpFlag, "", "Path to the blocks dump file")
|
ForceNewEpoch.Flags().String(commonflags.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
ForceNewEpoch.Flags().Int64(deltaFlag, 1, "Number of epochs to increase the current epoch")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -19,7 +19,7 @@ func getRPCClient(cmd *cobra.Command) (*client.Contract, *helper.LocalActor, uti
|
||||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||||
|
|
||||||
r := management.NewReader(ac.Invoker)
|
r := management.NewReader(ac.Invoker)
|
||||||
nnsCs, err := helper.GetContractByID(r, 1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
return client.New(ac, nnsCs.Hash), ac, nnsCs.Hash
|
return client.New(ac, nnsCs.Hash), ac, nnsCs.Hash
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,19 +47,6 @@ func initDelRecordsCmd() {
|
||||||
_ = cobra.MarkFlagRequired(delRecordsCmd.Flags(), nnsRecordTypeFlag)
|
_ = cobra.MarkFlagRequired(delRecordsCmd.Flags(), nnsRecordTypeFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDelRecordCmd() {
|
|
||||||
Cmd.AddCommand(delRecordCmd)
|
|
||||||
delRecordCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
|
||||||
delRecordCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
delRecordCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
|
||||||
delRecordCmd.Flags().String(nnsRecordTypeFlag, "", nnsRecordTypeFlagDesc)
|
|
||||||
delRecordCmd.Flags().String(nnsRecordDataFlag, "", nnsRecordDataFlagDesc)
|
|
||||||
|
|
||||||
_ = cobra.MarkFlagRequired(delRecordCmd.Flags(), nnsNameFlag)
|
|
||||||
_ = cobra.MarkFlagRequired(delRecordCmd.Flags(), nnsRecordTypeFlag)
|
|
||||||
_ = cobra.MarkFlagRequired(delRecordCmd.Flags(), nnsRecordDataFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addRecord(cmd *cobra.Command, _ []string) {
|
func addRecord(cmd *cobra.Command, _ []string) {
|
||||||
c, actor, _ := getRPCClient(cmd)
|
c, actor, _ := getRPCClient(cmd)
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||||
|
@ -128,22 +115,6 @@ func delRecords(cmd *cobra.Command, _ []string) {
|
||||||
cmd.Println("Records removed successfully")
|
cmd.Println("Records removed successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
func delRecord(cmd *cobra.Command, _ []string) {
|
|
||||||
c, actor, _ := getRPCClient(cmd)
|
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
|
||||||
data, _ := cmd.Flags().GetString(nnsRecordDataFlag)
|
|
||||||
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
|
||||||
typ, err := getRecordType(recordType)
|
|
||||||
commonCmd.ExitOnErr(cmd, "unable to parse record type: %w", err)
|
|
||||||
h, vub, err := c.DeleteRecord(name, typ, data)
|
|
||||||
commonCmd.ExitOnErr(cmd, "unable to delete record: %w", err)
|
|
||||||
|
|
||||||
cmd.Println("Waiting for transaction to persist...")
|
|
||||||
_, err = actor.Wait(h, vub, err)
|
|
||||||
commonCmd.ExitOnErr(cmd, "delete records error: %w", err)
|
|
||||||
cmd.Println("Record removed successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRecordType(recordType string) (*big.Int, error) {
|
func getRecordType(recordType string) (*big.Int, error) {
|
||||||
switch strings.ToUpper(recordType) {
|
switch strings.ToUpper(recordType) {
|
||||||
case "A":
|
case "A":
|
||||||
|
|
|
@ -42,23 +42,3 @@ func registerDomain(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "register domain error: %w", err)
|
commonCmd.ExitOnErr(cmd, "register domain error: %w", err)
|
||||||
cmd.Println("Domain registered successfully")
|
cmd.Println("Domain registered successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDeleteCmd() {
|
|
||||||
Cmd.AddCommand(deleteCmd)
|
|
||||||
deleteCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
|
||||||
deleteCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
deleteCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
|
||||||
|
|
||||||
_ = cobra.MarkFlagRequired(deleteCmd.Flags(), nnsNameFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteDomain(cmd *cobra.Command, _ []string) {
|
|
||||||
c, actor, _ := getRPCClient(cmd)
|
|
||||||
|
|
||||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
|
||||||
h, vub, err := c.DeleteDomain(name)
|
|
||||||
|
|
||||||
_, err = actor.Wait(h, vub, err)
|
|
||||||
commonCmd.ExitOnErr(cmd, "delete domain error: %w", err)
|
|
||||||
cmd.Println("Domain deleted successfully")
|
|
||||||
}
|
|
|
@ -42,15 +42,6 @@ var (
|
||||||
},
|
},
|
||||||
Run: registerDomain,
|
Run: registerDomain,
|
||||||
}
|
}
|
||||||
deleteCmd = &cobra.Command{
|
|
||||||
Use: "delete",
|
|
||||||
Short: "Delete a domain by name",
|
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
},
|
|
||||||
Run: deleteDomain,
|
|
||||||
}
|
|
||||||
renewCmd = &cobra.Command{
|
renewCmd = &cobra.Command{
|
||||||
Use: "renew",
|
Use: "renew",
|
||||||
Short: "Increases domain expiration date",
|
Short: "Increases domain expiration date",
|
||||||
|
@ -95,25 +86,14 @@ var (
|
||||||
},
|
},
|
||||||
Run: delRecords,
|
Run: delRecords,
|
||||||
}
|
}
|
||||||
delRecordCmd = &cobra.Command{
|
|
||||||
Use: "delete-record",
|
|
||||||
Short: "Removes domain record with the specified type and data",
|
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
},
|
|
||||||
Run: delRecord,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
initTokensCmd()
|
initTokensCmd()
|
||||||
initRegisterCmd()
|
initRegisterCmd()
|
||||||
initDeleteCmd()
|
|
||||||
initRenewCmd()
|
initRenewCmd()
|
||||||
initUpdateCmd()
|
initUpdateCmd()
|
||||||
initAddRecordCmd()
|
initAddRecordCmd()
|
||||||
initGetRecordsCmd()
|
initGetRecordsCmd()
|
||||||
initDelRecordsCmd()
|
initDelRecordsCmd()
|
||||||
initDelRecordCmd()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,15 @@
|
||||||
package nns
|
package nns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
|
||||||
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
verboseDesc = "Include additional information about CNAME record."
|
|
||||||
)
|
|
||||||
|
|
||||||
func initTokensCmd() {
|
func initTokensCmd() {
|
||||||
Cmd.AddCommand(tokensCmd)
|
Cmd.AddCommand(tokensCmd)
|
||||||
tokensCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
tokensCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
tokensCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
tokensCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||||
tokensCmd.Flags().BoolP(commonflags.Verbose, commonflags.VerboseShorthand, false, verboseDesc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func listTokens(cmd *cobra.Command, _ []string) {
|
func listTokens(cmd *cobra.Command, _ []string) {
|
||||||
|
@ -28,39 +18,7 @@ func listTokens(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "unable to get tokens: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to get tokens: %w", err)
|
||||||
for toks, err := it.Next(10); err == nil && len(toks) > 0; toks, err = it.Next(10) {
|
for toks, err := it.Next(10); err == nil && len(toks) > 0; toks, err = it.Next(10) {
|
||||||
for _, token := range toks {
|
for _, token := range toks {
|
||||||
output := string(token)
|
cmd.Println(string(token))
|
||||||
if verbose, _ := cmd.Flags().GetBool(commonflags.Verbose); verbose {
|
|
||||||
cname, err := getCnameRecord(c, token)
|
|
||||||
commonCmd.ExitOnErr(cmd, "", err)
|
|
||||||
if cname != "" {
|
|
||||||
output += " (CNAME: " + cname + ")"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd.Println(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCnameRecord(c *client.Contract, token []byte) (string, error) {
|
|
||||||
items, err := c.GetRecords(string(token), big.NewInt(int64(nns.CNAME)))
|
|
||||||
|
|
||||||
// GetRecords returns the error "not an array" if the domain does not contain records.
|
|
||||||
if err != nil && strings.Contains(err.Error(), "not an array") {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(items) == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
record, err := items[0].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(record), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func RemoveNodesCmd(cmd *cobra.Command, args []string) error {
|
||||||
defer wCtx.Close()
|
defer wCtx.Close()
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func RemoveNodesCmd(cmd *cobra.Command, args []string) error {
|
||||||
int64(netmapcontract.NodeStateOffline), nodeKeys[i].Bytes())
|
int64(netmapcontract.NodeStateOffline), nodeKeys[i].Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash, 1); err != nil {
|
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ var errInvalidParameterFormat = errors.New("invalid parameter format, must be Pa
|
||||||
func SetPolicyCmd(cmd *cobra.Command, args []string) error {
|
func SetPolicyCmd(cmd *cobra.Command, args []string) error {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't initialize context: %w", err)
|
return fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
|
|
|
@ -39,11 +39,11 @@ func removeProxyAccount(cmd *cobra.Command, _ []string) {
|
||||||
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
||||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't initialize context: %w", err)
|
return fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
cs, err := helper.GetContractByID(r, 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,13 +30,11 @@ var (
|
||||||
func initProxyAddAccount() {
|
func initProxyAddAccount() {
|
||||||
AddAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
AddAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
AddAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
AddAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||||
AddAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initProxyRemoveAccount() {
|
func initProxyRemoveAccount() {
|
||||||
RemoveAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
RemoveAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
RemoveAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
RemoveAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||||
RemoveAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"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/cmd/frostfs-adm/internal/modules/metabase"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/storagecfg"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/storagecfg"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
||||||
|
@ -42,7 +41,6 @@ func init() {
|
||||||
rootCmd.AddCommand(config.RootCmd)
|
rootCmd.AddCommand(config.RootCmd)
|
||||||
rootCmd.AddCommand(morph.RootCmd)
|
rootCmd.AddCommand(morph.RootCmd)
|
||||||
rootCmd.AddCommand(storagecfg.RootCmd)
|
rootCmd.AddCommand(storagecfg.RootCmd)
|
||||||
rootCmd.AddCommand(metabase.RootCmd)
|
|
||||||
|
|
||||||
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
||||||
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
||||||
|
|
|
@ -2,13 +2,10 @@ package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"slices"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -17,6 +14,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"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"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
@ -191,6 +189,54 @@ func DeleteContainer(ctx context.Context, prm DeleteContainerPrm) (res DeleteCon
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EACLPrm groups parameters of EACL operation.
|
||||||
|
type EACLPrm struct {
|
||||||
|
Client *client.Client
|
||||||
|
ClientParams client.PrmContainerEACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// EACLRes groups the resulting values of EACL operation.
|
||||||
|
type EACLRes struct {
|
||||||
|
cliRes *client.ResContainerEACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// EACL returns requested eACL table.
|
||||||
|
func (x EACLRes) EACL() eacl.Table {
|
||||||
|
return x.cliRes.Table()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EACL reads eACL table from FrostFS by container ID.
|
||||||
|
//
|
||||||
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
|
func EACL(ctx context.Context, prm EACLPrm) (res EACLRes, err error) {
|
||||||
|
res.cliRes, err = prm.Client.ContainerEACL(ctx, prm.ClientParams)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEACLPrm groups parameters of SetEACL operation.
|
||||||
|
type SetEACLPrm struct {
|
||||||
|
Client *client.Client
|
||||||
|
ClientParams client.PrmContainerSetEACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEACLRes groups the resulting values of SetEACL operation.
|
||||||
|
type SetEACLRes struct{}
|
||||||
|
|
||||||
|
// SetEACL requests to save an eACL table in FrostFS.
|
||||||
|
//
|
||||||
|
// Operation is asynchronous and no guaranteed even in the absence of errors.
|
||||||
|
// The required time is also not predictable.
|
||||||
|
//
|
||||||
|
// Success can be verified by reading by container identifier.
|
||||||
|
//
|
||||||
|
// Returns any error which prevented the operation from completing correctly in error return.
|
||||||
|
func SetEACL(ctx context.Context, prm SetEACLPrm) (res SetEACLRes, err error) {
|
||||||
|
_, err = prm.Client.ContainerSetEACL(ctx, prm.ClientParams)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// NetworkInfoPrm groups parameters of NetworkInfo operation.
|
// NetworkInfoPrm groups parameters of NetworkInfo operation.
|
||||||
type NetworkInfoPrm struct {
|
type NetworkInfoPrm struct {
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
|
@ -565,6 +611,13 @@ type HeadObjectPrm struct {
|
||||||
commonObjectPrm
|
commonObjectPrm
|
||||||
objectAddressPrm
|
objectAddressPrm
|
||||||
rawPrm
|
rawPrm
|
||||||
|
|
||||||
|
mainOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMainOnlyFlag sets flag to get only main fields of an object header in terms of FrostFS API.
|
||||||
|
func (x *HeadObjectPrm) SetMainOnlyFlag(v bool) {
|
||||||
|
x.mainOnly = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeadObjectRes groups the resulting values of HeadObject operation.
|
// HeadObjectRes groups the resulting values of HeadObject operation.
|
||||||
|
@ -659,7 +712,7 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n, ok = rdr.Read(buf)
|
n, ok = rdr.Read(buf)
|
||||||
for i := range n {
|
for i := 0; i < n; i++ {
|
||||||
list = append(list, buf[i])
|
list = append(list, buf[i])
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -839,65 +892,3 @@ func SyncContainerSettings(ctx context.Context, prm SyncContainerPrm) (*SyncCont
|
||||||
|
|
||||||
return new(SyncContainerRes), nil
|
return new(SyncContainerRes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PatchObjectPrm groups parameters of PatchObject operation.
|
|
||||||
type PatchObjectPrm struct {
|
|
||||||
commonObjectPrm
|
|
||||||
objectAddressPrm
|
|
||||||
|
|
||||||
NewAttributes []objectSDK.Attribute
|
|
||||||
|
|
||||||
ReplaceAttribute bool
|
|
||||||
|
|
||||||
PayloadPatches []PayloadPatch
|
|
||||||
}
|
|
||||||
|
|
||||||
type PayloadPatch struct {
|
|
||||||
Range objectSDK.Range
|
|
||||||
|
|
||||||
PayloadPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PatchRes struct {
|
|
||||||
OID oid.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func Patch(ctx context.Context, prm PatchObjectPrm) (*PatchRes, error) {
|
|
||||||
patchPrm := client.PrmObjectPatch{
|
|
||||||
XHeaders: prm.xHeaders,
|
|
||||||
BearerToken: prm.bearerToken,
|
|
||||||
Session: prm.sessionToken,
|
|
||||||
Address: prm.objAddr,
|
|
||||||
}
|
|
||||||
|
|
||||||
slices.SortFunc(prm.PayloadPatches, func(a, b PayloadPatch) int {
|
|
||||||
return cmp.Compare(a.Range.GetOffset(), b.Range.GetOffset())
|
|
||||||
})
|
|
||||||
|
|
||||||
patcher, err := prm.cli.ObjectPatchInit(ctx, patchPrm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("init payload reading: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if patcher.PatchAttributes(ctx, prm.NewAttributes, prm.ReplaceAttribute) {
|
|
||||||
for _, pp := range prm.PayloadPatches {
|
|
||||||
payloadFile, err := os.OpenFile(pp.PayloadPath, os.O_RDONLY, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
applied := patcher.PatchPayload(ctx, &pp.Range, payloadFile)
|
|
||||||
_ = payloadFile.Close()
|
|
||||||
if !applied {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := patcher.Close(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &PatchRes{
|
|
||||||
OID: res.ObjectID(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -50,13 +50,6 @@ const (
|
||||||
|
|
||||||
TracingFlag = "trace"
|
TracingFlag = "trace"
|
||||||
TracingFlagUsage = "Generate trace ID and print it."
|
TracingFlagUsage = "Generate trace ID and print it."
|
||||||
|
|
||||||
AwaitFlag = "await"
|
|
||||||
AwaitFlagUsage = "Wait for the operation to complete"
|
|
||||||
|
|
||||||
QuietFlag = "quiet"
|
|
||||||
QuietFlagShorthand = "q"
|
|
||||||
QuietFlagUsage = "Print nothing and exit with non-zero code on failure"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init adds common flags to the command:
|
// Init adds common flags to the command:
|
||||||
|
|
|
@ -24,8 +24,6 @@ var testCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_getOrGenerate(t *testing.T) {
|
func Test_getOrGenerate(t *testing.T) {
|
||||||
t.Cleanup(viper.Reset)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
|
|
||||||
wallPath := filepath.Join(dir, "wallet.json")
|
wallPath := filepath.Join(dir, "wallet.json")
|
||||||
|
|
|
@ -15,12 +15,10 @@ import (
|
||||||
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
eaclFlag = "eacl"
|
eaclFlag = "eacl"
|
||||||
apeFlag = "ape"
|
|
||||||
issuedAtFlag = "issued-at"
|
issuedAtFlag = "issued-at"
|
||||||
notValidBeforeFlag = "not-valid-before"
|
notValidBeforeFlag = "not-valid-before"
|
||||||
ownerFlag = "owner"
|
ownerFlag = "owner"
|
||||||
|
@ -39,17 +37,10 @@ In this case --` + commonflags.RPC + ` flag should be specified and the epoch in
|
||||||
is set to current epoch + n.
|
is set to current epoch + n.
|
||||||
`,
|
`,
|
||||||
Run: createToken,
|
Run: createToken,
|
||||||
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
|
||||||
ff := cmd.Flags()
|
|
||||||
|
|
||||||
_ = viper.BindPFlag(commonflags.WalletPath, ff.Lookup(commonflags.WalletPath))
|
|
||||||
_ = viper.BindPFlag(commonflags.Account, ff.Lookup(commonflags.Account))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createCmd.Flags().StringP(eaclFlag, "e", "", "Path to the extended ACL table (mutually exclusive with --impersonate and --ape flag)")
|
createCmd.Flags().StringP(eaclFlag, "e", "", "Path to the extended ACL table (mutually exclusive with --impersonate flag)")
|
||||||
createCmd.Flags().StringP(apeFlag, "a", "", "Path to the JSON-encoded APE override (mutually exclusive with --impersonate and --eacl flag)")
|
|
||||||
createCmd.Flags().StringP(issuedAtFlag, "i", "+0", "Epoch to issue token at")
|
createCmd.Flags().StringP(issuedAtFlag, "i", "+0", "Epoch to issue token at")
|
||||||
createCmd.Flags().StringP(notValidBeforeFlag, "n", "+0", "Not valid before epoch")
|
createCmd.Flags().StringP(notValidBeforeFlag, "n", "+0", "Not valid before epoch")
|
||||||
createCmd.Flags().StringP(commonflags.ExpireAt, "x", "", "The last active epoch for the token")
|
createCmd.Flags().StringP(commonflags.ExpireAt, "x", "", "The last active epoch for the token")
|
||||||
|
@ -58,15 +49,13 @@ func init() {
|
||||||
createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON")
|
createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON")
|
||||||
createCmd.Flags().Bool(impersonateFlag, false, "Mark token as impersonate to consider the token signer as the request owner (mutually exclusive with --eacl flag)")
|
createCmd.Flags().Bool(impersonateFlag, false, "Mark token as impersonate to consider the token signer as the request owner (mutually exclusive with --eacl flag)")
|
||||||
createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
|
||||||
createCmd.Flags().StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
|
|
||||||
createCmd.Flags().StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
|
|
||||||
|
|
||||||
createCmd.MarkFlagsMutuallyExclusive(eaclFlag, apeFlag, impersonateFlag)
|
createCmd.MarkFlagsMutuallyExclusive(eaclFlag, impersonateFlag)
|
||||||
|
|
||||||
_ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag)
|
_ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag)
|
||||||
_ = cobra.MarkFlagFilename(createCmd.Flags(), apeFlag)
|
|
||||||
|
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt)
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt)
|
||||||
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), ownerFlag)
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag)
|
_ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,16 +96,16 @@ func createToken(cmd *cobra.Command, _ []string) {
|
||||||
fmt.Errorf("expiration epoch is less than not-valid-before epoch: %d < %d", exp, nvb))
|
fmt.Errorf("expiration epoch is less than not-valid-before epoch: %d < %d", exp, nvb))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ownerStr, _ := cmd.Flags().GetString(ownerFlag)
|
||||||
|
|
||||||
|
var ownerID user.ID
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't parse recipient: %w", ownerID.DecodeString(ownerStr))
|
||||||
|
|
||||||
var b bearer.Token
|
var b bearer.Token
|
||||||
b.SetExp(exp)
|
b.SetExp(exp)
|
||||||
b.SetNbf(nvb)
|
b.SetNbf(nvb)
|
||||||
b.SetIat(iat)
|
b.SetIat(iat)
|
||||||
|
b.ForUser(ownerID)
|
||||||
if ownerStr, _ := cmd.Flags().GetString(ownerFlag); ownerStr != "" {
|
|
||||||
var ownerID user.ID
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse recipient: %w", ownerID.DecodeString(ownerStr))
|
|
||||||
b.ForUser(ownerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
impersonate, _ := cmd.Flags().GetBool(impersonateFlag)
|
impersonate, _ := cmd.Flags().GetBool(impersonateFlag)
|
||||||
b.SetImpersonate(impersonate)
|
b.SetImpersonate(impersonate)
|
||||||
|
@ -130,14 +119,6 @@ func createToken(cmd *cobra.Command, _ []string) {
|
||||||
b.SetEACLTable(*table)
|
b.SetEACLTable(*table)
|
||||||
}
|
}
|
||||||
|
|
||||||
apePath, _ := cmd.Flags().GetString(apeFlag)
|
|
||||||
if apePath != "" {
|
|
||||||
var apeOverride bearer.APEOverride
|
|
||||||
raw, err := os.ReadFile(apePath)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't read APE rules: %w", err)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse APE rules: %w", json.Unmarshal(raw, &apeOverride))
|
|
||||||
b.SetAPEOverride(apeOverride)
|
|
||||||
}
|
|
||||||
var data []byte
|
var data []byte
|
||||||
|
|
||||||
toJSON, _ := cmd.Flags().GetBool(jsonFlag)
|
toJSON, _ := cmd.Flags().GetBool(jsonFlag)
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
package bearer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
|
||||||
parseutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
|
||||||
apeSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ape"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
|
||||||
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
||||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errChainIDCannotBeEmpty = errors.New("chain id cannot be empty")
|
|
||||||
errRuleIsNotParsed = errors.New("rule is not passed")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
chainIDFlag = "chain-id"
|
|
||||||
chainIDHexFlag = "chain-id-hex"
|
|
||||||
ruleFlag = "rule"
|
|
||||||
pathFlag = "path"
|
|
||||||
outputFlag = "output"
|
|
||||||
)
|
|
||||||
|
|
||||||
var generateAPEOverrideCmd = &cobra.Command{
|
|
||||||
Use: "generate-ape-override",
|
|
||||||
Short: "Generate APE override.",
|
|
||||||
Long: `Generate APE override by target and APE chains. Util command.
|
|
||||||
|
|
||||||
Generated APE override can be dumped to a file in JSON format that is passed to
|
|
||||||
"create" command.
|
|
||||||
`,
|
|
||||||
Run: genereateAPEOverride,
|
|
||||||
}
|
|
||||||
|
|
||||||
func genereateAPEOverride(cmd *cobra.Command, _ []string) {
|
|
||||||
c := parseChain(cmd)
|
|
||||||
|
|
||||||
targetCID, _ := cmd.Flags().GetString(commonflags.CIDFlag)
|
|
||||||
var cid cidSDK.ID
|
|
||||||
commonCmd.ExitOnErr(cmd, "invalid cid format: %w", cid.DecodeString(targetCID))
|
|
||||||
|
|
||||||
override := &bearer.APEOverride{
|
|
||||||
Target: apeSDK.ChainTarget{
|
|
||||||
TargetType: apeSDK.TargetTypeContainer,
|
|
||||||
Name: targetCID,
|
|
||||||
},
|
|
||||||
Chains: []apeSDK.Chain{
|
|
||||||
{
|
|
||||||
Raw: c.Bytes(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
overrideMarshalled, err := override.MarshalJSON()
|
|
||||||
commonCmd.ExitOnErr(cmd, "failed to marshal APE override: %w", err)
|
|
||||||
|
|
||||||
outputPath, _ := cmd.Flags().GetString(outputFlag)
|
|
||||||
if outputPath != "" {
|
|
||||||
err := os.WriteFile(outputPath, []byte(overrideMarshalled), 0o644)
|
|
||||||
commonCmd.ExitOnErr(cmd, "dump error: %w", err)
|
|
||||||
} else {
|
|
||||||
fmt.Print("\n")
|
|
||||||
fmt.Println(string(overrideMarshalled))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ff := generateAPEOverrideCmd.Flags()
|
|
||||||
|
|
||||||
ff.StringP(commonflags.CIDFlag, "", "", "Target container ID.")
|
|
||||||
_ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.CIDFlag)
|
|
||||||
|
|
||||||
ff.StringArray(ruleFlag, []string{}, "Rule statement")
|
|
||||||
ff.String(pathFlag, "", "Path to encoded chain in JSON or binary format")
|
|
||||||
ff.String(chainIDFlag, "", "Assign ID to the parsed chain")
|
|
||||||
ff.Bool(chainIDHexFlag, false, "Flag to parse chain ID as hex")
|
|
||||||
|
|
||||||
ff.String(outputFlag, "", "Output path to dump result JSON-encoded APE override")
|
|
||||||
_ = cobra.MarkFlagFilename(createCmd.Flags(), outputFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseChainID(cmd *cobra.Command) apechain.ID {
|
|
||||||
chainID, _ := cmd.Flags().GetString(chainIDFlag)
|
|
||||||
if chainID == "" {
|
|
||||||
commonCmd.ExitOnErr(cmd, "read chain id error: %w",
|
|
||||||
errChainIDCannotBeEmpty)
|
|
||||||
}
|
|
||||||
return apechain.ID(chainID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseChain(cmd *cobra.Command) *apechain.Chain {
|
|
||||||
chain := new(apechain.Chain)
|
|
||||||
|
|
||||||
if rules, _ := cmd.Flags().GetStringArray(ruleFlag); len(rules) > 0 {
|
|
||||||
commonCmd.ExitOnErr(cmd, "parser error: %w", parseutil.ParseAPEChain(chain, rules))
|
|
||||||
} else if encPath, _ := cmd.Flags().GetString(pathFlag); encPath != "" {
|
|
||||||
commonCmd.ExitOnErr(cmd, "decode binary or json error: %w", parseutil.ParseAPEChainBinaryOrJSON(chain, encPath))
|
|
||||||
} else {
|
|
||||||
commonCmd.ExitOnErr(cmd, "parser error: %w", errRuleIsNotParsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.ID = parseChainID(cmd)
|
|
||||||
|
|
||||||
cmd.Println("Parsed chain:")
|
|
||||||
parseutil.PrintHumanReadableAPEChain(cmd, chain)
|
|
||||||
|
|
||||||
return chain
|
|
||||||
}
|
|
|
@ -11,5 +11,4 @@ var Cmd = &cobra.Command{
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Cmd.AddCommand(createCmd)
|
Cmd.AddCommand(createCmd)
|
||||||
Cmd.AddCommand(generateAPEOverrideCmd)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for range awaitTimeout {
|
for i := 0; i < awaitTimeout; i++ {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
||||||
|
|
|
@ -110,7 +110,7 @@ Only owner of the container has a permission to remove container.`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for range awaitTimeout {
|
for i := 0; i < awaitTimeout; i++ {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
||||||
|
|
68
cmd/frostfs-cli/modules/container/get_eacl.go
Normal file
68
cmd/frostfs-cli/modules/container/get_eacl.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
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/key"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var getExtendedACLCmd = &cobra.Command{
|
||||||
|
Use: "get-eacl",
|
||||||
|
Short: "Get extended ACL table of container",
|
||||||
|
Long: `Get extended ACL table of container`,
|
||||||
|
Run: func(cmd *cobra.Command, _ []string) {
|
||||||
|
id := parseContainerID(cmd)
|
||||||
|
pk := key.GetOrGenerate(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||||
|
|
||||||
|
eaclPrm := internalclient.EACLPrm{
|
||||||
|
Client: cli,
|
||||||
|
ClientParams: client.PrmContainerEACL{
|
||||||
|
ContainerID: &id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := internalclient.EACL(cmd.Context(), eaclPrm)
|
||||||
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
|
eaclTable := res.EACL()
|
||||||
|
|
||||||
|
if containerPathTo == "" {
|
||||||
|
cmd.Println("eACL: ")
|
||||||
|
common.PrettyPrintJSON(cmd, &eaclTable, "eACL")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
|
||||||
|
if containerJSON {
|
||||||
|
data, err = eaclTable.MarshalJSON()
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't encode to JSON: %w", err)
|
||||||
|
} else {
|
||||||
|
data, err = eaclTable.Marshal()
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't encode to binary: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Println("dumping data to file:", containerPathTo)
|
||||||
|
|
||||||
|
err = os.WriteFile(containerPathTo, data, 0o644)
|
||||||
|
commonCmd.ExitOnErr(cmd, "could not write eACL to file: %w", err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func initContainerGetEACLCmd() {
|
||||||
|
commonflags.Init(getExtendedACLCmd)
|
||||||
|
|
||||||
|
flags := getExtendedACLCmd.Flags()
|
||||||
|
|
||||||
|
flags.StringVar(&containerID, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
|
||||||
|
flags.StringVar(&containerPathTo, "to", "", "Path to dump encoded container (default: binary encoded)")
|
||||||
|
flags.BoolVar(&containerJSON, commonflags.JSON, false, "Encode EACL table in json format")
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
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/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"
|
||||||
|
@ -67,6 +70,7 @@ var listContainersCmd = &cobra.Command{
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cnrID := cnrID
|
||||||
prmGet.ClientParams.ContainerID = &cnrID
|
prmGet.ClientParams.ContainerID = &cnrID
|
||||||
res, err := internalclient.GetContainer(cmd.Context(), prmGet)
|
res, err := internalclient.GetContainer(cmd.Context(), prmGet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -81,8 +85,12 @@ var listContainersCmd = &cobra.Command{
|
||||||
cmd.Println(cnrID.String())
|
cmd.Println(cnrID.String())
|
||||||
|
|
||||||
if flagVarListPrintAttr {
|
if flagVarListPrintAttr {
|
||||||
cnr.IterateUserAttributes(func(key, val string) {
|
cnr.IterateAttributes(func(key, val string) {
|
||||||
cmd.Printf(" %s: %s\n", key, val)
|
if !strings.HasPrefix(key, container.SysAttributePrefix) && !strings.HasPrefix(key, container.SysAttributePrefixNeoFS) {
|
||||||
|
// FIXME(@cthulhu-rider): https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/issues/97
|
||||||
|
// Use dedicated method to skip system attributes.
|
||||||
|
cmd.Printf(" %s: %s\n", key, val)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
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/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"
|
||||||
|
@ -64,8 +67,14 @@ var listContainerObjectsCmd = &cobra.Command{
|
||||||
|
|
||||||
resHead, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
resHead, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, attr := range resHead.Header().UserAttributes() {
|
attrs := resHead.Header().Attributes()
|
||||||
cmd.Printf(" %s: %s\n", attr.Key(), attr.Value())
|
for i := range attrs {
|
||||||
|
attrKey := attrs[i].Key()
|
||||||
|
if !strings.HasPrefix(attrKey, v2object.SysAttributePrefix) && !strings.HasPrefix(attrKey, v2object.SysAttributePrefixNeoFS) {
|
||||||
|
// FIXME(@cthulhu-rider): https://git.frostfs.info/TrueCloudLab/frostfs-sdk-go/issues/97
|
||||||
|
// Use dedicated method to skip system attributes.
|
||||||
|
cmd.Printf(" %s: %s\n", attrKey, attrs[i].Value())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cmd.Printf(" failed to read attributes: %v\n", err)
|
cmd.Printf(" failed to read attributes: %v\n", err)
|
||||||
|
|
|
@ -25,6 +25,8 @@ func init() {
|
||||||
deleteContainerCmd,
|
deleteContainerCmd,
|
||||||
listContainerObjectsCmd,
|
listContainerObjectsCmd,
|
||||||
getContainerInfoCmd,
|
getContainerInfoCmd,
|
||||||
|
getExtendedACLCmd,
|
||||||
|
setExtendedACLCmd,
|
||||||
containerNodesCmd,
|
containerNodesCmd,
|
||||||
policyPlaygroundCmd,
|
policyPlaygroundCmd,
|
||||||
}
|
}
|
||||||
|
@ -36,6 +38,8 @@ func init() {
|
||||||
initContainerDeleteCmd()
|
initContainerDeleteCmd()
|
||||||
initContainerListObjectsCmd()
|
initContainerListObjectsCmd()
|
||||||
initContainerInfoCmd()
|
initContainerInfoCmd()
|
||||||
|
initContainerGetEACLCmd()
|
||||||
|
initContainerSetEACLCmd()
|
||||||
initContainerNodesCmd()
|
initContainerNodesCmd()
|
||||||
initContainerPolicyPlaygroundCmd()
|
initContainerPolicyPlaygroundCmd()
|
||||||
|
|
||||||
|
@ -49,6 +53,7 @@ func init() {
|
||||||
}{
|
}{
|
||||||
{createContainerCmd, "PUT"},
|
{createContainerCmd, "PUT"},
|
||||||
{deleteContainerCmd, "DELETE"},
|
{deleteContainerCmd, "DELETE"},
|
||||||
|
{setExtendedACLCmd, "SETEACL"},
|
||||||
} {
|
} {
|
||||||
commonflags.InitSession(el.cmd, "container "+el.verb)
|
commonflags.InitSession(el.cmd, "container "+el.verb)
|
||||||
}
|
}
|
||||||
|
|
108
cmd/frostfs-cli/modules/container/set_eacl.go
Normal file
108
cmd/frostfs-cli/modules/container/set_eacl.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
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/key"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagVarsSetEACL struct {
|
||||||
|
noPreCheck bool
|
||||||
|
|
||||||
|
srcPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
var setExtendedACLCmd = &cobra.Command{
|
||||||
|
Use: "set-eacl",
|
||||||
|
Short: "Set new extended ACL table for container",
|
||||||
|
Long: `Set new extended ACL table for container.
|
||||||
|
Container ID in EACL table will be substituted with ID from the CLI.`,
|
||||||
|
Run: func(cmd *cobra.Command, _ []string) {
|
||||||
|
id := parseContainerID(cmd)
|
||||||
|
eaclTable := common.ReadEACL(cmd, flagVarsSetEACL.srcPath)
|
||||||
|
|
||||||
|
tok := getSession(cmd)
|
||||||
|
|
||||||
|
eaclTable.SetCID(id)
|
||||||
|
|
||||||
|
pk := key.GetOrGenerate(cmd)
|
||||||
|
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||||
|
|
||||||
|
if !flagVarsSetEACL.noPreCheck {
|
||||||
|
cmd.Println("Checking the ability to modify access rights in the container...")
|
||||||
|
|
||||||
|
extendable, err := internalclient.IsACLExtendable(cmd.Context(), cli, id)
|
||||||
|
commonCmd.ExitOnErr(cmd, "Extensibility check failure: %w", err)
|
||||||
|
|
||||||
|
if !extendable {
|
||||||
|
commonCmd.ExitOnErr(cmd, "", errors.New("container ACL is immutable"))
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Println("ACL extension is enabled in the container, continue processing.")
|
||||||
|
}
|
||||||
|
|
||||||
|
setEACLPrm := internalclient.SetEACLPrm{
|
||||||
|
Client: cli,
|
||||||
|
ClientParams: client.PrmContainerSetEACL{
|
||||||
|
Table: eaclTable,
|
||||||
|
Session: tok,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := internalclient.SetEACL(cmd.Context(), setEACLPrm)
|
||||||
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
|
if containerAwait {
|
||||||
|
exp, err := eaclTable.Marshal()
|
||||||
|
commonCmd.ExitOnErr(cmd, "broken EACL table: %w", err)
|
||||||
|
|
||||||
|
cmd.Println("awaiting...")
|
||||||
|
|
||||||
|
getEACLPrm := internalclient.EACLPrm{
|
||||||
|
Client: cli,
|
||||||
|
ClientParams: client.PrmContainerEACL{
|
||||||
|
ContainerID: &id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < awaitTimeout; i++ {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
res, err := internalclient.EACL(cmd.Context(), getEACLPrm)
|
||||||
|
if err == nil {
|
||||||
|
// compare binary values because EACL could have been set already
|
||||||
|
table := res.EACL()
|
||||||
|
got, err := table.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(exp, got) {
|
||||||
|
cmd.Println("EACL has been persisted on sidechain")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commonCmd.ExitOnErr(cmd, "", errSetEACLTimeout)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func initContainerSetEACLCmd() {
|
||||||
|
commonflags.Init(setExtendedACLCmd)
|
||||||
|
|
||||||
|
flags := setExtendedACLCmd.Flags()
|
||||||
|
flags.StringVar(&containerID, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
|
||||||
|
flags.StringVar(&flagVarsSetEACL.srcPath, "table", "", "path to file with JSON or binary encoded EACL table")
|
||||||
|
flags.BoolVar(&containerAwait, "await", false, "block execution until EACL is persisted")
|
||||||
|
flags.BoolVar(&flagVarsSetEACL.noPreCheck, "no-precheck", false, "do not pre-check the extensibility of the container ACL")
|
||||||
|
}
|
|
@ -18,8 +18,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errCreateTimeout = errors.New("timeout: container has not been persisted on sidechain")
|
errCreateTimeout = errors.New("timeout: container has not been persisted on sidechain")
|
||||||
errDeleteTimeout = errors.New("timeout: container has not been removed from sidechain")
|
errDeleteTimeout = errors.New("timeout: container has not been removed from sidechain")
|
||||||
|
errSetEACLTimeout = errors.New("timeout: EACL has not been persisted on sidechain")
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseContainerID(cmd *cobra.Command) cid.ID {
|
func parseContainerID(cmd *cobra.Command) cid.ID {
|
||||||
|
|
|
@ -20,10 +20,6 @@ const (
|
||||||
awaitFlag = "await"
|
awaitFlag = "await"
|
||||||
noProgressFlag = "no-progress"
|
noProgressFlag = "no-progress"
|
||||||
scopeFlag = "scope"
|
scopeFlag = "scope"
|
||||||
repOneOnlyFlag = "rep-one-only"
|
|
||||||
|
|
||||||
containerWorkerCountFlag = "container-worker-count"
|
|
||||||
objectWorkerCountFlag = "object-worker-count"
|
|
||||||
|
|
||||||
scopeAll = "all"
|
scopeAll = "all"
|
||||||
scopeObjects = "objects"
|
scopeObjects = "objects"
|
||||||
|
@ -68,18 +64,12 @@ func startEvacuateShard(cmd *cobra.Command, _ []string) {
|
||||||
pk := key.Get(cmd)
|
pk := key.Get(cmd)
|
||||||
|
|
||||||
ignoreErrors, _ := cmd.Flags().GetBool(ignoreErrorsFlag)
|
ignoreErrors, _ := cmd.Flags().GetBool(ignoreErrorsFlag)
|
||||||
containerWorkerCount, _ := cmd.Flags().GetUint32(containerWorkerCountFlag)
|
|
||||||
objectWorkerCount, _ := cmd.Flags().GetUint32(objectWorkerCountFlag)
|
|
||||||
repOneOnly, _ := cmd.Flags().GetBool(repOneOnlyFlag)
|
|
||||||
|
|
||||||
req := &control.StartShardEvacuationRequest{
|
req := &control.StartShardEvacuationRequest{
|
||||||
Body: &control.StartShardEvacuationRequest_Body{
|
Body: &control.StartShardEvacuationRequest_Body{
|
||||||
Shard_ID: getShardIDList(cmd),
|
Shard_ID: getShardIDList(cmd),
|
||||||
IgnoreErrors: ignoreErrors,
|
IgnoreErrors: ignoreErrors,
|
||||||
Scope: getEvacuationScope(cmd),
|
Scope: getEvacuationScope(cmd),
|
||||||
ContainerWorkerCount: containerWorkerCount,
|
|
||||||
ObjectWorkerCount: objectWorkerCount,
|
|
||||||
RepOneOnly: repOneOnly,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,9 +371,6 @@ func initControlStartEvacuationShardCmd() {
|
||||||
flags.String(scopeFlag, scopeAll, fmt.Sprintf("Evacuation scope; possible values: %s, %s, %s", scopeTrees, scopeObjects, scopeAll))
|
flags.String(scopeFlag, scopeAll, fmt.Sprintf("Evacuation scope; possible values: %s, %s, %s", scopeTrees, scopeObjects, scopeAll))
|
||||||
flags.Bool(awaitFlag, false, "Block execution until evacuation is completed")
|
flags.Bool(awaitFlag, false, "Block execution until evacuation is completed")
|
||||||
flags.Bool(noProgressFlag, false, fmt.Sprintf("Print progress if %s provided", awaitFlag))
|
flags.Bool(noProgressFlag, false, fmt.Sprintf("Print progress if %s provided", awaitFlag))
|
||||||
flags.Uint32(containerWorkerCountFlag, 0, "Count of concurrent container evacuation workers")
|
|
||||||
flags.Uint32(objectWorkerCountFlag, 0, "Count of concurrent object evacuation workers")
|
|
||||||
flags.Bool(repOneOnlyFlag, false, "Evacuate objects only from containers with policy 'REP 1 ...'")
|
|
||||||
|
|
||||||
startEvacuationShardCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
startEvacuationShardCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package control
|
package control
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
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/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"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||||
|
@ -27,7 +24,6 @@ func initControlHealthCheckCmd() {
|
||||||
|
|
||||||
flags := healthCheckCmd.Flags()
|
flags := healthCheckCmd.Flags()
|
||||||
flags.Bool(healthcheckIRFlag, false, "Communicate with IR node")
|
flags.Bool(healthcheckIRFlag, false, "Communicate with IR node")
|
||||||
flags.BoolP(commonflags.QuietFlag, commonflags.QuietFlagShorthand, false, commonflags.QuietFlagUsage)
|
|
||||||
_ = flags.MarkDeprecated(healthcheckIRFlag, "for health check of inner ring nodes, use the 'control ir healthcheck' command instead.")
|
_ = flags.MarkDeprecated(healthcheckIRFlag, "for health check of inner ring nodes, use the 'control ir healthcheck' command instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,12 +50,6 @@ func healthCheck(cmd *cobra.Command, args []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
||||||
if quietFlag, _ := cmd.Flags().GetBool(commonflags.QuietFlag); quietFlag {
|
|
||||||
if resp.GetBody().GetHealthStatus() == control.HealthStatus_READY {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Network status: %s\n", resp.GetBody().GetNetmapStatus())
|
cmd.Printf("Network status: %s\n", resp.GetBody().GetNetmapStatus())
|
||||||
cmd.Printf("Health status: %s\n", resp.GetBody().GetHealthStatus())
|
cmd.Printf("Health status: %s\n", resp.GetBody().GetHealthStatus())
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package control
|
package control
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
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/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"
|
||||||
ircontrol "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
ircontrol "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
||||||
|
@ -21,8 +18,6 @@ var irHealthCheckCmd = &cobra.Command{
|
||||||
|
|
||||||
func initControlIRHealthCheckCmd() {
|
func initControlIRHealthCheckCmd() {
|
||||||
initControlFlags(irHealthCheckCmd)
|
initControlFlags(irHealthCheckCmd)
|
||||||
flags := irHealthCheckCmd.Flags()
|
|
||||||
flags.BoolP(commonflags.QuietFlag, commonflags.QuietFlagShorthand, false, commonflags.QuietFlagUsage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func irHealthCheck(cmd *cobra.Command, _ []string) {
|
func irHealthCheck(cmd *cobra.Command, _ []string) {
|
||||||
|
@ -44,12 +39,6 @@ func irHealthCheck(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
||||||
if quietFlag, _ := cmd.Flags().GetBool(commonflags.QuietFlag); quietFlag {
|
|
||||||
if resp.GetBody().GetHealthStatus() == ircontrol.HealthStatus_READY {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Health status: %s\n", resp.GetBody().GetHealthStatus())
|
cmd.Printf("Health status: %s\n", resp.GetBody().GetHealthStatus())
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -54,10 +52,6 @@ func listTargets(cmd *cobra.Command, _ []string) {
|
||||||
resp, err = control.ListTargetsLocalOverrides(client, req)
|
resp, err = control.ListTargetsLocalOverrides(client, req)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil && status.Code(err) == codes.NotFound {
|
|
||||||
cmd.Println("Local overrides are not defined for any target.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
package control
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
|
||||||
"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"
|
|
||||||
"github.com/mr-tron/base58"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
fillPercentFlag = "fill_percent"
|
|
||||||
)
|
|
||||||
|
|
||||||
var shardsRebuildCmd = &cobra.Command{
|
|
||||||
Use: "rebuild",
|
|
||||||
Short: "Rebuild shards",
|
|
||||||
Long: "Rebuild reclaims storage occupied by dead objects and adjusts the storage structure according to the configuration (for blobovnicza only now)",
|
|
||||||
Run: shardsRebuild,
|
|
||||||
}
|
|
||||||
|
|
||||||
func shardsRebuild(cmd *cobra.Command, _ []string) {
|
|
||||||
pk := key.Get(cmd)
|
|
||||||
|
|
||||||
req := &control.StartShardRebuildRequest{
|
|
||||||
Body: &control.StartShardRebuildRequest_Body{
|
|
||||||
Shard_ID: getShardIDList(cmd),
|
|
||||||
TargetFillPercent: getFillPercentValue(cmd),
|
|
||||||
ConcurrencyLimit: getConcurrencyValue(cmd),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
|
||||||
|
|
||||||
cli := getClient(cmd, pk)
|
|
||||||
|
|
||||||
var resp *control.StartShardRebuildResponse
|
|
||||||
var err error
|
|
||||||
err = cli.ExecRaw(func(client *rawclient.Client) error {
|
|
||||||
resp, err = control.StartShardRebuild(client, req)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
|
||||||
|
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
|
||||||
|
|
||||||
var success, failed uint
|
|
||||||
for _, res := range resp.GetBody().GetResults() {
|
|
||||||
if res.GetSuccess() {
|
|
||||||
success++
|
|
||||||
cmd.Printf("Shard %s: OK\n", base58.Encode(res.GetShard_ID()))
|
|
||||||
} else {
|
|
||||||
failed++
|
|
||||||
cmd.Printf("Shard %s: failed with error %q\n", base58.Encode(res.GetShard_ID()), res.GetError())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd.Printf("Total: %d success, %d failed\n", success, failed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFillPercentValue(cmd *cobra.Command) uint32 {
|
|
||||||
v, _ := cmd.Flags().GetUint32(fillPercentFlag)
|
|
||||||
if v <= 0 || v > 100 {
|
|
||||||
commonCmd.ExitOnErr(cmd, "invalid fill_percent value", fmt.Errorf("fill_percent value must be (0, 100], current value: %d", v))
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func getConcurrencyValue(cmd *cobra.Command) uint32 {
|
|
||||||
v, _ := cmd.Flags().GetUint32(concurrencyFlag)
|
|
||||||
if v <= 0 || v > 10000 {
|
|
||||||
commonCmd.ExitOnErr(cmd, "invalid concurrency value", fmt.Errorf("concurrency value must be (0, 10 000], current value: %d", v))
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func initControlShardRebuildCmd() {
|
|
||||||
initControlFlags(shardsRebuildCmd)
|
|
||||||
|
|
||||||
flags := shardsRebuildCmd.Flags()
|
|
||||||
flags.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding")
|
|
||||||
flags.Bool(shardAllFlag, false, "Process all shards")
|
|
||||||
flags.Uint32(fillPercentFlag, 80, "Target fill percent to reclaim space")
|
|
||||||
flags.Uint32(concurrencyFlag, 20, "Maximum count of concurrently rebuilding files")
|
|
||||||
setShardModeCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
|
||||||
}
|
|
|
@ -1,10 +1,7 @@
|
||||||
package control
|
package control
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
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/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
||||||
|
@ -12,7 +9,6 @@ import (
|
||||||
"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"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,13 +18,8 @@ const (
|
||||||
netmapStatusOnline = "online"
|
netmapStatusOnline = "online"
|
||||||
netmapStatusOffline = "offline"
|
netmapStatusOffline = "offline"
|
||||||
netmapStatusMaintenance = "maintenance"
|
netmapStatusMaintenance = "maintenance"
|
||||||
|
|
||||||
maxSetStatusMaxWaitTime = 30 * time.Minute
|
|
||||||
setStatusWaitTimeout = 30 * time.Second
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNetmapStatusAwaitFailed = errors.New("netmap status hasn't changed for 30 minutes")
|
|
||||||
|
|
||||||
var setNetmapStatusCmd = &cobra.Command{
|
var setNetmapStatusCmd = &cobra.Command{
|
||||||
Use: "set-status",
|
Use: "set-status",
|
||||||
Short: "Set status of the storage node in FrostFS network map",
|
Short: "Set status of the storage node in FrostFS network map",
|
||||||
|
@ -52,8 +43,6 @@ func initControlSetNetmapStatusCmd() {
|
||||||
|
|
||||||
flags.BoolP(commonflags.ForceFlag, commonflags.ForceFlagShorthand, false,
|
flags.BoolP(commonflags.ForceFlag, commonflags.ForceFlagShorthand, false,
|
||||||
"Force turning to local maintenance")
|
"Force turning to local maintenance")
|
||||||
|
|
||||||
flags.Bool(commonflags.AwaitFlag, false, commonflags.AwaitFlagUsage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setNetmapStatus(cmd *cobra.Command, _ []string) {
|
func setNetmapStatus(cmd *cobra.Command, _ []string) {
|
||||||
|
@ -67,27 +56,22 @@ func setNetmapStatus(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await, _ := cmd.Flags().GetBool(commonflags.AwaitFlag)
|
|
||||||
var targetStatus control.NetmapStatus
|
|
||||||
switch st, _ := cmd.Flags().GetString(netmapStatusFlag); st {
|
switch st, _ := cmd.Flags().GetString(netmapStatusFlag); st {
|
||||||
default:
|
default:
|
||||||
commonCmd.ExitOnErr(cmd, "", fmt.Errorf("unsupported status %s", st))
|
commonCmd.ExitOnErr(cmd, "", fmt.Errorf("unsupported status %s", st))
|
||||||
case netmapStatusOnline:
|
case netmapStatusOnline:
|
||||||
body.SetStatus(control.NetmapStatus_ONLINE)
|
body.SetStatus(control.NetmapStatus_ONLINE)
|
||||||
printIgnoreForce(control.NetmapStatus_ONLINE)
|
printIgnoreForce(control.NetmapStatus_ONLINE)
|
||||||
targetStatus = control.NetmapStatus_ONLINE
|
|
||||||
case netmapStatusOffline:
|
case netmapStatusOffline:
|
||||||
body.SetStatus(control.NetmapStatus_OFFLINE)
|
body.SetStatus(control.NetmapStatus_OFFLINE)
|
||||||
printIgnoreForce(control.NetmapStatus_OFFLINE)
|
printIgnoreForce(control.NetmapStatus_OFFLINE)
|
||||||
targetStatus = control.NetmapStatus_OFFLINE
|
|
||||||
case netmapStatusMaintenance:
|
case netmapStatusMaintenance:
|
||||||
body.SetStatus(control.NetmapStatus_MAINTENANCE)
|
body.SetStatus(control.NetmapStatus_MAINTENANCE)
|
||||||
|
|
||||||
if force {
|
if force {
|
||||||
body.SetForceMaintenance(true)
|
body.SetForceMaintenance()
|
||||||
common.PrintVerbose(cmd, "Local maintenance will be forced.")
|
common.PrintVerbose(cmd, "Local maintenance will be forced.")
|
||||||
}
|
}
|
||||||
targetStatus = control.NetmapStatus_MAINTENANCE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req := new(control.SetNetmapStatusRequest)
|
req := new(control.SetNetmapStatusRequest)
|
||||||
|
@ -108,52 +92,4 @@ func setNetmapStatus(cmd *cobra.Command, _ []string) {
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
||||||
|
|
||||||
cmd.Println("Network status update request successfully sent.")
|
cmd.Println("Network status update request successfully sent.")
|
||||||
|
|
||||||
if await {
|
|
||||||
awaitSetNetmapStatus(cmd, pk, cli, targetStatus)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func awaitSetNetmapStatus(cmd *cobra.Command, pk *ecdsa.PrivateKey, cli *client.Client, targetStatus control.NetmapStatus) {
|
|
||||||
req := &control.GetNetmapStatusRequest{
|
|
||||||
Body: &control.GetNetmapStatusRequest_Body{},
|
|
||||||
}
|
|
||||||
signRequest(cmd, pk, req)
|
|
||||||
var epoch uint64
|
|
||||||
var status control.NetmapStatus
|
|
||||||
startTime := time.Now()
|
|
||||||
cmd.Println("Wait until epoch and netmap status change...")
|
|
||||||
for {
|
|
||||||
var resp *control.GetNetmapStatusResponse
|
|
||||||
var err error
|
|
||||||
err = cli.ExecRaw(func(client *rawclient.Client) error {
|
|
||||||
resp, err = control.GetNetmapStatus(client, req)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
commonCmd.ExitOnErr(cmd, "failed to get current netmap status: %w", err)
|
|
||||||
|
|
||||||
if epoch == 0 {
|
|
||||||
epoch = resp.GetBody().GetEpoch()
|
|
||||||
}
|
|
||||||
|
|
||||||
status = resp.GetBody().GetStatus()
|
|
||||||
if resp.GetBody().GetEpoch() > epoch {
|
|
||||||
epoch = resp.GetBody().GetEpoch()
|
|
||||||
cmd.Printf("Epoch changed to %d\n", resp.GetBody().GetEpoch())
|
|
||||||
}
|
|
||||||
|
|
||||||
if status == targetStatus {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if time.Since(startTime) > maxSetStatusMaxWaitTime {
|
|
||||||
commonCmd.ExitOnErr(cmd, "failed to wait netmap status: %w", errNetmapStatusAwaitFailed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(setStatusWaitTimeout)
|
|
||||||
|
|
||||||
cmd.Printf("Current netmap status '%s', target status '%s'\n", status.String(), targetStatus.String())
|
|
||||||
}
|
|
||||||
cmd.Printf("Netmap status changed to '%s' successfully.\n", status.String())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ func initControlShardsCmd() {
|
||||||
shardsCmd.AddCommand(doctorCmd)
|
shardsCmd.AddCommand(doctorCmd)
|
||||||
shardsCmd.AddCommand(writecacheShardCmd)
|
shardsCmd.AddCommand(writecacheShardCmd)
|
||||||
shardsCmd.AddCommand(shardsDetachCmd)
|
shardsCmd.AddCommand(shardsDetachCmd)
|
||||||
shardsCmd.AddCommand(shardsRebuildCmd)
|
|
||||||
|
|
||||||
initControlShardsListCmd()
|
initControlShardsListCmd()
|
||||||
initControlSetShardModeCmd()
|
initControlSetShardModeCmd()
|
||||||
|
@ -29,5 +28,4 @@ func initControlShardsCmd() {
|
||||||
initControlDoctorCmd()
|
initControlDoctorCmd()
|
||||||
initControlShardsWritecacheCmd()
|
initControlShardsWritecacheCmd()
|
||||||
initControlShardsDetachCmd()
|
initControlShardsDetachCmd()
|
||||||
initControlShardRebuildCmd()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,18 +61,17 @@ func listShards(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prettyPrintShardsJSON(cmd *cobra.Command, ii []control.ShardInfo) {
|
func prettyPrintShardsJSON(cmd *cobra.Command, ii []*control.ShardInfo) {
|
||||||
out := make([]map[string]any, 0, len(ii))
|
out := make([]map[string]any, 0, len(ii))
|
||||||
for _, i := range ii {
|
for _, i := range ii {
|
||||||
out = append(out, map[string]any{
|
out = append(out, map[string]any{
|
||||||
"shard_id": base58.Encode(i.GetShard_ID()),
|
"shard_id": base58.Encode(i.GetShard_ID()),
|
||||||
"mode": shardModeToString(i.GetMode()),
|
"mode": shardModeToString(i.GetMode()),
|
||||||
"metabase": i.GetMetabasePath(),
|
"metabase": i.GetMetabasePath(),
|
||||||
"blobstor": i.GetBlobstor(),
|
"blobstor": i.GetBlobstor(),
|
||||||
"writecache": i.GetWritecachePath(),
|
"writecache": i.GetWritecachePath(),
|
||||||
"pilorama": i.GetPiloramaPath(),
|
"pilorama": i.GetPiloramaPath(),
|
||||||
"error_count": i.GetErrorCount(),
|
"error_count": i.GetErrorCount(),
|
||||||
"evacuation_in_progress": i.GetEvacuationInProgress(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,10 +80,10 @@ func prettyPrintShardsJSON(cmd *cobra.Command, ii []control.ShardInfo) {
|
||||||
enc.SetIndent("", " ")
|
enc.SetIndent("", " ")
|
||||||
commonCmd.ExitOnErr(cmd, "cannot shard info to JSON: %w", enc.Encode(out))
|
commonCmd.ExitOnErr(cmd, "cannot shard info to JSON: %w", enc.Encode(out))
|
||||||
|
|
||||||
cmd.Print(buf.String()) // pretty printer emits newline, so no need for Println
|
cmd.Print(buf.String()) // pretty printer emits newline, to no need for Println
|
||||||
}
|
}
|
||||||
|
|
||||||
func prettyPrintShards(cmd *cobra.Command, ii []control.ShardInfo) {
|
func prettyPrintShards(cmd *cobra.Command, ii []*control.ShardInfo) {
|
||||||
for _, i := range ii {
|
for _, i := range ii {
|
||||||
pathPrinter := func(name, path string) string {
|
pathPrinter := func(name, path string) string {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
@ -106,8 +105,7 @@ func prettyPrintShards(cmd *cobra.Command, ii []control.ShardInfo) {
|
||||||
sb.String()+
|
sb.String()+
|
||||||
pathPrinter("Write-cache", i.GetWritecachePath())+
|
pathPrinter("Write-cache", i.GetWritecachePath())+
|
||||||
pathPrinter("Pilorama", i.GetPiloramaPath())+
|
pathPrinter("Pilorama", i.GetPiloramaPath())+
|
||||||
fmt.Sprintf("Error count: %d\n", i.GetErrorCount())+
|
fmt.Sprintf("Error count: %d\n", i.GetErrorCount()),
|
||||||
fmt.Sprintf("Evacuation in progress: %t\n", i.GetEvacuationInProgress()),
|
|
||||||
base58.Encode(i.GetShard_ID()),
|
base58.Encode(i.GetShard_ID()),
|
||||||
shardModeToString(i.GetMode()),
|
shardModeToString(i.GetMode()),
|
||||||
)
|
)
|
||||||
|
@ -123,7 +121,7 @@ func shardModeToString(m control.ShardMode) string {
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortShardsByID(ii []control.ShardInfo) {
|
func sortShardsByID(ii []*control.ShardInfo) {
|
||||||
sort.Slice(ii, func(i, j int) bool {
|
sort.Slice(ii, func(i, j int) bool {
|
||||||
return bytes.Compare(ii[i].GetShard_ID(), ii[j].GetShard_ID()) < 0
|
return bytes.Compare(ii[i].GetShard_ID(), ii[j].GetShard_ID()) < 0
|
||||||
})
|
})
|
||||||
|
|
|
@ -117,10 +117,10 @@ func setShardMode(cmd *cobra.Command, _ []string) {
|
||||||
req.SetBody(body)
|
req.SetBody(body)
|
||||||
|
|
||||||
body.SetMode(mode)
|
body.SetMode(mode)
|
||||||
body.SetShard_ID(getShardIDList(cmd))
|
body.SetShardIDList(getShardIDList(cmd))
|
||||||
|
|
||||||
reset, _ := cmd.Flags().GetBool(shardClearErrorsFlag)
|
reset, _ := cmd.Flags().GetBool(shardClearErrorsFlag)
|
||||||
body.SetResetErrorCounter(reset)
|
body.ClearErrorCounter(reset)
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
signRequest(cmd, pk, req)
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ func verifyResponse(cmd *cobra.Command,
|
||||||
GetSign() []byte
|
GetSign() []byte
|
||||||
},
|
},
|
||||||
body interface {
|
body interface {
|
||||||
MarshalProtobuf([]byte) []byte
|
StableMarshal([]byte) []byte
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if sigControl == nil {
|
if sigControl == nil {
|
||||||
|
@ -60,7 +60,7 @@ func verifyResponse(cmd *cobra.Command,
|
||||||
var sig frostfscrypto.Signature
|
var sig frostfscrypto.Signature
|
||||||
commonCmd.ExitOnErr(cmd, "can't read signature: %w", sig.ReadFromV2(sigV2))
|
commonCmd.ExitOnErr(cmd, "can't read signature: %w", sig.ReadFromV2(sigV2))
|
||||||
|
|
||||||
if !sig.Verify(body.MarshalProtobuf(nil)) {
|
if !sig.Verify(body.StableMarshal(nil)) {
|
||||||
commonCmd.ExitOnErr(cmd, "", errors.New("invalid response signature"))
|
commonCmd.ExitOnErr(cmd, "", errors.New("invalid response signature"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,6 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
asyncFlag = "async"
|
|
||||||
restoreModeFlag = "restore-mode"
|
|
||||||
shrinkFlag = "shrink"
|
|
||||||
)
|
|
||||||
|
|
||||||
var writecacheShardCmd = &cobra.Command{
|
var writecacheShardCmd = &cobra.Command{
|
||||||
Use: "writecache",
|
Use: "writecache",
|
||||||
Short: "Operations with storage node's write-cache",
|
Short: "Operations with storage node's write-cache",
|
||||||
|
@ -32,16 +26,10 @@ func sealWritecache(cmd *cobra.Command, _ []string) {
|
||||||
pk := key.Get(cmd)
|
pk := key.Get(cmd)
|
||||||
|
|
||||||
ignoreErrors, _ := cmd.Flags().GetBool(ignoreErrorsFlag)
|
ignoreErrors, _ := cmd.Flags().GetBool(ignoreErrorsFlag)
|
||||||
async, _ := cmd.Flags().GetBool(asyncFlag)
|
|
||||||
restoreMode, _ := cmd.Flags().GetBool(restoreModeFlag)
|
|
||||||
shrink, _ := cmd.Flags().GetBool(shrinkFlag)
|
|
||||||
|
|
||||||
req := &control.SealWriteCacheRequest{Body: &control.SealWriteCacheRequest_Body{
|
req := &control.SealWriteCacheRequest{Body: &control.SealWriteCacheRequest_Body{
|
||||||
Shard_ID: getShardIDList(cmd),
|
Shard_ID: getShardIDList(cmd),
|
||||||
IgnoreErrors: ignoreErrors,
|
IgnoreErrors: ignoreErrors,
|
||||||
Async: async,
|
|
||||||
RestoreMode: restoreMode,
|
|
||||||
Shrink: shrink,
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
signRequest(cmd, pk, req)
|
signRequest(cmd, pk, req)
|
||||||
|
@ -80,9 +68,6 @@ func initControlShardsWritecacheCmd() {
|
||||||
ff.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding")
|
ff.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding")
|
||||||
ff.Bool(shardAllFlag, false, "Process all shards")
|
ff.Bool(shardAllFlag, false, "Process all shards")
|
||||||
ff.Bool(ignoreErrorsFlag, true, "Skip invalid/unreadable objects")
|
ff.Bool(ignoreErrorsFlag, true, "Skip invalid/unreadable objects")
|
||||||
ff.Bool(asyncFlag, false, "Run operation in background")
|
|
||||||
ff.Bool(restoreModeFlag, false, "Restore writecache's mode after sealing")
|
|
||||||
ff.Bool(shrinkFlag, false, "Shrink writecache's internal storage")
|
|
||||||
|
|
||||||
sealWritecacheShardCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
sealWritecacheShardCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,14 +49,14 @@ func prettyPrintNodeInfo(cmd *cobra.Command, i netmap.NodeInfo) {
|
||||||
cmd.Println("key:", hex.EncodeToString(i.PublicKey()))
|
cmd.Println("key:", hex.EncodeToString(i.PublicKey()))
|
||||||
|
|
||||||
var stateWord string
|
var stateWord string
|
||||||
switch i.Status() {
|
switch {
|
||||||
default:
|
default:
|
||||||
stateWord = "<undefined>"
|
stateWord = "<undefined>"
|
||||||
case netmap.Online:
|
case i.IsOnline():
|
||||||
stateWord = "online"
|
stateWord = "online"
|
||||||
case netmap.Offline:
|
case i.IsOffline():
|
||||||
stateWord = "offline"
|
stateWord = "offline"
|
||||||
case netmap.Maintenance:
|
case i.IsMaintenance():
|
||||||
stateWord = "maintenance"
|
stateWord = "maintenance"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ func initObjectHeadCmd() {
|
||||||
_ = objectHeadCmd.MarkFlagRequired(commonflags.OIDFlag)
|
_ = objectHeadCmd.MarkFlagRequired(commonflags.OIDFlag)
|
||||||
|
|
||||||
flags.String(fileFlag, "", "File to write header to. Default: stdout.")
|
flags.String(fileFlag, "", "File to write header to. Default: stdout.")
|
||||||
|
flags.Bool("main-only", false, "Return only main fields")
|
||||||
flags.Bool(commonflags.JSON, false, "Marshal output in JSON")
|
flags.Bool(commonflags.JSON, false, "Marshal output in JSON")
|
||||||
flags.Bool("proto", false, "Marshal output in Protobuf")
|
flags.Bool("proto", false, "Marshal output in Protobuf")
|
||||||
flags.Bool(rawFlag, false, rawFlagDesc)
|
flags.Bool(rawFlag, false, rawFlagDesc)
|
||||||
|
@ -48,6 +49,7 @@ func getObjectHeader(cmd *cobra.Command, _ []string) {
|
||||||
var obj oid.ID
|
var obj oid.ID
|
||||||
|
|
||||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
||||||
|
mainOnly, _ := cmd.Flags().GetBool("main-only")
|
||||||
pk := key.GetOrGenerate(cmd)
|
pk := key.GetOrGenerate(cmd)
|
||||||
|
|
||||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||||
|
@ -60,6 +62,7 @@ func getObjectHeader(cmd *cobra.Command, _ []string) {
|
||||||
raw, _ := cmd.Flags().GetBool(rawFlag)
|
raw, _ := cmd.Flags().GetBool(rawFlag)
|
||||||
prm.SetRawFlag(raw)
|
prm.SetRawFlag(raw)
|
||||||
prm.SetAddress(objAddr)
|
prm.SetAddress(objAddr)
|
||||||
|
prm.SetMainOnlyFlag(mainOnly)
|
||||||
|
|
||||||
res, err := internalclient.HeadObject(cmd.Context(), prm)
|
res, err := internalclient.HeadObject(cmd.Context(), prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -30,14 +30,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
verifyPresenceAllFlag = "verify-presence-all"
|
verifyPresenceAllFlag = "verify-presence-all"
|
||||||
preferInternalAddressesFlag = "prefer-internal-addresses"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var errNoAvailableEndpoint = errors.New("failed to create client: no available endpoint")
|
||||||
errNoAvailableEndpoint = errors.New("failed to create client: no available endpoint")
|
|
||||||
errMalformedComplexObject = errors.New("object consists of EC and non EC parts")
|
|
||||||
)
|
|
||||||
|
|
||||||
type phyObject struct {
|
type phyObject struct {
|
||||||
containerID cid.ID
|
containerID cid.ID
|
||||||
|
@ -98,7 +94,6 @@ func initObjectNodesCmd() {
|
||||||
|
|
||||||
flags.Bool(verifyPresenceAllFlag, false, "Verify the actual presence of the object on all netmap nodes.")
|
flags.Bool(verifyPresenceAllFlag, false, "Verify the actual presence of the object on all netmap nodes.")
|
||||||
flags.Bool(commonflags.JSON, false, "Print information about the object placement as json.")
|
flags.Bool(commonflags.JSON, false, "Print information about the object placement as json.")
|
||||||
flags.Bool(preferInternalAddressesFlag, false, "Use internal addresses first to get object info.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func objectNodes(cmd *cobra.Command, _ []string) {
|
func objectNodes(cmd *cobra.Command, _ []string) {
|
||||||
|
@ -172,7 +167,7 @@ func getComplexObjectParts(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *
|
||||||
func getCompexObjectMembers(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, prmHead internalclient.HeadObjectPrm, errSplitInfo *objectSDK.SplitInfoError) []oid.ID {
|
func getCompexObjectMembers(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli *client.Client, prmHead internalclient.HeadObjectPrm, errSplitInfo *objectSDK.SplitInfoError) []oid.ID {
|
||||||
splitInfo := errSplitInfo.SplitInfo()
|
splitInfo := errSplitInfo.SplitInfo()
|
||||||
|
|
||||||
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnrID); ok {
|
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnrID, false); ok {
|
||||||
return members
|
return members
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,63 +180,29 @@ func getCompexObjectMembers(cmd *cobra.Command, cnrID cid.ID, objID oid.ID, cli
|
||||||
|
|
||||||
func flattenComplexMembersIfECContainer(cmd *cobra.Command, cnrID cid.ID, members []oid.ID, prmHead internalclient.HeadObjectPrm) []phyObject {
|
func flattenComplexMembersIfECContainer(cmd *cobra.Command, cnrID cid.ID, members []oid.ID, prmHead internalclient.HeadObjectPrm) []phyObject {
|
||||||
result := make([]phyObject, 0, len(members))
|
result := make([]phyObject, 0, len(members))
|
||||||
var hasNonEC, hasEC bool
|
var addrObj oid.Address
|
||||||
var resultGuard sync.Mutex
|
addrObj.SetContainer(cnrID)
|
||||||
|
|
||||||
if len(members) == 0 {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
prmHead.SetRawFlag(true) // to get an error instead of whole object
|
prmHead.SetRawFlag(true) // to get an error instead of whole object
|
||||||
|
for _, partObjID := range members {
|
||||||
|
addrObj.SetObject(partObjID)
|
||||||
|
prmHead.SetAddress(addrObj)
|
||||||
|
|
||||||
eg, egCtx := errgroup.WithContext(cmd.Context())
|
_, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
||||||
for idx := range len(members) {
|
var ecInfoError *objectSDK.ECInfoError
|
||||||
partObjID := members[idx]
|
if errors.As(err, &ecInfoError) {
|
||||||
|
chunks := getECObjectChunks(cmd, cnrID, partObjID, ecInfoError)
|
||||||
eg.Go(func() error {
|
result = append(result, chunks...)
|
||||||
partHeadPrm := prmHead
|
continue
|
||||||
var partAddr oid.Address
|
} else if err == nil { // not EC object, so all members must be phy objects
|
||||||
partAddr.SetContainer(cnrID)
|
for _, member := range members {
|
||||||
partAddr.SetObject(partObjID)
|
result = append(result, phyObject{
|
||||||
partHeadPrm.SetAddress(partAddr)
|
containerID: cnrID,
|
||||||
|
objectID: member,
|
||||||
obj, err := internalclient.HeadObject(egCtx, partHeadPrm)
|
})
|
||||||
if err != nil {
|
|
||||||
var ecInfoError *objectSDK.ECInfoError
|
|
||||||
if errors.As(err, &ecInfoError) {
|
|
||||||
resultGuard.Lock()
|
|
||||||
defer resultGuard.Unlock()
|
|
||||||
result = append(result, getECObjectChunks(cmd, cnrID, partObjID, ecInfoError)...)
|
|
||||||
hasEC = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
break
|
||||||
if obj.Header().Type() != objectSDK.TypeRegular {
|
}
|
||||||
commonCmd.ExitOnErr(cmd, "failed to flatten parts of complex object: %w", fmt.Errorf("object '%s' with type '%s' is not supported as part of complex object", partAddr, obj.Header().Type()))
|
commonCmd.ExitOnErr(cmd, "failed to read EC chunk of complex object: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj.Header().Children()) > 0 {
|
|
||||||
// linking object is not data object, so skip it
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
resultGuard.Lock()
|
|
||||||
defer resultGuard.Unlock()
|
|
||||||
result = append(result, phyObject{
|
|
||||||
containerID: cnrID,
|
|
||||||
objectID: partObjID,
|
|
||||||
})
|
|
||||||
hasNonEC = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
commonCmd.ExitOnErr(cmd, "failed to flatten parts of complex object: %w", eg.Wait())
|
|
||||||
if hasEC && hasNonEC {
|
|
||||||
commonCmd.ExitOnErr(cmd, "failed to flatten parts of complex object: %w", errMalformedComplexObject)
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -393,6 +354,8 @@ func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.
|
||||||
|
|
||||||
eg, egCtx := errgroup.WithContext(cmd.Context())
|
eg, egCtx := errgroup.WithContext(cmd.Context())
|
||||||
for _, cand := range candidates {
|
for _, cand := range candidates {
|
||||||
|
cand := cand
|
||||||
|
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
cli, err := createClient(egCtx, cmd, cand, pk)
|
cli, err := createClient(egCtx, cmd, cand, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -403,6 +366,7 @@ func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, object := range objects {
|
for _, object := range objects {
|
||||||
|
object := object
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
stored, err := isObjectStoredOnNode(egCtx, cmd, object.containerID, object.objectID, cli, pk)
|
stored, err := isObjectStoredOnNode(egCtx, cmd, object.containerID, object.objectID, cli, pk)
|
||||||
resultMtx.Lock()
|
resultMtx.Lock()
|
||||||
|
@ -446,20 +410,11 @@ func getNodesToCheckObjectExistance(cmd *cobra.Command, netmap *netmapSDK.NetMap
|
||||||
func createClient(ctx context.Context, cmd *cobra.Command, candidate netmapSDK.NodeInfo, pk *ecdsa.PrivateKey) (*client.Client, error) {
|
func createClient(ctx context.Context, cmd *cobra.Command, candidate netmapSDK.NodeInfo, pk *ecdsa.PrivateKey) (*client.Client, error) {
|
||||||
var cli *client.Client
|
var cli *client.Client
|
||||||
var addresses []string
|
var addresses []string
|
||||||
if preferInternal, _ := cmd.Flags().GetBool(preferInternalAddressesFlag); preferInternal {
|
candidate.IterateNetworkEndpoints(func(s string) bool {
|
||||||
candidate.IterateNetworkEndpoints(func(s string) bool {
|
addresses = append(addresses, s)
|
||||||
addresses = append(addresses, s)
|
return false
|
||||||
return false
|
})
|
||||||
})
|
addresses = append(addresses, candidate.ExternalAddresses()...)
|
||||||
addresses = append(addresses, candidate.ExternalAddresses()...)
|
|
||||||
} else {
|
|
||||||
addresses = append(addresses, candidate.ExternalAddresses()...)
|
|
||||||
candidate.IterateNetworkEndpoints(func(s string) bool {
|
|
||||||
addresses = append(addresses, s)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastErr error
|
var lastErr error
|
||||||
for _, address := range addresses {
|
for _, address := range addresses {
|
||||||
var networkAddr network.Address
|
var networkAddr network.Address
|
||||||
|
@ -503,6 +458,7 @@ func isObjectStoredOnNode(ctx context.Context, cmd *cobra.Command, cnrID cid.ID,
|
||||||
if errors.As(err, ¬Found) || errors.As(err, &removed) {
|
if errors.As(err, ¬Found) || errors.As(err, &removed) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
cmd.Printf("failed to get object %s from client\n", objID.EncodeToString())
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
package object
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/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"
|
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
newAttrsFlagName = "new-attrs"
|
|
||||||
replaceAttrsFlagName = "replace-attrs"
|
|
||||||
rangeFlagName = "range"
|
|
||||||
payloadFlagName = "payload"
|
|
||||||
)
|
|
||||||
|
|
||||||
var objectPatchCmd = &cobra.Command{
|
|
||||||
Use: "patch",
|
|
||||||
Run: patch,
|
|
||||||
Short: "Patch FrostFS object",
|
|
||||||
Long: "Patch FrostFS object. Each range passed to the command requires to pass a corresponding patch payload.",
|
|
||||||
Example: `
|
|
||||||
frostfs-cli -c config.yml -r 127.0.0.1:8080 object patch --cid <CID> --oid <OID> --new-attrs 'key1=val1,key2=val2' --replace-attrs
|
|
||||||
frostfs-cli -c config.yml -r 127.0.0.1:8080 object patch --cid <CID> --oid <OID> --range offX:lnX --payload /path/to/payloadX --range offY:lnY --payload /path/to/payloadY
|
|
||||||
frostfs-cli -c config.yml -r 127.0.0.1:8080 object patch --cid <CID> --oid <OID> --new-attrs 'key1=val1,key2=val2' --replace-attrs --range offX:lnX --payload /path/to/payload
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func initObjectPatchCmd() {
|
|
||||||
commonflags.Init(objectPatchCmd)
|
|
||||||
initFlagSession(objectPatchCmd, "PATCH")
|
|
||||||
|
|
||||||
flags := objectPatchCmd.Flags()
|
|
||||||
|
|
||||||
flags.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
|
|
||||||
_ = objectRangeCmd.MarkFlagRequired(commonflags.CIDFlag)
|
|
||||||
|
|
||||||
flags.String(commonflags.OIDFlag, "", commonflags.OIDFlagUsage)
|
|
||||||
_ = objectRangeCmd.MarkFlagRequired(commonflags.OIDFlag)
|
|
||||||
|
|
||||||
flags.String(newAttrsFlagName, "", "New object attributes in form of Key1=Value1,Key2=Value2")
|
|
||||||
flags.Bool(replaceAttrsFlagName, false, "Replace object attributes by new ones.")
|
|
||||||
flags.StringSlice(rangeFlagName, []string{}, "Range to which patch payload is applied. Format: offset:length")
|
|
||||||
flags.StringSlice(payloadFlagName, []string{}, "Path to file with patch payload.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func patch(cmd *cobra.Command, _ []string) {
|
|
||||||
var cnr cid.ID
|
|
||||||
var obj oid.ID
|
|
||||||
|
|
||||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
|
||||||
|
|
||||||
ranges, err := getRangeSlice(cmd)
|
|
||||||
commonCmd.ExitOnErr(cmd, "", err)
|
|
||||||
|
|
||||||
payloads := patchPayloadPaths(cmd)
|
|
||||||
|
|
||||||
if len(ranges) != len(payloads) {
|
|
||||||
commonCmd.ExitOnErr(cmd, "", fmt.Errorf("the number of ranges and payloads are not equal: ranges = %d, payloads = %d", len(ranges), len(payloads)))
|
|
||||||
}
|
|
||||||
|
|
||||||
newAttrs, err := parseNewObjectAttrs(cmd)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse new object attributes: %w", err)
|
|
||||||
replaceAttrs, _ := cmd.Flags().GetBool(replaceAttrsFlagName)
|
|
||||||
|
|
||||||
pk := key.GetOrGenerate(cmd)
|
|
||||||
|
|
||||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
|
||||||
|
|
||||||
var prm internalclient.PatchObjectPrm
|
|
||||||
prm.SetClient(cli)
|
|
||||||
Prepare(cmd, &prm)
|
|
||||||
ReadOrOpenSession(cmd, &prm, pk, cnr, nil)
|
|
||||||
|
|
||||||
prm.SetAddress(objAddr)
|
|
||||||
prm.NewAttributes = newAttrs
|
|
||||||
prm.ReplaceAttribute = replaceAttrs
|
|
||||||
|
|
||||||
for i := range ranges {
|
|
||||||
prm.PayloadPatches = append(prm.PayloadPatches, internalclient.PayloadPatch{
|
|
||||||
Range: ranges[i],
|
|
||||||
PayloadPath: payloads[i],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := internalclient.Patch(cmd.Context(), prm)
|
|
||||||
if err != nil {
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't patch the object: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Println("Patched object ID: ", res.OID.EncodeToString())
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseNewObjectAttrs(cmd *cobra.Command) ([]objectSDK.Attribute, error) {
|
|
||||||
var rawAttrs []string
|
|
||||||
|
|
||||||
raw := cmd.Flag(newAttrsFlagName).Value.String()
|
|
||||||
if len(raw) != 0 {
|
|
||||||
rawAttrs = strings.Split(raw, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs := make([]objectSDK.Attribute, len(rawAttrs), len(rawAttrs)+2) // name + timestamp attributes
|
|
||||||
for i := range rawAttrs {
|
|
||||||
k, v, found := strings.Cut(rawAttrs[i], "=")
|
|
||||||
if !found {
|
|
||||||
return nil, fmt.Errorf("invalid attribute format: %s", rawAttrs[i])
|
|
||||||
}
|
|
||||||
attrs[i].SetKey(k)
|
|
||||||
attrs[i].SetValue(v)
|
|
||||||
}
|
|
||||||
return attrs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRangeSlice(cmd *cobra.Command) ([]objectSDK.Range, error) {
|
|
||||||
v, _ := cmd.Flags().GetStringSlice(rangeFlagName)
|
|
||||||
if len(v) == 0 {
|
|
||||||
return []objectSDK.Range{}, nil
|
|
||||||
}
|
|
||||||
rs := make([]objectSDK.Range, len(v))
|
|
||||||
for i := range v {
|
|
||||||
before, after, found := strings.Cut(v[i], rangeSep)
|
|
||||||
if !found {
|
|
||||||
return nil, fmt.Errorf("invalid range specifier: %s", v[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
offset, err := strconv.ParseUint(before, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid '%s' range offset specifier: %w", v[i], err)
|
|
||||||
}
|
|
||||||
length, err := strconv.ParseUint(after, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid '%s' range length specifier: %w", v[i], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rs[i].SetOffset(offset)
|
|
||||||
rs[i].SetLength(length)
|
|
||||||
}
|
|
||||||
return rs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func patchPayloadPaths(cmd *cobra.Command) []string {
|
|
||||||
v, _ := cmd.Flags().GetStringSlice(payloadFlagName)
|
|
||||||
return v
|
|
||||||
}
|
|
|
@ -29,7 +29,6 @@ func init() {
|
||||||
objectRangeCmd,
|
objectRangeCmd,
|
||||||
objectLockCmd,
|
objectLockCmd,
|
||||||
objectNodesCmd,
|
objectNodesCmd,
|
||||||
objectPatchCmd,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cmd.AddCommand(objectChildCommands...)
|
Cmd.AddCommand(objectChildCommands...)
|
||||||
|
@ -40,7 +39,6 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
initObjectPutCmd()
|
initObjectPutCmd()
|
||||||
initObjectPatchCmd()
|
|
||||||
initObjectDeleteCmd()
|
initObjectDeleteCmd()
|
||||||
initObjectGetCmd()
|
initObjectGetCmd()
|
||||||
initObjectSearchCmd()
|
initObjectSearchCmd()
|
||||||
|
|
|
@ -306,8 +306,6 @@ func finalizeSession(cmd *cobra.Command, dst SessionPrm, tok *session.Object, ke
|
||||||
case *internal.PutObjectPrm:
|
case *internal.PutObjectPrm:
|
||||||
common.PrintVerbose(cmd, "Binding session to object PUT...")
|
common.PrintVerbose(cmd, "Binding session to object PUT...")
|
||||||
tok.ForVerb(session.VerbObjectPut)
|
tok.ForVerb(session.VerbObjectPut)
|
||||||
case *internal.PatchObjectPrm:
|
|
||||||
tok.ForVerb(session.VerbObjectPatch)
|
|
||||||
case *internal.DeleteObjectPrm:
|
case *internal.DeleteObjectPrm:
|
||||||
common.PrintVerbose(cmd, "Binding session to object DELETE...")
|
common.PrintVerbose(cmd, "Binding session to object DELETE...")
|
||||||
tok.ForVerb(session.VerbObjectDelete)
|
tok.ForVerb(session.VerbObjectDelete)
|
||||||
|
@ -356,7 +354,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
|
||||||
|
|
||||||
Prepare(cmd, &prmHead)
|
Prepare(cmd, &prmHead)
|
||||||
|
|
||||||
o, err := internal.HeadObject(cmd.Context(), prmHead)
|
_, err := internal.HeadObject(cmd.Context(), prmHead)
|
||||||
|
|
||||||
var errSplit *objectSDK.SplitInfoError
|
var errSplit *objectSDK.SplitInfoError
|
||||||
var errEC *objectSDK.ECInfoError
|
var errEC *objectSDK.ECInfoError
|
||||||
|
@ -366,15 +364,12 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
|
||||||
commonCmd.ExitOnErr(cmd, "failed to get raw object header: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to get raw object header: %w", err)
|
||||||
case err == nil:
|
case err == nil:
|
||||||
common.PrintVerbose(cmd, "Raw header received - object is singular.")
|
common.PrintVerbose(cmd, "Raw header received - object is singular.")
|
||||||
if ech := o.Header().ECHeader(); ech != nil {
|
|
||||||
commonCmd.ExitOnErr(cmd, "Lock EC chunk failed: %w", errors.ErrUnsupported)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
case errors.As(err, &errSplit):
|
case errors.As(err, &errSplit):
|
||||||
common.PrintVerbose(cmd, "Split information received - object is virtual.")
|
common.PrintVerbose(cmd, "Split information received - object is virtual.")
|
||||||
splitInfo := errSplit.SplitInfo()
|
splitInfo := errSplit.SplitInfo()
|
||||||
|
|
||||||
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnr); ok {
|
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnr, true); ok {
|
||||||
return members
|
return members
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +385,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryGetSplitMembersByLinkingObject(cmd *cobra.Command, splitInfo *objectSDK.SplitInfo, prmHead internal.HeadObjectPrm, cnr cid.ID) ([]oid.ID, bool) {
|
func tryGetSplitMembersByLinkingObject(cmd *cobra.Command, splitInfo *objectSDK.SplitInfo, prmHead internal.HeadObjectPrm, cnr cid.ID, withLinking bool) ([]oid.ID, bool) {
|
||||||
// collect split chain by the descending ease of operations (ease is evaluated heuristically).
|
// collect split chain by the descending ease of operations (ease is evaluated heuristically).
|
||||||
// If any approach fails, we don't try the next since we assume that it will fail too.
|
// If any approach fails, we don't try the next since we assume that it will fail too.
|
||||||
|
|
||||||
|
@ -411,7 +406,10 @@ func tryGetSplitMembersByLinkingObject(cmd *cobra.Command, splitInfo *objectSDK.
|
||||||
|
|
||||||
common.PrintVerbose(cmd, "Received split members from the linking object: %v", children)
|
common.PrintVerbose(cmd, "Received split members from the linking object: %v", children)
|
||||||
|
|
||||||
return append(children, idLinking), true
|
if withLinking {
|
||||||
|
return append(children, idLinking), true
|
||||||
|
}
|
||||||
|
return children, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// linking object is not required for
|
// linking object is not required for
|
||||||
|
|
|
@ -47,10 +47,9 @@ func add(cmd *cobra.Command, _ []string) {
|
||||||
meta, err := parseMeta(cmd)
|
meta, err := parseMeta(cmd)
|
||||||
commonCmd.ExitOnErr(cmd, "meta data parsing: %w", err)
|
commonCmd.ExitOnErr(cmd, "meta data parsing: %w", err)
|
||||||
|
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
@ -73,18 +72,18 @@ func add(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "signing message: %w", tree.SignMessage(req, pk))
|
commonCmd.ExitOnErr(cmd, "signing message: %w", tree.SignMessage(req, pk))
|
||||||
|
|
||||||
resp, err := cli.Add(ctx, req)
|
resp, err := cli.Add(ctx, req)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to call add: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to cal add: %w", err)
|
||||||
|
|
||||||
cmd.Println("Node ID: ", resp.GetBody().GetNodeId())
|
cmd.Println("Node ID: ", resp.GetBody().GetNodeId())
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMeta(cmd *cobra.Command) ([]tree.KeyValue, error) {
|
func parseMeta(cmd *cobra.Command) ([]*tree.KeyValue, error) {
|
||||||
raws, _ := cmd.Flags().GetStringSlice(metaFlagKey)
|
raws, _ := cmd.Flags().GetStringSlice(metaFlagKey)
|
||||||
if len(raws) == 0 {
|
if len(raws) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pairs := make([]tree.KeyValue, 0, len(raws))
|
pairs := make([]*tree.KeyValue, 0, len(raws))
|
||||||
for i := range raws {
|
for i := range raws {
|
||||||
k, v, found := strings.Cut(raws[i], "=")
|
k, v, found := strings.Cut(raws[i], "=")
|
||||||
if !found {
|
if !found {
|
||||||
|
@ -95,7 +94,7 @@ func parseMeta(cmd *cobra.Command) ([]tree.KeyValue, error) {
|
||||||
pair.Key = k
|
pair.Key = k
|
||||||
pair.Value = []byte(v)
|
pair.Value = []byte(v)
|
||||||
|
|
||||||
pairs = append(pairs, pair)
|
pairs = append(pairs, &pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pairs, nil
|
return pairs, nil
|
||||||
|
|
|
@ -50,10 +50,9 @@ func addByPath(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
||||||
|
|
||||||
tid, _ := cmd.Flags().GetString(treeIDFlagKey)
|
tid, _ := cmd.Flags().GetString(treeIDFlagKey)
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
|
|
@ -3,14 +3,13 @@ package tree
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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/pkg/network"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/tree"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/tree"
|
||||||
metrics "git.frostfs.info/TrueCloudLab/frostfs-observability/metrics/grpc"
|
metrics "git.frostfs.info/TrueCloudLab/frostfs-observability/metrics/grpc"
|
||||||
tracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
|
tracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
@ -18,7 +17,7 @@ import (
|
||||||
|
|
||||||
// _client returns grpc Tree service client. Should be removed
|
// _client returns grpc Tree service client. Should be removed
|
||||||
// after making Tree API public.
|
// after making Tree API public.
|
||||||
func _client() (tree.TreeServiceClient, error) {
|
func _client(ctx context.Context) (tree.TreeServiceClient, error) {
|
||||||
var netAddr network.Address
|
var netAddr network.Address
|
||||||
err := netAddr.FromString(viper.GetString(commonflags.RPC))
|
err := netAddr.FromString(viper.GetString(commonflags.RPC))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -26,6 +25,7 @@ func _client() (tree.TreeServiceClient, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := []grpc.DialOption{
|
opts := []grpc.DialOption{
|
||||||
|
grpc.WithBlock(),
|
||||||
grpc.WithChainUnaryInterceptor(
|
grpc.WithChainUnaryInterceptor(
|
||||||
metrics.NewUnaryClientInterceptor(),
|
metrics.NewUnaryClientInterceptor(),
|
||||||
tracing.NewUnaryClientInteceptor(),
|
tracing.NewUnaryClientInteceptor(),
|
||||||
|
@ -40,14 +40,12 @@ func _client() (tree.TreeServiceClient, error) {
|
||||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, err := grpc.NewClient(netAddr.URIAddr(), opts...)
|
// a default connection establishing timeout
|
||||||
|
const defaultClientConnectTimeout = time.Second * 2
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, defaultClientConnectTimeout)
|
||||||
|
cc, err := grpc.DialContext(ctx, netAddr.URIAddr(), opts...)
|
||||||
|
cancel()
|
||||||
|
|
||||||
return tree.NewTreeServiceClient(cc), err
|
return tree.NewTreeServiceClient(cc), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextWithTimeout(cmd *cobra.Command) (context.Context, context.CancelFunc) {
|
|
||||||
if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 {
|
|
||||||
common.PrintVerbose(cmd, "Set request timeout to %s.", timeout)
|
|
||||||
return context.WithTimeout(cmd.Context(), timeout)
|
|
||||||
}
|
|
||||||
return context.WithTimeout(cmd.Context(), commonflags.TimeoutDefault)
|
|
||||||
}
|
|
||||||
|
|
|
@ -50,10 +50,9 @@ func getByPath(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
||||||
|
|
||||||
tid, _ := cmd.Flags().GetString(treeIDFlagKey)
|
tid, _ := cmd.Flags().GetString(treeIDFlagKey)
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
|
|
@ -44,10 +44,9 @@ func getOpLog(cmd *cobra.Command, _ []string) {
|
||||||
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
||||||
|
|
||||||
tid, _ := cmd.Flags().GetString(treeIDFlagKey)
|
tid, _ := cmd.Flags().GetString(treeIDFlagKey)
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
|
|
@ -26,10 +26,9 @@ func initHealthcheckCmd() {
|
||||||
|
|
||||||
func healthcheck(cmd *cobra.Command, _ []string) {
|
func healthcheck(cmd *cobra.Command, _ []string) {
|
||||||
pk := key.GetOrGenerate(cmd)
|
pk := key.GetOrGenerate(cmd)
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
req := &tree.HealthcheckRequest{
|
req := &tree.HealthcheckRequest{
|
||||||
|
|
|
@ -38,10 +38,9 @@ func list(cmd *cobra.Command, _ []string) {
|
||||||
err := cnr.DecodeString(cidString)
|
err := cnr.DecodeString(cidString)
|
||||||
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
||||||
|
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
|
|
@ -45,10 +45,9 @@ func move(cmd *cobra.Command, _ []string) {
|
||||||
err := cnr.DecodeString(cidString)
|
err := cnr.DecodeString(cidString)
|
||||||
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
||||||
|
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
@ -67,7 +66,7 @@ func move(cmd *cobra.Command, _ []string) {
|
||||||
Body: &tree.GetSubTreeRequest_Body{
|
Body: &tree.GetSubTreeRequest_Body{
|
||||||
ContainerId: rawCID,
|
ContainerId: rawCID,
|
||||||
TreeId: tid,
|
TreeId: tid,
|
||||||
RootId: []uint64{nid},
|
RootId: nid,
|
||||||
Depth: 1,
|
Depth: 1,
|
||||||
BearerToken: bt,
|
BearerToken: bt,
|
||||||
},
|
},
|
||||||
|
@ -76,7 +75,7 @@ func move(cmd *cobra.Command, _ []string) {
|
||||||
resp, err := cli.GetSubTree(ctx, subTreeReq)
|
resp, err := cli.GetSubTree(ctx, subTreeReq)
|
||||||
commonCmd.ExitOnErr(cmd, "rpc call: %w", err)
|
commonCmd.ExitOnErr(cmd, "rpc call: %w", err)
|
||||||
|
|
||||||
var meta []tree.KeyValue
|
var meta []*tree.KeyValue
|
||||||
subtreeResp, err := resp.Recv()
|
subtreeResp, err := resp.Recv()
|
||||||
for ; err == nil; subtreeResp, err = resp.Recv() {
|
for ; err == nil; subtreeResp, err = resp.Recv() {
|
||||||
meta = subtreeResp.GetBody().GetMeta()
|
meta = subtreeResp.GetBody().GetMeta()
|
||||||
|
|
|
@ -41,10 +41,9 @@ func remove(cmd *cobra.Command, _ []string) {
|
||||||
err := cnr.DecodeString(cidString)
|
err := cnr.DecodeString(cidString)
|
||||||
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
||||||
|
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
|
|
@ -49,7 +49,6 @@ const (
|
||||||
heightFlagKey = "height"
|
heightFlagKey = "height"
|
||||||
countFlagKey = "count"
|
countFlagKey = "count"
|
||||||
depthFlagKey = "depth"
|
depthFlagKey = "depth"
|
||||||
orderFlagKey = "ordered"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func initCTID(cmd *cobra.Command) {
|
func initCTID(cmd *cobra.Command) {
|
||||||
|
|
|
@ -30,7 +30,6 @@ func initGetSubtreeCmd() {
|
||||||
ff := getSubtreeCmd.Flags()
|
ff := getSubtreeCmd.Flags()
|
||||||
ff.Uint64(rootIDFlagKey, 0, "Root ID to traverse from.")
|
ff.Uint64(rootIDFlagKey, 0, "Root ID to traverse from.")
|
||||||
ff.Uint32(depthFlagKey, 10, "Traversal depth.")
|
ff.Uint32(depthFlagKey, 10, "Traversal depth.")
|
||||||
ff.Bool(orderFlagKey, false, "Sort output by ascending FileName.")
|
|
||||||
|
|
||||||
_ = getSubtreeCmd.MarkFlagRequired(commonflags.CIDFlag)
|
_ = getSubtreeCmd.MarkFlagRequired(commonflags.CIDFlag)
|
||||||
_ = getSubtreeCmd.MarkFlagRequired(treeIDFlagKey)
|
_ = getSubtreeCmd.MarkFlagRequired(treeIDFlagKey)
|
||||||
|
@ -46,10 +45,9 @@ func getSubTree(cmd *cobra.Command, _ []string) {
|
||||||
err := cnr.DecodeString(cidString)
|
err := cnr.DecodeString(cidString)
|
||||||
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)
|
||||||
|
|
||||||
ctx, cancel := contextWithTimeout(cmd)
|
ctx := cmd.Context()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cli, err := _client()
|
cli, err := _client(ctx)
|
||||||
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)
|
||||||
|
|
||||||
rawCID := make([]byte, sha256.Size)
|
rawCID := make([]byte, sha256.Size)
|
||||||
|
@ -61,13 +59,6 @@ func getSubTree(cmd *cobra.Command, _ []string) {
|
||||||
|
|
||||||
depth, _ := cmd.Flags().GetUint32(depthFlagKey)
|
depth, _ := cmd.Flags().GetUint32(depthFlagKey)
|
||||||
|
|
||||||
order, _ := cmd.Flags().GetBool(orderFlagKey)
|
|
||||||
|
|
||||||
bodyOrder := tree.GetSubTreeRequest_Body_Order_None
|
|
||||||
if order {
|
|
||||||
bodyOrder = tree.GetSubTreeRequest_Body_Order_Asc
|
|
||||||
}
|
|
||||||
|
|
||||||
var bt []byte
|
var bt []byte
|
||||||
if t := common.ReadBearerToken(cmd, bearerFlagKey); t != nil {
|
if t := common.ReadBearerToken(cmd, bearerFlagKey); t != nil {
|
||||||
bt = t.Marshal()
|
bt = t.Marshal()
|
||||||
|
@ -77,12 +68,9 @@ func getSubTree(cmd *cobra.Command, _ []string) {
|
||||||
Body: &tree.GetSubTreeRequest_Body{
|
Body: &tree.GetSubTreeRequest_Body{
|
||||||
ContainerId: rawCID,
|
ContainerId: rawCID,
|
||||||
TreeId: tid,
|
TreeId: tid,
|
||||||
RootId: []uint64{rid},
|
RootId: rid,
|
||||||
Depth: depth,
|
Depth: depth,
|
||||||
BearerToken: bt,
|
BearerToken: bt,
|
||||||
OrderBy: &tree.GetSubTreeRequest_Body_Order{
|
|
||||||
Direction: bodyOrder,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,15 +83,10 @@ func getSubTree(cmd *cobra.Command, _ []string) {
|
||||||
for ; err == nil; subtreeResp, err = resp.Recv() {
|
for ; err == nil; subtreeResp, err = resp.Recv() {
|
||||||
b := subtreeResp.GetBody()
|
b := subtreeResp.GetBody()
|
||||||
|
|
||||||
if len(b.GetNodeId()) == 1 {
|
cmd.Printf("Node ID: %d\n", b.GetNodeId())
|
||||||
cmd.Printf("Node ID: %d\n", b.GetNodeId())
|
|
||||||
cmd.Println("\tParent ID: ", b.GetParentId())
|
cmd.Println("\tParent ID: ", b.GetParentId())
|
||||||
cmd.Println("\tTimestamp: ", b.GetTimestamp())
|
cmd.Println("\tTimestamp: ", b.GetTimestamp())
|
||||||
} else {
|
|
||||||
cmd.Printf("Node IDs: %v\n", b.GetNodeId())
|
|
||||||
cmd.Println("\tParent IDs: ", b.GetParentId())
|
|
||||||
cmd.Println("\tTimestamps: ", b.GetTimestamp())
|
|
||||||
}
|
|
||||||
|
|
||||||
if meta := b.GetMeta(); len(meta) > 0 {
|
if meta := b.GetMeta(); len(meta) > 0 {
|
||||||
cmd.Println("\tMeta pairs: ")
|
cmd.Println("\tMeta pairs: ")
|
||||||
|
|
|
@ -33,7 +33,7 @@ func PrettyPrintTableBACL(cmd *cobra.Command, bacl *acl.Basic) {
|
||||||
fmt.Fprintln(w, strings.Join(bits, "\t"))
|
fmt.Fprintln(w, strings.Join(bits, "\t"))
|
||||||
// Footer
|
// Footer
|
||||||
footer := []string{"X F"}
|
footer := []string{"X F"}
|
||||||
for range 7 {
|
for i := 0; i < 7; i++ {
|
||||||
footer = append(footer, "U S O B")
|
footer = append(footer, "U S O B")
|
||||||
}
|
}
|
||||||
fmt.Fprintln(w, strings.Join(footer, "\t"))
|
fmt.Fprintln(w, strings.Join(footer, "\t"))
|
||||||
|
|
|
@ -239,8 +239,6 @@ func parseAction(lexeme string) ([]string, bool, error) {
|
||||||
return []string{nativeschema.MethodRangeObject}, true, nil
|
return []string{nativeschema.MethodRangeObject}, true, nil
|
||||||
case "object.hash":
|
case "object.hash":
|
||||||
return []string{nativeschema.MethodHashObject}, true, nil
|
return []string{nativeschema.MethodHashObject}, true, nil
|
||||||
case "object.patch":
|
|
||||||
return []string{nativeschema.MethodPatchObject}, true, nil
|
|
||||||
case "object.*":
|
case "object.*":
|
||||||
return []string{
|
return []string{
|
||||||
nativeschema.MethodPutObject,
|
nativeschema.MethodPutObject,
|
||||||
|
@ -248,9 +246,7 @@ func parseAction(lexeme string) ([]string, bool, error) {
|
||||||
nativeschema.MethodHeadObject,
|
nativeschema.MethodHeadObject,
|
||||||
nativeschema.MethodDeleteObject,
|
nativeschema.MethodDeleteObject,
|
||||||
nativeschema.MethodSearchObject,
|
nativeschema.MethodSearchObject,
|
||||||
nativeschema.MethodRangeObject,
|
|
||||||
nativeschema.MethodHashObject,
|
nativeschema.MethodHashObject,
|
||||||
nativeschema.MethodPatchObject,
|
|
||||||
}, true, nil
|
}, true, nil
|
||||||
case "container.put":
|
case "container.put":
|
||||||
return []string{nativeschema.MethodPutContainer}, false, nil
|
return []string{nativeschema.MethodPutContainer}, false, nil
|
||||||
|
@ -258,6 +254,10 @@ func parseAction(lexeme string) ([]string, bool, error) {
|
||||||
return []string{nativeschema.MethodDeleteContainer}, false, nil
|
return []string{nativeschema.MethodDeleteContainer}, false, nil
|
||||||
case "container.get":
|
case "container.get":
|
||||||
return []string{nativeschema.MethodGetContainer}, false, nil
|
return []string{nativeschema.MethodGetContainer}, false, nil
|
||||||
|
case "container.setcontainereacl":
|
||||||
|
return []string{nativeschema.MethodSetContainerEACL}, false, nil
|
||||||
|
case "container.getcontainereacl":
|
||||||
|
return []string{nativeschema.MethodGetContainerEACL}, false, nil
|
||||||
case "container.list":
|
case "container.list":
|
||||||
return []string{nativeschema.MethodListContainers}, false, nil
|
return []string{nativeschema.MethodListContainers}, false, nil
|
||||||
case "container.*":
|
case "container.*":
|
||||||
|
@ -265,6 +265,8 @@ func parseAction(lexeme string) ([]string, bool, error) {
|
||||||
nativeschema.MethodPutContainer,
|
nativeschema.MethodPutContainer,
|
||||||
nativeschema.MethodDeleteContainer,
|
nativeschema.MethodDeleteContainer,
|
||||||
nativeschema.MethodGetContainer,
|
nativeschema.MethodGetContainer,
|
||||||
|
nativeschema.MethodSetContainerEACL,
|
||||||
|
nativeschema.MethodGetContainerEACL,
|
||||||
nativeschema.MethodListContainers,
|
nativeschema.MethodListContainers,
|
||||||
}, false, nil
|
}, false, nil
|
||||||
default:
|
default:
|
||||||
|
@ -277,7 +279,7 @@ func parseResource(lexeme string, isObj bool) (string, error) {
|
||||||
if isObj {
|
if isObj {
|
||||||
if lexeme == "*" {
|
if lexeme == "*" {
|
||||||
return nativeschema.ResourceFormatAllObjects, nil
|
return nativeschema.ResourceFormatAllObjects, nil
|
||||||
} else if lexeme == "/*" || lexeme == "root/*" {
|
} else if lexeme == "/*" {
|
||||||
return nativeschema.ResourceFormatRootObjects, nil
|
return nativeschema.ResourceFormatRootObjects, nil
|
||||||
} else if strings.HasPrefix(lexeme, "/") {
|
} else if strings.HasPrefix(lexeme, "/") {
|
||||||
lexeme = lexeme[1:]
|
lexeme = lexeme[1:]
|
||||||
|
|
|
@ -26,7 +26,7 @@ func TestParseAPERule(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Valid rule for all objects in implicit root namespace",
|
name: "Valid rule for all objects in root namespace",
|
||||||
rule: "allow Object.Put /*",
|
rule: "allow Object.Put /*",
|
||||||
expectRule: policyengine.Rule{
|
expectRule: policyengine.Rule{
|
||||||
Status: policyengine.Allow,
|
Status: policyengine.Allow,
|
||||||
|
@ -34,15 +34,6 @@ func TestParseAPERule(t *testing.T) {
|
||||||
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
|
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Valid rule for all objects in explicit root namespace",
|
|
||||||
rule: "allow Object.Put root/*",
|
|
||||||
expectRule: policyengine.Rule{
|
|
||||||
Status: policyengine.Allow,
|
|
||||||
Actions: policyengine.Actions{Names: []string{nativeschema.MethodPutObject}},
|
|
||||||
Resources: policyengine.Resources{Names: []string{nativeschema.ResourceFormatRootObjects}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "Valid rule for all objects in root namespace and container",
|
name: "Valid rule for all objects in root namespace and container",
|
||||||
rule: "allow Object.Put /cid/*",
|
rule: "allow Object.Put /cid/*",
|
||||||
|
|
|
@ -6,17 +6,9 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
|
"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"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
apeutil "git.frostfs.info/TrueCloudLab/frostfs-node/internal/ape"
|
|
||||||
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
fromFlagStr = "from"
|
|
||||||
toFlagStr = "to"
|
|
||||||
apeFlagStr = "ape"
|
|
||||||
)
|
|
||||||
|
|
||||||
var convertEACLCmd = &cobra.Command{
|
var convertEACLCmd = &cobra.Command{
|
||||||
Use: "eacl",
|
Use: "eacl",
|
||||||
Short: "Convert representation of extended ACL table",
|
Short: "Convert representation of extended ACL table",
|
||||||
|
@ -26,35 +18,24 @@ var convertEACLCmd = &cobra.Command{
|
||||||
func initConvertEACLCmd() {
|
func initConvertEACLCmd() {
|
||||||
flags := convertEACLCmd.Flags()
|
flags := convertEACLCmd.Flags()
|
||||||
|
|
||||||
flags.String(fromFlagStr, "", "File with JSON or binary encoded extended ACL table")
|
flags.String("from", "", "File with JSON or binary encoded extended ACL table")
|
||||||
_ = convertEACLCmd.MarkFlagFilename(fromFlagStr)
|
_ = convertEACLCmd.MarkFlagFilename("from")
|
||||||
_ = convertEACLCmd.MarkFlagRequired(fromFlagStr)
|
_ = convertEACLCmd.MarkFlagRequired("from")
|
||||||
|
|
||||||
flags.String(toFlagStr, "", "File to dump extended ACL table (default: binary encoded)")
|
flags.String("to", "", "File to dump extended ACL table (default: binary encoded)")
|
||||||
flags.Bool(commonflags.JSON, false, "Dump extended ACL table in JSON encoding")
|
flags.Bool(commonflags.JSON, false, "Dump extended ACL table in JSON encoding")
|
||||||
|
|
||||||
flags.Bool(apeFlagStr, false, "Dump converted eACL table to APE chain format")
|
|
||||||
|
|
||||||
convertEACLCmd.MarkFlagsMutuallyExclusive(apeFlagStr, commonflags.JSON)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertEACLTable(cmd *cobra.Command, _ []string) {
|
func convertEACLTable(cmd *cobra.Command, _ []string) {
|
||||||
pathFrom := cmd.Flag(fromFlagStr).Value.String()
|
pathFrom := cmd.Flag("from").Value.String()
|
||||||
to := cmd.Flag(toFlagStr).Value.String()
|
to := cmd.Flag("to").Value.String()
|
||||||
jsonFlag, _ := cmd.Flags().GetBool(commonflags.JSON)
|
jsonFlag, _ := cmd.Flags().GetBool(commonflags.JSON)
|
||||||
apeFlag, _ := cmd.Flags().GetBool(apeFlagStr)
|
|
||||||
|
|
||||||
table := common.ReadEACL(cmd, pathFrom)
|
table := common.ReadEACL(cmd, pathFrom)
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
var err error
|
var err error
|
||||||
|
if jsonFlag || len(to) == 0 {
|
||||||
if apeFlag {
|
|
||||||
var ch *chain.Chain
|
|
||||||
ch, err = apeutil.ConvertEACLToAPE(table)
|
|
||||||
commonCmd.ExitOnErr(cmd, "convert eACL table to APE chain error: %w", err)
|
|
||||||
data = ch.Bytes()
|
|
||||||
} else if jsonFlag || len(to) == 0 {
|
|
||||||
data, err = table.MarshalJSON()
|
data, err = table.MarshalJSON()
|
||||||
commonCmd.ExitOnErr(cmd, "can't JSON encode extended ACL table: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't JSON encode extended ACL table: %w", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
18
cmd/frostfs-cli/modules/util/locode.go
Normal file
18
cmd/frostfs-cli/modules/util/locode.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// locode section.
|
||||||
|
var locodeCmd = &cobra.Command{
|
||||||
|
Use: "locode",
|
||||||
|
Short: "Working with FrostFS UN/LOCODE database",
|
||||||
|
}
|
||||||
|
|
||||||
|
func initLocodeCmd() {
|
||||||
|
locodeCmd.AddCommand(locodeGenerateCmd, locodeInfoCmd)
|
||||||
|
|
||||||
|
initUtilLocodeInfoCmd()
|
||||||
|
initUtilLocodeGenerateCmd()
|
||||||
|
}
|
96
cmd/frostfs-cli/modules/util/locode_generate.go
Normal file
96
cmd/frostfs-cli/modules/util/locode_generate.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
locodedb "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/locode/db"
|
||||||
|
airportsdb "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/locode/db/airports"
|
||||||
|
locodebolt "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/locode/db/boltdb"
|
||||||
|
continentsdb "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/locode/db/continents/geojson"
|
||||||
|
csvlocode "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/locode/table/csv"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type namesDB struct {
|
||||||
|
*airportsdb.DB
|
||||||
|
*csvlocode.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
locodeGenerateInputFlag = "in"
|
||||||
|
locodeGenerateSubDivFlag = "subdiv"
|
||||||
|
locodeGenerateAirportsFlag = "airports"
|
||||||
|
locodeGenerateCountriesFlag = "countries"
|
||||||
|
locodeGenerateContinentsFlag = "continents"
|
||||||
|
locodeGenerateOutputFlag = "out"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
locodeGenerateInPaths []string
|
||||||
|
locodeGenerateSubDivPath string
|
||||||
|
locodeGenerateAirportsPath string
|
||||||
|
locodeGenerateCountriesPath string
|
||||||
|
locodeGenerateContinentsPath string
|
||||||
|
locodeGenerateOutPath string
|
||||||
|
|
||||||
|
locodeGenerateCmd = &cobra.Command{
|
||||||
|
Use: "generate",
|
||||||
|
Short: "Generate UN/LOCODE database for FrostFS",
|
||||||
|
Run: func(cmd *cobra.Command, _ []string) {
|
||||||
|
locodeDB := csvlocode.New(
|
||||||
|
csvlocode.Prm{
|
||||||
|
Path: locodeGenerateInPaths[0],
|
||||||
|
SubDivPath: locodeGenerateSubDivPath,
|
||||||
|
},
|
||||||
|
csvlocode.WithExtraPaths(locodeGenerateInPaths[1:]...),
|
||||||
|
)
|
||||||
|
|
||||||
|
airportDB := airportsdb.New(airportsdb.Prm{
|
||||||
|
AirportsPath: locodeGenerateAirportsPath,
|
||||||
|
CountriesPath: locodeGenerateCountriesPath,
|
||||||
|
})
|
||||||
|
|
||||||
|
continentsDB := continentsdb.New(continentsdb.Prm{
|
||||||
|
Path: locodeGenerateContinentsPath,
|
||||||
|
})
|
||||||
|
|
||||||
|
targetDB := locodebolt.New(locodebolt.Prm{
|
||||||
|
Path: locodeGenerateOutPath,
|
||||||
|
})
|
||||||
|
|
||||||
|
err := targetDB.Open()
|
||||||
|
commonCmd.ExitOnErr(cmd, "", err)
|
||||||
|
|
||||||
|
defer targetDB.Close()
|
||||||
|
|
||||||
|
names := &namesDB{
|
||||||
|
DB: airportDB,
|
||||||
|
Table: locodeDB,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = locodedb.FillDatabase(locodeDB, airportDB, continentsDB, names, targetDB)
|
||||||
|
commonCmd.ExitOnErr(cmd, "", err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initUtilLocodeGenerateCmd() {
|
||||||
|
flags := locodeGenerateCmd.Flags()
|
||||||
|
|
||||||
|
flags.StringSliceVar(&locodeGenerateInPaths, locodeGenerateInputFlag, nil, "List of paths to UN/LOCODE tables (csv)")
|
||||||
|
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateInputFlag)
|
||||||
|
|
||||||
|
flags.StringVar(&locodeGenerateSubDivPath, locodeGenerateSubDivFlag, "", "Path to UN/LOCODE subdivision database (csv)")
|
||||||
|
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateSubDivFlag)
|
||||||
|
|
||||||
|
flags.StringVar(&locodeGenerateAirportsPath, locodeGenerateAirportsFlag, "", "Path to OpenFlights airport database (csv)")
|
||||||
|
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateAirportsFlag)
|
||||||
|
|
||||||
|
flags.StringVar(&locodeGenerateCountriesPath, locodeGenerateCountriesFlag, "", "Path to OpenFlights country database (csv)")
|
||||||
|
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateCountriesFlag)
|
||||||
|
|
||||||
|
flags.StringVar(&locodeGenerateContinentsPath, locodeGenerateContinentsFlag, "", "Path to continent polygons (GeoJSON)")
|
||||||
|
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateContinentsFlag)
|
||||||
|
|
||||||
|
flags.StringVar(&locodeGenerateOutPath, locodeGenerateOutputFlag, "", "Target path for generated database")
|
||||||
|
_ = locodeGenerateCmd.MarkFlagRequired(locodeGenerateOutputFlag)
|
||||||
|
}
|
56
cmd/frostfs-cli/modules/util/locode_info.go
Normal file
56
cmd/frostfs-cli/modules/util/locode_info.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
locodedb "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/locode/db"
|
||||||
|
locodebolt "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/locode/db/boltdb"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
locodeInfoDBFlag = "db"
|
||||||
|
locodeInfoCodeFlag = "locode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
locodeInfoDBPath string
|
||||||
|
locodeInfoCode string
|
||||||
|
|
||||||
|
locodeInfoCmd = &cobra.Command{
|
||||||
|
Use: "info",
|
||||||
|
Short: "Print information about UN/LOCODE from FrostFS database",
|
||||||
|
Run: func(cmd *cobra.Command, _ []string) {
|
||||||
|
targetDB := locodebolt.New(locodebolt.Prm{
|
||||||
|
Path: locodeInfoDBPath,
|
||||||
|
}, locodebolt.ReadOnly())
|
||||||
|
|
||||||
|
err := targetDB.Open()
|
||||||
|
commonCmd.ExitOnErr(cmd, "", err)
|
||||||
|
|
||||||
|
defer targetDB.Close()
|
||||||
|
|
||||||
|
record, err := locodedb.LocodeRecord(targetDB, locodeInfoCode)
|
||||||
|
commonCmd.ExitOnErr(cmd, "", err)
|
||||||
|
|
||||||
|
cmd.Printf("Country: %s\n", record.CountryName())
|
||||||
|
cmd.Printf("Location: %s\n", record.LocationName())
|
||||||
|
cmd.Printf("Continent: %s\n", record.Continent())
|
||||||
|
if subDivCode := record.SubDivCode(); subDivCode != "" {
|
||||||
|
cmd.Printf("Subdivision: [%s] %s\n", subDivCode, record.SubDivName())
|
||||||
|
}
|
||||||
|
|
||||||
|
geoPoint := record.GeoPoint()
|
||||||
|
cmd.Printf("Coordinates: %0.2f, %0.2f\n", geoPoint.Latitude(), geoPoint.Longitude())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initUtilLocodeInfoCmd() {
|
||||||
|
flags := locodeInfoCmd.Flags()
|
||||||
|
|
||||||
|
flags.StringVar(&locodeInfoDBPath, locodeInfoDBFlag, "", "Path to FrostFS UN/LOCODE database")
|
||||||
|
_ = locodeInfoCmd.MarkFlagRequired(locodeInfoDBFlag)
|
||||||
|
|
||||||
|
flags.StringVar(&locodeInfoCode, locodeInfoCodeFlag, "", "UN/LOCODE")
|
||||||
|
_ = locodeInfoCmd.MarkFlagRequired(locodeInfoCodeFlag)
|
||||||
|
}
|
|
@ -23,9 +23,11 @@ func init() {
|
||||||
signCmd,
|
signCmd,
|
||||||
convertCmd,
|
convertCmd,
|
||||||
keyerCmd,
|
keyerCmd,
|
||||||
|
locodeCmd,
|
||||||
)
|
)
|
||||||
|
|
||||||
initSignCmd()
|
initSignCmd()
|
||||||
initConvertCmd()
|
initConvertCmd()
|
||||||
initKeyerCmd()
|
initKeyerCmd()
|
||||||
|
initLocodeCmd()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -36,58 +35,28 @@ func reloadConfig() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmode.Store(cfg.GetBool("node.kludge_compatibility_mode"))
|
cmode.Store(cfg.GetBool("node.kludge_compatibility_mode"))
|
||||||
audit.Store(cfg.GetBool("audit.enabled"))
|
|
||||||
err = logPrm.SetLevelString(cfg.GetString("logger.level"))
|
err = logPrm.SetLevelString(cfg.GetString("logger.level"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logPrm.PrependTimestamp = cfg.GetBool("logger.timestamp")
|
|
||||||
|
|
||||||
return logPrm.Reload()
|
return logPrm.Reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
func watchForSignal(cancel func()) {
|
func watchForSignal(cancel func()) {
|
||||||
ch := make(chan os.Signal, 1)
|
ch := make(chan os.Signal, 1)
|
||||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
sighupCh := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sighupCh, syscall.SIGHUP)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
// signals causing application to shut down should have priority over
|
case err := <-intErr:
|
||||||
// reconfiguration signal
|
|
||||||
case <-ch:
|
|
||||||
log.Info(logs.FrostFSNodeTerminationSignalHasBeenReceivedStopping)
|
|
||||||
cancel()
|
|
||||||
shutdown()
|
|
||||||
log.Info(logs.FrostFSNodeTerminationSignalProcessingIsComplete)
|
|
||||||
return
|
|
||||||
case err := <-intErr: // internal application error
|
|
||||||
log.Info(logs.FrostFSIRInternalError, zap.String("msg", err.Error()))
|
log.Info(logs.FrostFSIRInternalError, zap.String("msg", err.Error()))
|
||||||
cancel()
|
cancel()
|
||||||
shutdown()
|
shutdown()
|
||||||
return
|
return
|
||||||
default:
|
case sig := <-ch:
|
||||||
// block until any signal is receieved
|
switch sig {
|
||||||
select {
|
case syscall.SIGHUP:
|
||||||
case <-ch:
|
|
||||||
log.Info(logs.FrostFSNodeTerminationSignalHasBeenReceivedStopping)
|
|
||||||
cancel()
|
|
||||||
shutdown()
|
|
||||||
log.Info(logs.FrostFSNodeTerminationSignalProcessingIsComplete)
|
|
||||||
return
|
|
||||||
case err := <-intErr: // internal application error
|
|
||||||
log.Info(logs.FrostFSIRInternalError, zap.String("msg", err.Error()))
|
|
||||||
cancel()
|
|
||||||
shutdown()
|
|
||||||
return
|
|
||||||
case <-sighupCh:
|
|
||||||
log.Info(logs.FrostFSNodeSIGHUPHasBeenReceivedRereadingConfiguration)
|
log.Info(logs.FrostFSNodeSIGHUPHasBeenReceivedRereadingConfiguration)
|
||||||
if !innerRing.CompareAndSwapHealthStatus(control.HealthStatus_READY, control.HealthStatus_RECONFIGURING) {
|
|
||||||
log.Info(logs.FrostFSNodeSIGHUPSkip)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err := reloadConfig()
|
err := reloadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(logs.FrostFSNodeConfigurationReading, zap.Error(err))
|
log.Error(logs.FrostFSNodeConfigurationReading, zap.Error(err))
|
||||||
|
@ -99,8 +68,13 @@ func watchForSignal(cancel func()) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(logs.FrostFSNodeConfigurationReading, zap.Error(err))
|
log.Error(logs.FrostFSNodeConfigurationReading, zap.Error(err))
|
||||||
}
|
}
|
||||||
innerRing.CompareAndSwapHealthStatus(control.HealthStatus_RECONFIGURING, control.HealthStatus_READY)
|
|
||||||
log.Info(logs.FrostFSNodeConfigurationHasBeenReloadedSuccessfully)
|
log.Info(logs.FrostFSNodeConfigurationHasBeenReloadedSuccessfully)
|
||||||
|
case syscall.SIGTERM, syscall.SIGINT:
|
||||||
|
log.Info(logs.FrostFSNodeTerminationSignalHasBeenReceivedStopping)
|
||||||
|
cancel()
|
||||||
|
shutdown()
|
||||||
|
log.Info(logs.FrostFSNodeTerminationSignalProcessingIsComplete)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
func defaultConfiguration(cfg *viper.Viper) {
|
func defaultConfiguration(cfg *viper.Viper) {
|
||||||
cfg.SetDefault("logger.level", "info")
|
cfg.SetDefault("logger.level", "info")
|
||||||
cfg.SetDefault("logger.destination", "stdout")
|
cfg.SetDefault("logger.destination", "stdout")
|
||||||
cfg.SetDefault("logger.timestamp", false)
|
|
||||||
|
|
||||||
setPprofDefaults(cfg)
|
setPprofDefaults(cfg)
|
||||||
|
|
||||||
|
@ -46,8 +45,6 @@ func defaultConfiguration(cfg *viper.Viper) {
|
||||||
cfg.SetDefault("governance.disable", false)
|
cfg.SetDefault("governance.disable", false)
|
||||||
|
|
||||||
cfg.SetDefault("node.kludge_compatibility_mode", false)
|
cfg.SetDefault("node.kludge_compatibility_mode", false)
|
||||||
|
|
||||||
cfg.SetDefault("audit.enabled", false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setControlDefaults(cfg *viper.Viper) {
|
func setControlDefaults(cfg *viper.Viper) {
|
||||||
|
|
|
@ -9,11 +9,10 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
irMetrics "git.frostfs.info/TrueCloudLab/frostfs-node/internal/metrics"
|
|
||||||
"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"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sdnotify"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -40,7 +39,6 @@ var (
|
||||||
configFile *string
|
configFile *string
|
||||||
configDir *string
|
configDir *string
|
||||||
cmode = &atomic.Bool{}
|
cmode = &atomic.Bool{}
|
||||||
audit = &atomic.Bool{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func exitErr(err error) {
|
func exitErr(err error) {
|
||||||
|
@ -79,8 +77,6 @@ func main() {
|
||||||
)
|
)
|
||||||
exitErr(err)
|
exitErr(err)
|
||||||
logPrm.SamplingHook = metrics.LogMetrics().GetSamplingHook()
|
logPrm.SamplingHook = metrics.LogMetrics().GetSamplingHook()
|
||||||
logPrm.PrependTimestamp = cfg.GetBool("logger.timestamp")
|
|
||||||
|
|
||||||
log, err = logger.NewLogger(logPrm)
|
log, err = logger.NewLogger(logPrm)
|
||||||
exitErr(err)
|
exitErr(err)
|
||||||
|
|
||||||
|
@ -91,9 +87,8 @@ func main() {
|
||||||
|
|
||||||
metricsCmp = newMetricsComponent()
|
metricsCmp = newMetricsComponent()
|
||||||
metricsCmp.init()
|
metricsCmp.init()
|
||||||
audit.Store(cfg.GetBool("audit.enabled"))
|
|
||||||
|
|
||||||
innerRing, err = innerring.New(ctx, log, cfg, intErr, metrics, cmode, audit)
|
innerRing, err = innerring.New(ctx, log, cfg, intErr, metrics, cmode)
|
||||||
exitErr(err)
|
exitErr(err)
|
||||||
|
|
||||||
pprofCmp.start()
|
pprofCmp.start()
|
||||||
|
@ -127,8 +122,4 @@ func shutdown() {
|
||||||
zap.String("error", err.Error()),
|
zap.String("error", err.Error()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sdnotify.ClearStatus(); err != nil {
|
|
||||||
log.Error(logs.FailedToReportStatusToSystemd, zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue