forked from TrueCloudLab/frostfs-node
Compare commits
365 commits
fix/blobst
...
master
Author | SHA1 | Date | |
---|---|---|---|
d82f0d1926 | |||
acd5babd86 | |||
b65874d1c3 | |||
69c63006da | |||
d77a218f7c | |||
44df67492f | |||
1e6f132b4e | |||
6dc0dc6691 | |||
f7cb6b4d87 | |||
7fc6101bec | |||
7ef36749d0 | |||
c6066d6ee4 | |||
612b34d570 | |||
7429553266 | |||
6921a89061 | |||
16598553d9 | |||
c139892117 | |||
62b5181618 | |||
6db46257c0 | |||
c16dae8b4d | |||
fd004add00 | |||
8ed7a676d5 | |||
b451de94c8 | |||
f1556e3c42 | |||
e122ff6013 | |||
e2658c7519 | |||
c00f4bab18 | |||
46fef276b4 | |||
9bd05e94c8 | |||
16830033f8 | |||
1cf51a8079 | |||
3324c26fd8 | |||
a692298533 | |||
be2753de00 | |||
b543569c3f | |||
80f8a8fd3a | |||
2f3bc6eb84 | |||
8a57c78f5f | |||
ad01fb958a | |||
d336f2d487 | |||
c82c753e9f | |||
f666898e5d | |||
b1a31281e4 | |||
764450d04a | |||
755cae3f19 | |||
9b13a18aac | |||
ef64930fef | |||
c8fb154151 | |||
7edec9193c | |||
3cf6ea745d | |||
9a77527f46 | |||
5b1ba8e23d | |||
9902965ff4 | |||
33ad753302 | |||
15102e6dfd | |||
17ec84151b | |||
6c45a17af6 | |||
d19ab43500 | |||
5bcf81d1cc | |||
c2effcc61c | |||
2285cfc36f | |||
e74d05c03f | |||
48862e0e63 | |||
89892d9754 | |||
7ac0852364 | |||
d28a5d2d7a | |||
87ac3c5279 | |||
d5ee6d3039 | |||
433aab12bb | |||
81f4cdbb91 | |||
3cd7d23f10 | |||
012af5cc38 | |||
eb5336d5ff | |||
bc8d79ddf9 | |||
29708b78d7 | |||
b9284604d9 | |||
65a4320c75 | |||
9a260c2e64 | |||
6f798b9c4b | |||
e515dd4582 | |||
8b6ec57c61 | |||
ed13387c0e | |||
5afea62ec0 | |||
c0a2f20eee | |||
2d064d0bd8 | |||
ef38420623 | |||
f7caef355b | |||
fbdfd503e4 | |||
67798bb50e | |||
5b653aa65f | |||
e314f328c4 | |||
6c96cc2af6 | |||
74db735265 | |||
3304afa9d1 | |||
b42bcdc6fa | |||
b0c5def2d9 | |||
90f3669399 | |||
07ce40e119 | |||
41038b2ec0 | |||
d83879d4b8 | |||
f6582081a4 | |||
00b1cecfb7 | |||
63466d71b2 | |||
d53732f663 | |||
3012286452 | |||
714ff784fa | |||
d2a59b2de8 | |||
acd6eb1815 | |||
42bf03e5cc | |||
5992ee901a | |||
dfb00083d0 | |||
1134760271 | |||
02bb7159a5 | |||
94302235d0 | |||
cc5360a578 | |||
4190fba86d | |||
936ebbb8e5 | |||
c065d55ca3 | |||
fe9f664b57 | |||
87f4b934d1 | |||
8093e145b3 | |||
3da168f8cf | |||
4572fa4874 | |||
1efa64ee72 | |||
be744ae3e6 | |||
1b520f7973 | |||
899cd55c27 | |||
0c49bca19c | |||
5fbb2657ca | |||
a5de74a249 | |||
fc032838c0 | |||
2f710d8f94 | |||
4dc9a1b300 | |||
963faa615a | |||
9a87acb87a | |||
9206ce5cd2 | |||
6c46044c9c | |||
01e3944b31 | |||
434048e8d9 | |||
f83f7feb8c | |||
62028cd7ee | |||
f45e75e3eb | |||
57c31e9802 | |||
9c5ddc4dfe | |||
54eb005822 | |||
a13219808a | |||
7f8a1dcf8e | |||
d0ed29b3c7 | |||
5f22ba6f38 | |||
a5e1aa22c9 | |||
772b471aab | |||
4fbfffd44c | |||
29e4cf7ba1 | |||
a2ab6d4942 | |||
bdd57c8b6b | |||
d1d6e3471c | |||
401c398704 | |||
004ff9e9bf | |||
63a567a1de | |||
580cd55180 | |||
e319bf403e | |||
aedb55f913 | |||
b69e07da7a | |||
2bd560e528 | |||
95597d3437 | |||
fd18aa363b | |||
76268e3ea2 | |||
8434f3dbfc | |||
34e6a309c6 | |||
bdf386366c | |||
839dead226 | |||
3bb65ba820 | |||
d4493a6d08 | |||
0b87be804a | |||
f71418b73c | |||
c34b8acedd | |||
c290d079fd | |||
53a90634fc | |||
5a53f9c4fd | |||
1361db91ee | |||
945b7c740b | |||
61d5e140e0 | |||
3441fff05d | |||
ac1eee091d | |||
a603d14d08 | |||
d4be2f20d4 | |||
e5c8f7ff9f | |||
1e7f9909da | |||
b807d8c400 | |||
d4bec24c9f | |||
ea48e928c8 | |||
96308a26c6 | |||
74a6a1da7f | |||
2be1aa781d | |||
89d0435b1d | |||
54fe8383a4 | |||
944160427b | |||
bb44867491 | |||
546d09660f | |||
e3764c51df | |||
b33559754d | |||
f345fe9a58 | |||
3b236160a6 | |||
25d2ae8aaf | |||
e39378b1c3 | |||
8a6e3025a0 | |||
2dd3a6f7a8 | |||
b142b6f48e | |||
5f6c7cbdb1 | |||
66e17f4b8e | |||
99be4c83a7 | |||
ec8da40567 | |||
dea6f031f9 | |||
5fac4058e8 | |||
2220f6a809 | |||
a812932984 | |||
92fe5d90f5 | |||
4668efc0bf | |||
654d970fad | |||
d3b209c8e1 | |||
edb1747af7 | |||
a61201a987 | |||
6b6eabe41c | |||
d508da8397 | |||
007827255e | |||
f652518c24 | |||
273980cfb9 | |||
108e4e07be | |||
b3deb893ba | |||
7768a482b5 | |||
371d97f61a | |||
e655336390 | |||
ed396448ac | |||
9cbd32bce8 | |||
1ae86f35a8 | |||
b9043433a0 | |||
a4fb7f085b | |||
a685fcdc96 | |||
2b3fc50681 | |||
98fe24cdb7 | |||
882c068410 | |||
6c2146bbc1 | |||
03976c6ed5 | |||
7e97df4878 | |||
01b6f1733c | |||
7abbdca064 | |||
6488ddee88 | |||
d6b42972a8 | |||
5e9a97fd3e | |||
fa7f9fbce2 | |||
806ea37101 | |||
80099d9a2f | |||
a059a7dcf0 | |||
bd24beecf8 | |||
dfe825b81b | |||
76f67ea34e | |||
7d0d781db1 | |||
0f08a2efba | |||
7bf20c9f1f | |||
2542d4f5df | |||
15dae8685e | |||
7bca428db0 | |||
a345c972bf | |||
8c1082b31a | |||
cfda9003a7 | |||
6ff0b0996b | |||
8319b59238 | |||
b7acb34fa4 | |||
41104f2383 | |||
eeab417dcf | |||
5ed317e24c | |||
e890f1b4b1 | |||
a4a1c3f18b | |||
ec1509de4e | |||
5da41f1fe5 | |||
d055168e2a | |||
80ce7c3a00 | |||
93d63e1632 | |||
68029d756e | |||
fa82854af4 | |||
c985b1198f | |||
08b1f18bca | |||
36efccd862 | |||
5c01bd5be8 | |||
8e51d7849a | |||
10602b55b1 | |||
eeca796d2e | |||
327d364f34 | |||
dc3dcabadc | |||
8021bacc43 | |||
ef4cea6d19 | |||
a55600893e | |||
c49982d22a | |||
7e04083c27 | |||
a12c39667d | |||
85a77b7c21 | |||
8377372a40 | |||
dd459d399f | |||
7fd7961dfa | |||
8398a8b4b3 | |||
9c2c76ca32 | |||
1032075a21 | |||
94f07b4778 | |||
7ddba70030 | |||
658e3cb92f | |||
9ee1bd3669 | |||
ca4d6df1cc | |||
18182e578e | |||
3119f2fbc3 | |||
e377a92d97 | |||
eadcea8df0 | |||
5e5ee545b6 | |||
27caa8a72f | |||
e83d39e33f | |||
fc383ea6ae | |||
00a88b9936 | |||
3940bc17c1 | |||
e5767c9002 | |||
286df198c9 | |||
3a48b282b6 | |||
21431f22c0 | |||
d5dc14c639 | |||
39866a957c | |||
d02a7c2d38 | |||
08953a2f94 | |||
0308835d48 | |||
36956db123 | |||
3bf6e6dde6 | |||
b027a7f91e | |||
6ace2f597e | |||
784e8ef857 | |||
ca974b8b4c | |||
84ecd61dfd | |||
6ef38c07bd | |||
d90aab5454 | |||
0c2b6f3dac | |||
47bcd346d3 | |||
8eb591d668 | |||
cfd5e3d403 | |||
62cbb72a5e | |||
78b1d9b18d | |||
40c9ddb6ba | |||
3a797e4682 | |||
2bac82cd6f | |||
bbe95dac8b | |||
80d7459560 | |||
4bd4667791 | |||
f3a861806e | |||
a378ff9cf6 | |||
91bed3b0ba | |||
74842e7f43 | |||
259007540f | |||
56eeb630b6 | |||
36eab4059c | |||
72ab373b71 | |||
7a8ac4907a | |||
10497e9136 | |||
dc2867682f | |||
dc6778f385 | |||
7085723c6b | |||
4c7ff159ec | |||
81070ada01 | |||
452faa3c89 | |||
87a4a6e8d0 | |||
4f7d76c9ef |
846 changed files with 20645 additions and 14476 deletions
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.22 as builder
|
||||
FROM golang:1.23 AS builder
|
||||
ARG BUILD=now
|
||||
ARG VERSION=dev
|
||||
ARG REPO=repository
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.22
|
||||
FROM golang:1.23
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.22 as builder
|
||||
FROM golang:1.23 AS builder
|
||||
ARG BUILD=now
|
||||
ARG VERSION=dev
|
||||
ARG REPO=repository
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.22 as builder
|
||||
FROM golang:1.23 AS builder
|
||||
ARG BUILD=now
|
||||
ARG VERSION=dev
|
||||
ARG REPO=repository
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.22 as builder
|
||||
FROM golang:1.23 AS builder
|
||||
ARG BUILD=now
|
||||
ARG VERSION=dev
|
||||
ARG REPO=repository
|
||||
|
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
@ -8,7 +8,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go_versions: [ '1.21', '1.22' ]
|
||||
go_versions: [ '1.22', '1.23' ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
|
|
@ -9,14 +9,14 @@ jobs:
|
|||
SKIP: make-lint,go-staticcheck-repo-mod,go-unit-tests,gofumpt
|
||||
runs-on: ubuntu-22.04
|
||||
# If we use actions/setup-python from either Github or Gitea,
|
||||
# the line above fails with a cryptic error about not being able to found python.
|
||||
# the line above fails with a cryptic error about not being able to find python.
|
||||
# So install everything manually.
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.22
|
||||
go-version: 1.23
|
||||
- name: Set up Python
|
||||
run: |
|
||||
apt update
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
|
||||
- name: Install linters
|
||||
|
@ -25,7 +25,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go_versions: [ '1.21', '1.22' ]
|
||||
go_versions: [ '1.22', '1.23' ]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
@ -48,7 +48,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
|
||||
- name: Run tests
|
||||
|
@ -63,7 +63,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.23'
|
||||
cache: true
|
||||
|
||||
- name: Install staticcheck
|
||||
|
@ -81,7 +81,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
|
||||
- name: Install gopls
|
||||
|
@ -89,3 +89,23 @@ jobs:
|
|||
|
||||
- name: Run gopls
|
||||
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
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '1.23'
|
||||
|
||||
- name: Install govulncheck
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
|
|
|
@ -12,7 +12,8 @@ run:
|
|||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
format: tab
|
||||
formats:
|
||||
- format: tab
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
|
@ -37,6 +38,10 @@ linters-settings:
|
|||
alias:
|
||||
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object
|
||||
alias: objectSDK
|
||||
unused:
|
||||
field-writes-are-uses: false
|
||||
exported-fields-are-used: false
|
||||
local-variables-are-used: false
|
||||
custom:
|
||||
truecloudlab-linters:
|
||||
path: bin/linters/external_linters.so
|
||||
|
@ -66,7 +71,7 @@ linters:
|
|||
- bidichk
|
||||
- durationcheck
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- copyloopvar
|
||||
- gofmt
|
||||
- goimports
|
||||
- misspell
|
||||
|
@ -82,5 +87,7 @@ linters:
|
|||
- perfsprint
|
||||
- testifylint
|
||||
- protogetter
|
||||
- intrange
|
||||
- tenv
|
||||
disable-all: true
|
||||
fast: false
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
pipeline:
|
||||
# Kludge for non-root containers under WoodPecker
|
||||
fix-ownership:
|
||||
image: alpine:latest
|
||||
commands: chown -R 1234:1234 .
|
||||
|
||||
pre-commit:
|
||||
image: git.frostfs.info/truecloudlab/frostfs-ci:v0.36
|
||||
commands:
|
||||
- export HOME="$(getent passwd $(id -u) | cut '-d:' -f6)"
|
||||
- pre-commit run --hook-stage manual
|
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -9,6 +9,30 @@ Changelog for FrostFS Node
|
|||
### Removed
|
||||
### 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
|
||||
|
|
61
Makefile
61
Makefile
|
@ -4,20 +4,19 @@ SHELL = bash
|
|||
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")
|
||||
|
||||
HUB_IMAGE ?= truecloudlab/frostfs
|
||||
HUB_IMAGE ?= git.frostfs.info/truecloudlab/frostfs
|
||||
HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')"
|
||||
|
||||
GO_VERSION ?= 1.22
|
||||
LINT_VERSION ?= 1.56.1
|
||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.5
|
||||
LINT_VERSION ?= 1.62.0
|
||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.8
|
||||
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-sdk-go)
|
||||
PROTOC_OS_VERSION=osx-x86_64
|
||||
ifeq ($(shell uname), Linux)
|
||||
PROTOC_OS_VERSION=linux-x86_64
|
||||
endif
|
||||
STATICCHECK_VERSION ?= 2023.1.6
|
||||
STATICCHECK_VERSION ?= 2024.1.1
|
||||
ARCH = amd64
|
||||
|
||||
BIN = bin
|
||||
|
@ -28,24 +27,21 @@ DIRS = $(BIN) $(RELEASE)
|
|||
CMDS = $(notdir $(basename $(wildcard cmd/frostfs-*)))
|
||||
BINS = $(addprefix $(BIN)/, $(CMDS))
|
||||
|
||||
# .deb package versioning
|
||||
OS_RELEASE = $(shell lsb_release -cs)
|
||||
PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
|
||||
sed -E "s/(.*)-(g[a-fA-F0-9]{6,8})(.*)/\1\3~\2/" | \
|
||||
sed "s/-/~/")-${OS_RELEASE}
|
||||
|
||||
OUTPUT_LINT_DIR ?= $(abspath $(BIN))/linters
|
||||
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
|
||||
TMP_DIR := .cache
|
||||
PROTOBUF_DIR ?= $(abspath $(BIN))/protobuf
|
||||
PROTOC_DIR ?= $(PROTOBUF_DIR)/protoc-v$(PROTOC_VERSION)
|
||||
PROTOC_GEN_GO_DIR ?= $(PROTOBUF_DIR)/protoc-gen-go-$(PROTOC_GEN_GO_VERSION)
|
||||
PROTOGEN_FROSTFS_DIR ?= $(PROTOBUF_DIR)/protogen-$(PROTOGEN_FROSTFS_VERSION)
|
||||
STATICCHECK_DIR ?= $(abspath $(BIN))/staticcheck
|
||||
STATICCHECK_VERSION_DIR ?= $(STATICCHECK_DIR)/$(STATICCHECK_VERSION)
|
||||
|
||||
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_DIR ?= $(abspath $(BIN))/gopls
|
||||
GOPLS_VERSION_DIR ?= $(GOPLS_DIR)/$(GOPLS_VERSION)
|
||||
|
@ -56,7 +52,7 @@ LOCODE_DB_PATH=$(abspath ./.cache/locode_db)
|
|||
LOCODE_DB_VERSION=v0.4.0
|
||||
|
||||
.PHONY: help all images dep clean fmts fumpt imports test lint docker/lint
|
||||
prepare-release debpackage pre-commit unpre-commit
|
||||
prepare-release pre-commit unpre-commit
|
||||
|
||||
# To build a specific binary, use it's name prefix with bin/ as a target
|
||||
# For example `make bin/frostfs-node` will build only storage node binary
|
||||
|
@ -103,17 +99,15 @@ export-metrics: dep
|
|||
|
||||
# Regenerate proto files:
|
||||
protoc:
|
||||
@if [ ! -d "$(PROTOC_DIR)" ] || [ ! -d "$(PROTOC_GEN_GO_DIR)" ] || [ ! -d "$(PROTOGEN_FROSTFS_DIR)" ]; then \
|
||||
@if [ ! -d "$(PROTOC_DIR)" ] || [ ! -d "$(PROTOGEN_FROSTFS_DIR)" ]; then \
|
||||
make protoc-install; \
|
||||
fi
|
||||
@for f in `find . -type f -name '*.proto' -not -path './bin/*'`; do \
|
||||
echo "⇒ Processing $$f "; \
|
||||
$(PROTOC_DIR)/bin/protoc \
|
||||
--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 \
|
||||
--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_out=. --go-grpc_opt=paths=source_relative $$f; \
|
||||
done
|
||||
|
@ -126,10 +120,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'
|
||||
@unzip -q -o $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip -d $(PROTOC_DIR)
|
||||
@rm $(PROTOBUF_DIR)/protoc-$(PROTOC_VERSION).zip
|
||||
@echo "⇒ Installing protoc-gen-go..."
|
||||
@GOBIN=$(PROTOC_GEN_GO_DIR) go install -v google.golang.org/protobuf/...@$(PROTOC_GEN_GO_VERSION)
|
||||
@echo "⇒ Instaling protogen FrostFS plugin..."
|
||||
@GOBIN=$(PROTOGEN_FROSTFS_DIR) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/protogen@$(PROTOGEN_FROSTFS_VERSION)
|
||||
@GOBIN=$(PROTOGEN_FROSTFS_DIR) go install -mod=mod -v git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/util/protogen@$(PROTOGEN_FROSTFS_VERSION)
|
||||
|
||||
# Build FrostFS component's docker image
|
||||
image-%:
|
||||
|
@ -165,10 +157,19 @@ imports:
|
|||
@echo "⇒ Processing goimports check"
|
||||
@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
|
||||
fumpt:
|
||||
@if [ ! -d "$(GOFUMPT_VERSION_DIR)" ]; then \
|
||||
make fumpt-install; \
|
||||
fi
|
||||
@echo "⇒ Processing gofumpt check"
|
||||
@gofumpt -l -w cmd/ pkg/ misc/
|
||||
$(GOFUMPT_VERSION_DIR)/gofumpt -l -w cmd/ pkg/ misc/
|
||||
|
||||
# Run Unit Test with go test
|
||||
test: GOFLAGS ?= "-count=1"
|
||||
|
@ -190,7 +191,7 @@ lint-install:
|
|||
@@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
||||
@rm -rf $(TMP_DIR)/linters
|
||||
@rmdir $(TMP_DIR) 2>/dev/null || true
|
||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install -trimpath github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
||||
|
||||
# Run linters
|
||||
lint:
|
||||
|
@ -256,19 +257,6 @@ clean:
|
|||
rm -rf $(BIN)
|
||||
rm -rf $(RELEASE)
|
||||
|
||||
# Package for Debian
|
||||
debpackage:
|
||||
dch -b --package frostfs-node \
|
||||
--controlmaint \
|
||||
--newversion $(PKG_VERSION) \
|
||||
--distribution $(OS_RELEASE) \
|
||||
"Please see CHANGELOG.md for code changes for $(VERSION)"
|
||||
dpkg-buildpackage --no-sign -b
|
||||
|
||||
# Cleanup deb package build directories
|
||||
debclean:
|
||||
dh clean
|
||||
|
||||
# Download locode database
|
||||
locode-download:
|
||||
mkdir -p $(TMP_DIR)
|
||||
|
@ -294,7 +282,6 @@ env-up: all
|
|||
|
||||
# Shutdown dev environment
|
||||
env-down:
|
||||
docker compose -f dev/docker-compose.yml down
|
||||
docker volume rm -f frostfs-node_neo-go
|
||||
docker compose -f dev/docker-compose.yml down -v
|
||||
rm -rf ./$(TMP_DIR)/state
|
||||
rm -rf ./$(TMP_DIR)/storage
|
||||
|
|
21
README.md
21
README.md
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<img src="./.github/logo.svg" width="500px" alt="FrostFS">
|
||||
<img src="./.forgejo/logo.svg" width="500px" alt="FrostFS">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
|
@ -7,9 +7,8 @@
|
|||
</p>
|
||||
|
||||
---
|
||||
[![Report](https://goreportcard.com/badge/github.com/TrueCloudLab/frostfs-node)](https://goreportcard.com/report/github.com/TrueCloudLab/frostfs-node)
|
||||
![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)
|
||||
[![Report](https://goreportcard.com/badge/git.frostfs.info/TrueCloudLab/frostfs-node)](https://goreportcard.com/report/git.frostfs.info/TrueCloudLab/frostfs-node)
|
||||
![Release (latest)](https://git.frostfs.info/TrueCloudLab/frostfs-node/badges/release.svg)
|
||||
|
||||
# Overview
|
||||
|
||||
|
@ -33,8 +32,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
|
||||
protocol gateways for popular protocols such as [AWS
|
||||
S3](https://github.com/TrueCloudLab/frostfs-s3-gw),
|
||||
[HTTP](https://github.com/TrueCloudLab/frostfs-http-gw),
|
||||
S3](https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw),
|
||||
[HTTP](https://git.frostfs.info/TrueCloudLab/frostfs-http-gw),
|
||||
[FUSE](https://wikipedia.org/wiki/Filesystem_in_Userspace) and
|
||||
[sFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) allowing
|
||||
developers to integrate applications without rewriting their code.
|
||||
|
@ -45,11 +44,11 @@ Now, we only support GNU/Linux on amd64 CPUs with AVX/AVX2 instructions. More
|
|||
platforms will be officially supported after release `1.0`.
|
||||
|
||||
The latest version of frostfs-node works with frostfs-contract
|
||||
[v0.16.0](https://github.com/TrueCloudLab/frostfs-contract/releases/tag/v0.16.0).
|
||||
[v0.19.2](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases/tag/v0.19.2).
|
||||
|
||||
# Building
|
||||
|
||||
To make all binaries you need Go 1.21+ and `make`:
|
||||
To make all binaries you need Go 1.22+ and `make`:
|
||||
```
|
||||
make all
|
||||
```
|
||||
|
@ -71,7 +70,7 @@ make docker/bin/frostfs-<name> # build a specific binary
|
|||
|
||||
## Docker images
|
||||
|
||||
To make docker images suitable for use in [frostfs-dev-env](https://github.com/TrueCloudLab/frostfs-dev-env/) use:
|
||||
To make docker images suitable for use in [frostfs-dev-env](https://git.frostfs.info/TrueCloudLab/frostfs-dev-env/) use:
|
||||
```
|
||||
make images
|
||||
```
|
||||
|
@ -99,7 +98,7 @@ See `frostfs-contract`'s README.md for build instructions.
|
|||
4. To create container and put object into it run (container and object IDs will be different):
|
||||
|
||||
```
|
||||
./bin/frostfs-cli container create -r 127.0.0.1:8080 --wallet ./dev/wallet.json --policy "REP 1 IN X CBF 1 SELECT 1 FROM * AS X" --basic-acl public-read-write --await
|
||||
./bin/frostfs-cli container create -r 127.0.0.1:8080 --wallet ./dev/wallet.json --policy "REP 1 IN X CBF 1 SELECT 1 FROM * AS X" --await
|
||||
Enter password > <- press ENTER, the is no password for wallet
|
||||
CID: CfPhEuHQ2PRvM4gfBQDC4dWZY3NccovyfcnEdiq2ixju
|
||||
|
||||
|
@ -125,7 +124,7 @@ the feature/topic you are going to implement.
|
|||
|
||||
# Credits
|
||||
|
||||
FrostFS is maintained by [True Cloud Lab](https://github.com/TrueCloudLab/) with the help and
|
||||
FrostFS is maintained by [True Cloud Lab](https://git.frostfs.info/TrueCloudLab/) with the help and
|
||||
contributions from community members.
|
||||
|
||||
Please see [CREDITS](CREDITS.md) for details.
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
v0.41.0
|
||||
v0.42.0
|
||||
|
|
|
@ -56,7 +56,8 @@ credentials: # passwords for consensus node / alphabet wallets
|
|||
#### Network deployment
|
||||
|
||||
- `generate-alphabet` generates a set of wallets for consensus and
|
||||
Alphabet nodes.
|
||||
Alphabet nodes. The list of the name for alphabet wallets(no gaps between names allowed, order is important):
|
||||
- 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
|
||||
setting provided FrostFS network configuration.
|
||||
|
|
|
@ -9,8 +9,8 @@ related configuration details.
|
|||
|
||||
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 [frostfs-adm](https://github.com/TrueCloudLab/frostfs-node/releases) utility (v0.25.1 at the moment),
|
||||
- latest released version of compiled [frostfs-contract](https://github.com/TrueCloudLab/frostfs-contract/releases) (v0.11.0 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 compiled [frostfs-contract](https://git.frostfs.info/TrueCloudLab/frostfs-contract/releases) (v0.19.2 at the moment).
|
||||
|
||||
## Step 1: Prepare network configuration
|
||||
|
||||
|
@ -64,6 +64,11 @@ alphabet-wallets: /home/user/deploy/alphabet-wallets
|
|||
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
|
||||
storage.
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ func generateConfigExample(appDir string, credSize int) (string, error) {
|
|||
tmpl.AlphabetDir = filepath.Join(appDir, "alphabet-wallets")
|
||||
|
||||
var i innerring.GlagoliticLetter
|
||||
for i = 0; i < innerring.GlagoliticLetter(credSize); i++ {
|
||||
for i = range innerring.GlagoliticLetter(credSize) {
|
||||
tmpl.Glagolitics = append(tmpl.Glagolitics, i.String())
|
||||
}
|
||||
|
||||
|
|
15
cmd/frostfs-adm/internal/modules/metabase/root.go
Normal file
15
cmd/frostfs-adm/internal/modules/metabase/root.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
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()
|
||||
}
|
150
cmd/frostfs-adm/internal/modules/metabase/upgrade.go
Normal file
150
cmd/frostfs-adm/internal/modules/metabase/upgrade.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
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,6 +38,12 @@ var (
|
|||
func parseTarget(cmd *cobra.Command) policyengine.Target {
|
||||
name, _ := cmd.Flags().GetString(targetNameFlag)
|
||||
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)
|
||||
|
||||
return policyengine.Target{
|
||||
|
@ -115,7 +121,7 @@ func newPolicyContractReaderInterface(cmd *cobra.Command) (*morph.ContractStorag
|
|||
inv := invoker.New(c, nil)
|
||||
var ch util.Uint160
|
||||
r := management.NewReader(inv)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := helper.GetContractByID(r, 1)
|
||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||
|
||||
ch, err = helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||
|
@ -133,12 +139,12 @@ func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *he
|
|||
c, err := helper.GetN3Client(viper.GetViper())
|
||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||
|
||||
ac, err := helper.NewLocalActor(cmd, c)
|
||||
ac, err := helper.NewLocalActor(cmd, c, constants.ConsensusAccountName)
|
||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||
|
||||
var ch util.Uint160
|
||||
r := management.NewReader(ac.Invoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := helper.GetContractByID(r, 1)
|
||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||
|
||||
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 {
|
||||
r := management.NewReader(inv)
|
||||
nnsCs, err = r.GetContractByID(1)
|
||||
nnsCs, err = helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
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)
|
||||
r := management.NewReader(inv)
|
||||
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
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)
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
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 {
|
||||
r := management.NewReader(inv)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
||||
}
|
||||
|
@ -139,13 +139,12 @@ func dumpContainers(cmd *cobra.Command, _ []string) error {
|
|||
func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invoker, id []byte) (*Container, error) {
|
||||
bw.Reset()
|
||||
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, id)
|
||||
emit.AppCall(bw.BinWriter, ch, "eACL", callflag.All, id)
|
||||
res, err := inv.Run(bw.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get container info: %w", err)
|
||||
}
|
||||
if len(res.Stack) != 2 {
|
||||
return nil, fmt.Errorf("%w: expected 2 items on stack", errInvalidContainerResponse)
|
||||
if len(res.Stack) != 1 {
|
||||
return nil, fmt.Errorf("%w: expected 1 items on stack", errInvalidContainerResponse)
|
||||
}
|
||||
|
||||
cnt := new(Container)
|
||||
|
@ -154,14 +153,6 @@ func dumpSingleContainer(bw *io.BufBinWriter, ch util.Uint160, inv *invoker.Invo
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -258,10 +249,6 @@ func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd
|
|||
func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
||||
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
||||
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) {
|
||||
|
@ -304,7 +291,7 @@ func parseContainers(filename string) ([]Container, error) {
|
|||
|
||||
func fetchContainerContractHash(wCtx *helper.InitializeContext) (util.Uint160, error) {
|
||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
||||
}
|
||||
|
@ -322,15 +309,6 @@ type Container struct {
|
|||
Signature []byte `json:"signature"`
|
||||
PublicKey []byte `json:"public_key"`
|
||||
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.
|
||||
|
@ -377,50 +355,6 @@ func (c *Container) FromStackItem(item stackitem.Item) error {
|
|||
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.
|
||||
// Raw byte slices are used because it works with structures returned
|
||||
// from contract.
|
||||
|
|
|
@ -79,7 +79,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
r := management.NewReader(c.ReadOnlyInvoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
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))
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
|||
|
||||
if irSize != 0 {
|
||||
bw.Reset()
|
||||
for i := 0; i < irSize; i++ {
|
||||
for i := range irSize {
|
||||
emit.AppCall(bw.BinWriter, cs.Hash, "resolve", callflag.ReadOnly,
|
||||
helper.GetAlphabetNNSDomain(i),
|
||||
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)
|
||||
}
|
||||
|
||||
for i := 0; i < irSize; i++ {
|
||||
for i := range irSize {
|
||||
info := contractDumpInfo{name: fmt.Sprintf("alphabet %d", i)}
|
||||
if h, err := helper.ParseNNSResolveResult(alphaRes.Stack[i]); err == nil {
|
||||
info.hash = h
|
||||
|
|
|
@ -3,24 +3,32 @@ package frostfsid
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
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/modules/morph/constants"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||
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/encoding/address"
|
||||
"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/smartcontract/callflag"
|
||||
"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/stackitem"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const iteratorBatchSize = 1
|
||||
|
||||
const (
|
||||
namespaceFlag = "namespace"
|
||||
subjectNameFlag = "subject-name"
|
||||
|
@ -250,12 +258,15 @@ func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) {
|
|||
}
|
||||
|
||||
func frostfsidListNamespaces(cmd *cobra.Command, _ []string) {
|
||||
ffsid, err := newFrostfsIDClient(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err)
|
||||
|
||||
namespaces, err := ffsid.roCli.ListNamespaces()
|
||||
commonCmd.ExitOnErr(cmd, "list namespaces: %w", err)
|
||||
inv, _, hash := initInvoker(cmd)
|
||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||
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)
|
||||
commonCmd.ExitOnErr(cmd, "can't parse namespace: %w", err)
|
||||
sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].Name < namespaces[j].Name })
|
||||
|
||||
for _, namespace := range namespaces {
|
||||
|
@ -296,14 +307,15 @@ func frostfsidDeleteSubject(cmd *cobra.Command, _ []string) {
|
|||
}
|
||||
|
||||
func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
||||
ns := getFrostfsIDNamespace(cmd)
|
||||
includeNames, _ := cmd.Flags().GetBool(includeNamesFlag)
|
||||
ns := getFrostfsIDNamespace(cmd)
|
||||
inv, _, hash := initInvoker(cmd)
|
||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||
sessionID, it, err := reader.ListNamespaceSubjects(ns)
|
||||
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
||||
|
||||
ffsid, err := newFrostfsIDClient(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err)
|
||||
|
||||
subAddresses, err := ffsid.roCli.ListNamespaceSubjects(ns)
|
||||
commonCmd.ExitOnErr(cmd, "list subjects: %w", err)
|
||||
subAddresses, err := frostfsidclient.UnwrapArrayOfUint160(readIterator(inv, &it, iteratorBatchSize, sessionID))
|
||||
commonCmd.ExitOnErr(cmd, "can't unwrap: %w", err)
|
||||
|
||||
sort.Slice(subAddresses, func(i, j int) bool { return subAddresses[i].Less(subAddresses[j]) })
|
||||
|
||||
|
@ -313,8 +325,14 @@ func frostfsidListSubjects(cmd *cobra.Command, _ []string) {
|
|||
continue
|
||||
}
|
||||
|
||||
subj, err := ffsid.roCli.GetSubject(addr)
|
||||
commonCmd.ExitOnErr(cmd, "get subject: %w", err)
|
||||
sessionID, it, err := reader.ListSubjects()
|
||||
commonCmd.ExitOnErr(cmd, "can't 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)
|
||||
commonCmd.ExitOnErr(cmd, "can't parse subject: %w", err)
|
||||
|
||||
cmd.Printf("%s (%s)\n", address.Uint160ToString(addr), subj.Name)
|
||||
}
|
||||
|
@ -349,13 +367,17 @@ func frostfsidDeleteGroup(cmd *cobra.Command, _ []string) {
|
|||
}
|
||||
|
||||
func frostfsidListGroups(cmd *cobra.Command, _ []string) {
|
||||
inv, _, hash := initInvoker(cmd)
|
||||
ns := getFrostfsIDNamespace(cmd)
|
||||
|
||||
ffsid, err := newFrostfsIDClient(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "init contract invoker: %w", err)
|
||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||
sessionID, it, err := reader.ListGroups(ns)
|
||||
commonCmd.ExitOnErr(cmd, "can't get namespace: %w", err)
|
||||
|
||||
groups, err := ffsid.roCli.ListGroups(ns)
|
||||
commonCmd.ExitOnErr(cmd, "list groups: %w", err)
|
||||
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
||||
commonCmd.ExitOnErr(cmd, "can't list groups: %w", err)
|
||||
groups, err := frostfsidclient.ParseGroups(items)
|
||||
commonCmd.ExitOnErr(cmd, "can't parse groups: %w", err)
|
||||
|
||||
sort.Slice(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name })
|
||||
|
||||
|
@ -394,12 +416,19 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) {
|
|||
ns := getFrostfsIDNamespace(cmd)
|
||||
groupID := getFrostfsIDGroupID(cmd)
|
||||
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)
|
||||
|
||||
ffsid, err := newFrostfsIDClient(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "init contract client: %w", err)
|
||||
reader := frostfsidrpclient.NewReader(inv, hash)
|
||||
sessionID, it, err := reader.ListGroupSubjects(ns, big.NewInt(groupID))
|
||||
commonCmd.ExitOnErr(cmd, "can't list groups: %w", err)
|
||||
|
||||
subjects, err := ffsid.roCli.ListGroupSubjects(ns, groupID)
|
||||
commonCmd.ExitOnErr(cmd, "list group subjects: %w", err)
|
||||
items, err := readIterator(inv, &it, iteratorBatchSize, sessionID)
|
||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %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]) })
|
||||
|
||||
|
@ -409,9 +438,10 @@ func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) {
|
|||
continue
|
||||
}
|
||||
|
||||
subj, err := ffsid.roCli.GetSubject(subjAddr)
|
||||
commonCmd.ExitOnErr(cmd, "get subject: %w", err)
|
||||
|
||||
items, err := reader.GetSubject(subjAddr)
|
||||
commonCmd.ExitOnErr(cmd, "can't 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)
|
||||
}
|
||||
}
|
||||
|
@ -426,11 +456,11 @@ type frostfsidClient struct {
|
|||
func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) {
|
||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't to initialize context: %w", err)
|
||||
return nil, fmt.Errorf("can't initialize context: %w", err)
|
||||
}
|
||||
|
||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get NNS contract info: %w", err)
|
||||
}
|
||||
|
@ -474,3 +504,35 @@ func (f *frostfsidClient) sendWaitRes() (*state.AppExecResult, error) {
|
|||
f.wCtx.Command.Println("Waiting for transactions to persist...")
|
||||
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,7 +73,6 @@ func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, er
|
|||
return nil, fmt.Errorf("can't fetch password: %w", err)
|
||||
}
|
||||
|
||||
i := i
|
||||
errG.Go(func() error {
|
||||
p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json")
|
||||
f, err := os.OpenFile(p, os.O_CREATE, 0o644)
|
||||
|
@ -107,7 +106,6 @@ func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, er
|
|||
// Create consensus account with 2*N/3+1 multi-signature.
|
||||
bftCount := smartcontract.GetDefaultHonestNodeCount(size)
|
||||
for i := range wallets {
|
||||
i := i
|
||||
ps := pubs.Copy()
|
||||
errG.Go(func() error {
|
||||
if err := addMultisigAccount(wallets[i], majCount, constants.CommitteeAccountName, passwords[i], ps); err != nil {
|
||||
|
|
|
@ -63,7 +63,7 @@ func TestGenerateAlphabet(t *testing.T) {
|
|||
buf.Reset()
|
||||
v.Set(commonflags.AlphabetWalletsFlag, walletDir)
|
||||
require.NoError(t, GenerateAlphabetCmd.Flags().Set(commonflags.AlphabetSizeFlag, strconv.FormatUint(size, 10)))
|
||||
for i := uint64(0); i < size; i++ {
|
||||
for i := range uint64(size) {
|
||||
buf.WriteString(strconv.FormatUint(i, 10) + "\r")
|
||||
}
|
||||
|
||||
|
|
|
@ -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/modules/config"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
|
@ -31,7 +30,11 @@ type LocalActor struct {
|
|||
|
||||
// NewLocalActor create LocalActor with accounts form provided wallets.
|
||||
// In case of empty wallets provided created actor with dummy account only for read operation.
|
||||
func NewLocalActor(cmd *cobra.Command, c actor.RPCActor) (*LocalActor, error) {
|
||||
//
|
||||
// If wallets are provided, the contract client will use accounts with accName name from these wallets.
|
||||
// To determine which account name should be used in a contract client, refer to how the contract
|
||||
// verifies the transaction signature.
|
||||
func NewLocalActor(cmd *cobra.Command, c actor.RPCActor, accName string) (*LocalActor, error) {
|
||||
walletDir := config.ResolveHomePath(viper.GetString(commonflags.AlphabetWalletsFlag))
|
||||
var act *actor.Actor
|
||||
var accounts []*wallet.Account
|
||||
|
@ -53,8 +56,8 @@ func NewLocalActor(cmd *cobra.Command, c actor.RPCActor) (*LocalActor, error) {
|
|||
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
||||
|
||||
for _, w := range wallets {
|
||||
acc, err := GetWalletAccount(w, constants.CommitteeAccountName)
|
||||
commonCmd.ExitOnErr(cmd, "can't find committee account: %w", err)
|
||||
acc, err := GetWalletAccount(w, accName)
|
||||
commonCmd.ExitOnErr(cmd, fmt.Sprintf("can't find %s account: %%w", accName), err)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
act, err = actor.New(c, []actor.SignerAccount{{
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
|
||||
func getFrostfsIDAdminFromContract(roInvoker *invoker.Invoker) (util.Uint160, bool, error) {
|
||||
r := management.NewReader(roInvoker)
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
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
|
||||
// it's actual hash based on local data, thus query chain.
|
||||
r := management.NewReader(c.ReadOnlyInvoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get nns contract: %w", err)
|
||||
}
|
||||
|
@ -166,5 +166,6 @@ func DeployNNS(c *InitializeContext, method string) error {
|
|||
return fmt.Errorf("can't send deploy transaction: %w", err)
|
||||
}
|
||||
|
||||
c.Command.Println("NNS hash:", invokeHash.StringLE())
|
||||
return c.AwaitTx()
|
||||
}
|
||||
|
|
|
@ -224,7 +224,7 @@ func (l *LocalClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, e
|
|||
paramz = []manifest.Parameter{{Type: smartcontract.SignatureType}}
|
||||
} else if nSigs, _, ok := vm.ParseMultiSigContract(w.VerificationScript); ok {
|
||||
paramz = make([]manifest.Parameter, nSigs)
|
||||
for j := 0; j < nSigs; j++ {
|
||||
for j := range nSigs {
|
||||
paramz[j] = manifest.Parameter{Type: smartcontract.SignatureType}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,13 +72,17 @@ func InvalidConfigValueErr(key string) error {
|
|||
return fmt.Errorf("invalid %s config value from netmap contract", key)
|
||||
}
|
||||
|
||||
func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160) error {
|
||||
func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160, countEpoch int64) error {
|
||||
if countEpoch <= 0 {
|
||||
return errors.New("number of epochs cannot be less than 1")
|
||||
}
|
||||
|
||||
curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch"))
|
||||
if err != nil {
|
||||
return errors.New("can't fetch current epoch from the netmap contract")
|
||||
}
|
||||
|
||||
newEpoch := curr + 1
|
||||
newEpoch := curr + countEpoch
|
||||
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
|
||||
|
@ -89,7 +93,7 @@ func EmitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.
|
|||
|
||||
func GetNetConfigFromNetmapContract(roInvoker *invoker.Invoker) ([]stackitem.Item, error) {
|
||||
r := management.NewReader(roInvoker)
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get nns contract: %w", err)
|
||||
}
|
||||
|
|
|
@ -14,10 +14,12 @@ import (
|
|||
"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/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/crypto/keys"
|
||||
"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/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
@ -40,45 +42,48 @@ func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, er
|
|||
return nil, fmt.Errorf("can't read alphabet wallets dir: %w", err)
|
||||
}
|
||||
|
||||
var size int
|
||||
loop:
|
||||
for i := 0; i < len(walletFiles); i++ {
|
||||
name := innerring.GlagoliticLetter(i).String() + ".json"
|
||||
for j := range walletFiles {
|
||||
if walletFiles[j].Name() == name {
|
||||
size++
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
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()
|
||||
var wallets []*wallet.Wallet
|
||||
var letter string
|
||||
for i := range constants.MaxAlphabetNodes {
|
||||
letter = innerring.GlagoliticLetter(i).String()
|
||||
p := filepath.Join(walletDir, letter+".json")
|
||||
w, err := wallet.NewWalletFromFile(p)
|
||||
var w *wallet.Wallet
|
||||
w, err = wallet.NewWalletFromFile(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't open wallet: %w", err)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
} else {
|
||||
err = fmt.Errorf("can't open wallet: %w", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
password, err := config.GetPassword(v, letter)
|
||||
var password string
|
||||
password, err = config.GetPassword(v, letter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't fetch password: %w", err)
|
||||
err = fmt.Errorf("can't fetch password: %w", err)
|
||||
break
|
||||
}
|
||||
|
||||
for i := range w.Accounts {
|
||||
if err := w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
||||
return nil, fmt.Errorf("can't unlock wallet: %w", err)
|
||||
if err = w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
||||
err = fmt.Errorf("can't unlock wallet: %w", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
wallets[i] = w
|
||||
wallets = append(wallets, 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
|
||||
}
|
||||
|
||||
|
@ -178,3 +183,18 @@ func ParseGASAmount(s string) (fixedn.Fixed8, error) {
|
|||
}
|
||||
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 {
|
||||
r := management.NewReader(c.ReadOnlyInvoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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/io"
|
||||
|
@ -29,10 +31,14 @@ func setNotaryAndAlphabetNodes(c *helper.InitializeContext) error {
|
|||
callflag.States|callflag.AllowNotify, int64(noderoles.NeoFSAlphabet), pubs)
|
||||
|
||||
if err := c.SendCommitteeTx(w.Bytes(), false); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("send committee transaction: %w", err)
|
||||
}
|
||||
|
||||
return c.AwaitTx()
|
||||
err := c.AwaitTx()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("await committee transaction: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setRolesFinished(c *helper.InitializeContext) (bool, error) {
|
||||
|
|
|
@ -113,7 +113,7 @@ func generateTestData(dir string, size int) error {
|
|||
}
|
||||
|
||||
var pubs []string
|
||||
for i := 0; i < size; i++ {
|
||||
for i := range size {
|
||||
p := filepath.Join(dir, innerring.GlagoliticLetter(i).String()+".json")
|
||||
w, err := wallet.NewWalletFromFile(p)
|
||||
if err != nil {
|
||||
|
@ -148,7 +148,7 @@ func generateTestData(dir string, size int) error {
|
|||
}
|
||||
|
||||
func setTestCredentials(v *viper.Viper, size int) {
|
||||
for i := 0; i < size; i++ {
|
||||
for i := range size {
|
||||
v.Set("credentials."+innerring.GlagoliticLetter(i).String(), strconv.FormatUint(uint64(i), 10))
|
||||
}
|
||||
v.Set("credentials.contract", constants.TestContractPassword)
|
||||
|
|
|
@ -3,6 +3,7 @@ package initialize
|
|||
import (
|
||||
"fmt"
|
||||
"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/helper"
|
||||
|
@ -28,6 +29,10 @@ const (
|
|||
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 {
|
||||
ok, err := transferFundsFinished(c)
|
||||
if ok || err != nil {
|
||||
|
@ -54,7 +59,7 @@ func transferFunds(c *helper.InitializeContext) error {
|
|||
transferTarget{
|
||||
Token: gas.Hash,
|
||||
Address: c.CommitteeAcc.Contract.ScriptHash(),
|
||||
Amount: (gasInitialTotalSupply - initialAlphabetGASAmount*int64(len(c.Wallets))) / 2,
|
||||
Amount: initialCommitteeGASAmount(c),
|
||||
},
|
||||
transferTarget{
|
||||
Token: neo.Hash,
|
||||
|
@ -75,12 +80,19 @@ func transferFunds(c *helper.InitializeContext) error {
|
|||
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) {
|
||||
acc := c.Accounts[0]
|
||||
|
||||
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
||||
res, err := r.BalanceOf(acc.Contract.ScriptHash())
|
||||
return res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) == 1, err
|
||||
if err != nil || res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) != 1 {
|
||||
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 {
|
||||
|
@ -140,5 +152,17 @@ func createNEP17MultiTransferTx(c helper.Client, acc *wallet.Account, recipients
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create actor: %w", err)
|
||||
}
|
||||
return act.MakeRun(w.Bytes())
|
||||
tx, err := 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,14 +12,16 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const deltaFlag = "delta"
|
||||
|
||||
func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't to initialize context: %w", err)
|
||||
return fmt.Errorf("can't initialize context: %w", err)
|
||||
}
|
||||
|
||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get NNS contract info: %w", err)
|
||||
}
|
||||
|
@ -30,7 +32,8 @@ func ForceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
|||
}
|
||||
|
||||
bw := io.NewBufBinWriter()
|
||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash); err != nil {
|
||||
delta, _ := cmd.Flags().GetInt64(deltaFlag)
|
||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash, delta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ func listNetmapCandidatesNodes(cmd *cobra.Command, _ []string) {
|
|||
inv := invoker.New(c, nil)
|
||||
r := management.NewReader(inv)
|
||||
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract info: %w", err)
|
||||
|
||||
nmHash, err := helper.NNSResolveHash(inv, cs.Hash, helper.DomainOf(constants.NetmapContract))
|
||||
|
|
|
@ -35,6 +35,7 @@ func initForceNewEpochCmd() {
|
|||
ForceNewEpoch.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
ForceNewEpoch.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
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() {
|
||||
|
|
|
@ -42,3 +42,23 @@ func registerDomain(cmd *cobra.Command, _ []string) {
|
|||
commonCmd.ExitOnErr(cmd, "register domain error: %w", err)
|
||||
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")
|
||||
}
|
|
@ -2,6 +2,7 @@ package nns
|
|||
|
||||
import (
|
||||
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
||||
"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"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||
|
@ -15,11 +16,11 @@ func getRPCClient(cmd *cobra.Command) (*client.Contract, *helper.LocalActor, uti
|
|||
c, err := helper.GetN3Client(v)
|
||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||
|
||||
ac, err := helper.NewLocalActor(cmd, c)
|
||||
ac, err := helper.NewLocalActor(cmd, c, constants.CommitteeAccountName)
|
||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||
|
||||
r := management.NewReader(ac.Invoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
nnsCs, err := helper.GetContractByID(r, 1)
|
||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||
return client.New(ac, nnsCs.Hash), ac, nnsCs.Hash
|
||||
}
|
||||
|
|
|
@ -47,6 +47,19 @@ func initDelRecordsCmd() {
|
|||
_ = 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) {
|
||||
c, actor, _ := getRPCClient(cmd)
|
||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||
|
@ -115,6 +128,22 @@ func delRecords(cmd *cobra.Command, _ []string) {
|
|||
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) {
|
||||
switch strings.ToUpper(recordType) {
|
||||
case "A":
|
||||
|
|
|
@ -42,6 +42,15 @@ var (
|
|||
},
|
||||
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{
|
||||
Use: "renew",
|
||||
Short: "Increases domain expiration date",
|
||||
|
@ -86,14 +95,25 @@ var (
|
|||
},
|
||||
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() {
|
||||
initTokensCmd()
|
||||
initRegisterCmd()
|
||||
initDeleteCmd()
|
||||
initRenewCmd()
|
||||
initUpdateCmd()
|
||||
initAddRecordCmd()
|
||||
initGetRecordsCmd()
|
||||
initDelRecordsCmd()
|
||||
initDelRecordCmd()
|
||||
}
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
package nns
|
||||
|
||||
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"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
verboseDesc = "Include additional information about CNAME record."
|
||||
)
|
||||
|
||||
func initTokensCmd() {
|
||||
Cmd.AddCommand(tokensCmd)
|
||||
tokensCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
tokensCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
tokensCmd.Flags().BoolP(commonflags.Verbose, commonflags.VerboseShorthand, false, verboseDesc)
|
||||
}
|
||||
|
||||
func listTokens(cmd *cobra.Command, _ []string) {
|
||||
|
@ -18,7 +28,39 @@ func listTokens(cmd *cobra.Command, _ []string) {
|
|||
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 _, token := range toks {
|
||||
cmd.Println(string(token))
|
||||
output := 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()
|
||||
|
||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
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())
|
||||
}
|
||||
|
||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash); err != nil {
|
||||
if err := helper.EmitNewEpochCall(bw, wCtx, nmHash, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ var errInvalidParameterFormat = errors.New("invalid parameter format, must be Pa
|
|||
func SetPolicyCmd(cmd *cobra.Command, args []string) error {
|
||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't to initialize context: %w", err)
|
||||
return fmt.Errorf("can't initialize context: %w", err)
|
||||
}
|
||||
|
||||
bw := io.NewBufBinWriter()
|
||||
|
|
|
@ -39,11 +39,11 @@ func removeProxyAccount(cmd *cobra.Command, _ []string) {
|
|||
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
||||
wCtx, err := helper.NewInitializeContext(cmd, viper.GetViper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't to initialize context: %w", err)
|
||||
return fmt.Errorf("can't initialize context: %w", err)
|
||||
}
|
||||
|
||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||
cs, err := r.GetContractByID(1)
|
||||
cs, err := helper.GetContractByID(r, 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get NNS contract info: %w", err)
|
||||
}
|
||||
|
|
|
@ -30,11 +30,13 @@ var (
|
|||
func initProxyAddAccount() {
|
||||
AddAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
AddAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||
AddAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func initProxyRemoveAccount() {
|
||||
RemoveAccountCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
RemoveAccountCmd.Flags().String(accountAddressFlag, "", "Wallet address string")
|
||||
RemoveAccountCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"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/metabase"
|
||||
"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/misc"
|
||||
|
@ -41,6 +42,7 @@ func init() {
|
|||
rootCmd.AddCommand(config.RootCmd)
|
||||
rootCmd.AddCommand(morph.RootCmd)
|
||||
rootCmd.AddCommand(storagecfg.RootCmd)
|
||||
rootCmd.AddCommand(metabase.RootCmd)
|
||||
|
||||
rootCmd.AddCommand(autocomplete.Command("frostfs-adm"))
|
||||
rootCmd.AddCommand(gendoc.Command(rootCmd, gendoc.Options{}))
|
||||
|
|
|
@ -72,4 +72,3 @@ All other `object` sub-commands support only static sessions (2).
|
|||
List of commands supporting sessions (static only):
|
||||
- `create`
|
||||
- `delete`
|
||||
- `set-eacl`
|
||||
|
|
|
@ -2,10 +2,13 @@ package internal
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -14,7 +17,6 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
|
@ -189,54 +191,6 @@ func DeleteContainer(ctx context.Context, prm DeleteContainerPrm) (res DeleteCon
|
|||
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.
|
||||
type NetworkInfoPrm struct {
|
||||
Client *client.Client
|
||||
|
@ -611,13 +565,6 @@ type HeadObjectPrm struct {
|
|||
commonObjectPrm
|
||||
objectAddressPrm
|
||||
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.
|
||||
|
@ -712,9 +659,7 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
|
|||
|
||||
for {
|
||||
n, ok = rdr.Read(buf)
|
||||
for i := 0; i < n; i++ {
|
||||
list = append(list, buf[i])
|
||||
}
|
||||
list = append(list, buf[:n]...)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
@ -725,9 +670,8 @@ func SearchObjects(ctx context.Context, prm SearchObjectsPrm) (*SearchObjectsRes
|
|||
return nil, fmt.Errorf("read object list: %w", err)
|
||||
}
|
||||
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
lhs, rhs := list[i].EncodeToString(), list[j].EncodeToString()
|
||||
return strings.Compare(lhs, rhs) < 0
|
||||
slices.SortFunc(list, func(a, b oid.ID) int {
|
||||
return strings.Compare(a.EncodeToString(), b.EncodeToString())
|
||||
})
|
||||
|
||||
return &SearchObjectsRes{
|
||||
|
@ -892,3 +836,65 @@ func SyncContainerSettings(ctx context.Context, prm SyncContainerPrm) (*SyncCont
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ func GetSDKClient(ctx context.Context, cmd *cobra.Command, key *ecdsa.PrivateKey
|
|||
GRPCDialOptions: []grpc.DialOption{
|
||||
grpc.WithChainUnaryInterceptor(tracing.NewUnaryClientInteceptor()),
|
||||
grpc.WithChainStreamInterceptor(tracing.NewStreamClientInterceptor()),
|
||||
grpc.WithDefaultCallOptions(grpc.WaitForReady(true)),
|
||||
},
|
||||
}
|
||||
if timeout := viper.GetDuration(commonflags.Timeout); timeout > 0 {
|
||||
|
|
|
@ -2,7 +2,7 @@ package common
|
|||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||
|
@ -45,15 +45,11 @@ func StartClientCommandSpan(cmd *cobra.Command) {
|
|||
})
|
||||
commonCmd.ExitOnErr(cmd, "init tracing: %w", err)
|
||||
|
||||
var components sort.StringSlice
|
||||
var components []string
|
||||
for c := cmd; c != nil; c = c.Parent() {
|
||||
components = append(components, c.Name())
|
||||
}
|
||||
for i, j := 0, len(components)-1; i < j; {
|
||||
components.Swap(i, j)
|
||||
i++
|
||||
j--
|
||||
}
|
||||
slices.Reverse(components)
|
||||
|
||||
operation := strings.Join(components, ".")
|
||||
ctx, span := tracing.StartSpanFromContext(cmd.Context(), operation)
|
||||
|
|
|
@ -53,6 +53,10 @@ const (
|
|||
|
||||
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:
|
||||
|
|
|
@ -24,6 +24,8 @@ var testCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func Test_getOrGenerate(t *testing.T) {
|
||||
t.Cleanup(viper.Reset)
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
wallPath := filepath.Join(dir, "wallet.json")
|
||||
|
|
|
@ -31,11 +31,10 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
defaultNamespace = ""
|
||||
namespaceTarget = "namespace"
|
||||
containerTarget = "container"
|
||||
userTarget = "user"
|
||||
groupTarget = "group"
|
||||
namespaceTarget = "namespace"
|
||||
containerTarget = "container"
|
||||
userTarget = "user"
|
||||
groupTarget = "group"
|
||||
)
|
||||
|
||||
var errUnknownTargetType = errors.New("unknown target type")
|
||||
|
|
|
@ -7,22 +7,20 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
containerApi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
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"
|
||||
containerApi "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
containerACL string
|
||||
containerPolicy string
|
||||
containerAttributes []string
|
||||
containerAwait bool
|
||||
|
@ -89,9 +87,6 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
|||
err = parseAttributes(&cnr, containerAttributes)
|
||||
commonCmd.ExitOnErr(cmd, "", err)
|
||||
|
||||
var basicACL acl.Basic
|
||||
commonCmd.ExitOnErr(cmd, "decode basic ACL string: %w", basicACL.DecodeString(containerACL))
|
||||
|
||||
tok := getSession(cmd)
|
||||
|
||||
if tok != nil {
|
||||
|
@ -105,7 +100,6 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
|||
}
|
||||
|
||||
cnr.SetPlacementPolicy(*placementPolicy)
|
||||
cnr.SetBasicACL(basicACL)
|
||||
|
||||
var syncContainerPrm internalclient.SyncContainerPrm
|
||||
syncContainerPrm.SetClient(cli)
|
||||
|
@ -139,7 +133,7 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
|||
},
|
||||
}
|
||||
|
||||
for i := 0; i < awaitTimeout; i++ {
|
||||
for range awaitTimeout {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
||||
|
@ -163,10 +157,6 @@ func initContainerCreateCmd() {
|
|||
flags.DurationP(commonflags.Timeout, commonflags.TimeoutShorthand, commonflags.TimeoutDefault, commonflags.TimeoutUsage)
|
||||
flags.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
|
||||
flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
|
||||
|
||||
flags.StringVar(&containerACL, "basic-acl", acl.NamePrivate, fmt.Sprintf("HEX encoded basic ACL value or keywords like '%s', '%s', '%s'",
|
||||
acl.NamePublicRW, acl.NamePrivate, acl.NamePublicROExtended,
|
||||
))
|
||||
flags.StringVarP(&containerPolicy, "policy", "p", "", "QL-encoded or JSON-encoded placement policy or path to file with it")
|
||||
flags.StringSliceVarP(&containerAttributes, "attributes", "a", nil, "Comma separated pairs of container attributes in form of Key1=Value1,Key2=Value2")
|
||||
flags.BoolVar(&containerAwait, "await", false, "Block execution until container is persisted")
|
||||
|
|
|
@ -110,7 +110,7 @@ Only owner of the container has a permission to remove container.`,
|
|||
},
|
||||
}
|
||||
|
||||
for i := 0; i < awaitTimeout; i++ {
|
||||
for range awaitTimeout {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
_, err := internalclient.GetContainer(cmd.Context(), getPrm)
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
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,9 +1,6 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||
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"
|
||||
|
@ -70,7 +67,6 @@ var listContainersCmd = &cobra.Command{
|
|||
continue
|
||||
}
|
||||
|
||||
cnrID := cnrID
|
||||
prmGet.ClientParams.ContainerID = &cnrID
|
||||
res, err := internalclient.GetContainer(cmd.Context(), prmGet)
|
||||
if err != nil {
|
||||
|
@ -85,12 +81,8 @@ var listContainersCmd = &cobra.Command{
|
|||
cmd.Println(cnrID.String())
|
||||
|
||||
if flagVarListPrintAttr {
|
||||
cnr.IterateAttributes(func(key, val string) {
|
||||
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)
|
||||
}
|
||||
cnr.IterateUserAttributes(func(key, val string) {
|
||||
cmd.Printf(" %s: %s\n", key, val)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
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"
|
||||
|
@ -67,14 +64,8 @@ var listContainerObjectsCmd = &cobra.Command{
|
|||
|
||||
resHead, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
||||
if err == nil {
|
||||
attrs := resHead.Header().Attributes()
|
||||
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())
|
||||
}
|
||||
for _, attr := range resHead.Header().UserAttributes() {
|
||||
cmd.Printf(" %s: %s\n", attr.Key(), attr.Value())
|
||||
}
|
||||
} else {
|
||||
cmd.Printf(" failed to read attributes: %v\n", err)
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
|
@ -14,6 +13,7 @@ import (
|
|||
"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/netmap"
|
||||
"github.com/chzyer/readline"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
@ -163,6 +163,16 @@ func (repl *policyPlaygroundREPL) netMap() netmap.NetMap {
|
|||
return nm
|
||||
}
|
||||
|
||||
var policyPlaygroundCompleter = readline.NewPrefixCompleter(
|
||||
readline.PcItem("list"),
|
||||
readline.PcItem("ls"),
|
||||
readline.PcItem("add"),
|
||||
readline.PcItem("load"),
|
||||
readline.PcItem("remove"),
|
||||
readline.PcItem("rm"),
|
||||
readline.PcItem("eval"),
|
||||
)
|
||||
|
||||
func (repl *policyPlaygroundREPL) run() error {
|
||||
if len(viper.GetString(commonflags.RPC)) > 0 {
|
||||
key := key.GetOrGenerate(repl.cmd)
|
||||
|
@ -189,22 +199,38 @@ func (repl *policyPlaygroundREPL) run() error {
|
|||
"rm": repl.handleRemove,
|
||||
"eval": repl.handleEval,
|
||||
}
|
||||
for reader := bufio.NewReader(os.Stdin); ; {
|
||||
fmt.Print("> ")
|
||||
line, err := reader.ReadString('\n')
|
||||
|
||||
rl, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "> ",
|
||||
InterruptPrompt: "^C",
|
||||
AutoComplete: policyPlaygroundCompleter,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing readline: %w", err)
|
||||
}
|
||||
defer rl.Close()
|
||||
|
||||
var exit bool
|
||||
for {
|
||||
line, err := rl.Readline()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
if errors.Is(err, readline.ErrInterrupt) {
|
||||
if exit {
|
||||
return nil
|
||||
}
|
||||
exit = true
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("reading line: %v", err)
|
||||
return fmt.Errorf("reading line: %w", err)
|
||||
}
|
||||
exit = false
|
||||
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
cmd := parts[0]
|
||||
handler, exists := cmdHandlers[cmd]
|
||||
if exists {
|
||||
if handler, exists := cmdHandlers[cmd]; exists {
|
||||
if err := handler(parts[1:]); err != nil {
|
||||
fmt.Printf("error: %v\n", err)
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ func init() {
|
|||
deleteContainerCmd,
|
||||
listContainerObjectsCmd,
|
||||
getContainerInfoCmd,
|
||||
getExtendedACLCmd,
|
||||
setExtendedACLCmd,
|
||||
containerNodesCmd,
|
||||
policyPlaygroundCmd,
|
||||
}
|
||||
|
@ -38,8 +36,6 @@ func init() {
|
|||
initContainerDeleteCmd()
|
||||
initContainerListObjectsCmd()
|
||||
initContainerInfoCmd()
|
||||
initContainerGetEACLCmd()
|
||||
initContainerSetEACLCmd()
|
||||
initContainerNodesCmd()
|
||||
initContainerPolicyPlaygroundCmd()
|
||||
|
||||
|
@ -53,7 +49,6 @@ func init() {
|
|||
}{
|
||||
{createContainerCmd, "PUT"},
|
||||
{deleteContainerCmd, "DELETE"},
|
||||
{setExtendedACLCmd, "SETEACL"},
|
||||
} {
|
||||
commonflags.InitSession(el.cmd, "container "+el.verb)
|
||||
}
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
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,9 +18,8 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
errCreateTimeout = errors.New("timeout: container has not been persisted on sidechain")
|
||||
errDeleteTimeout = errors.New("timeout: container has not been removed from sidechain")
|
||||
errSetEACLTimeout = errors.New("timeout: EACL 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")
|
||||
)
|
||||
|
||||
func parseContainerID(cmd *cobra.Command) cid.ID {
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
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"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"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"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
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"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"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"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"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/local_object_storage/shard"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
clientSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -20,6 +20,10 @@ const (
|
|||
awaitFlag = "await"
|
||||
noProgressFlag = "no-progress"
|
||||
scopeFlag = "scope"
|
||||
repOneOnlyFlag = "rep-one-only"
|
||||
|
||||
containerWorkerCountFlag = "container-worker-count"
|
||||
objectWorkerCountFlag = "object-worker-count"
|
||||
|
||||
scopeAll = "all"
|
||||
scopeObjects = "objects"
|
||||
|
@ -64,12 +68,18 @@ func startEvacuateShard(cmd *cobra.Command, _ []string) {
|
|||
pk := key.Get(cmd)
|
||||
|
||||
ignoreErrors, _ := cmd.Flags().GetBool(ignoreErrorsFlag)
|
||||
containerWorkerCount, _ := cmd.Flags().GetUint32(containerWorkerCountFlag)
|
||||
objectWorkerCount, _ := cmd.Flags().GetUint32(objectWorkerCountFlag)
|
||||
repOneOnly, _ := cmd.Flags().GetBool(repOneOnlyFlag)
|
||||
|
||||
req := &control.StartShardEvacuationRequest{
|
||||
Body: &control.StartShardEvacuationRequest_Body{
|
||||
Shard_ID: getShardIDList(cmd),
|
||||
IgnoreErrors: ignoreErrors,
|
||||
Scope: getEvacuationScope(cmd),
|
||||
Shard_ID: getShardIDList(cmd),
|
||||
IgnoreErrors: ignoreErrors,
|
||||
Scope: getEvacuationScope(cmd),
|
||||
ContainerWorkerCount: containerWorkerCount,
|
||||
ObjectWorkerCount: objectWorkerCount,
|
||||
RepOneOnly: repOneOnly,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -371,6 +381,9 @@ func initControlStartEvacuationShardCmd() {
|
|||
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(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)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"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"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ package control
|
|||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"os"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -24,6 +27,7 @@ func initControlHealthCheckCmd() {
|
|||
|
||||
flags := healthCheckCmd.Flags()
|
||||
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.")
|
||||
}
|
||||
|
||||
|
@ -50,6 +54,12 @@ func healthCheck(cmd *cobra.Command, args []string) {
|
|||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||
|
||||
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("Health status: %s\n", resp.GetBody().GetHealthStatus())
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"os"
|
||||
|
||||
"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"
|
||||
ircontrol "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
||||
ircontrolsrv "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir/server"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -18,6 +21,8 @@ var irHealthCheckCmd = &cobra.Command{
|
|||
|
||||
func initControlIRHealthCheckCmd() {
|
||||
initControlFlags(irHealthCheckCmd)
|
||||
flags := irHealthCheckCmd.Flags()
|
||||
flags.BoolP(commonflags.QuietFlag, commonflags.QuietFlagShorthand, false, commonflags.QuietFlagUsage)
|
||||
}
|
||||
|
||||
func irHealthCheck(cmd *cobra.Command, _ []string) {
|
||||
|
@ -39,6 +44,12 @@ func irHealthCheck(cmd *cobra.Command, _ []string) {
|
|||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
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"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
ircontrol "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
||||
ircontrolsrv "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir/server"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
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"
|
||||
ircontrol "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
||||
ircontrolsrv "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir/server"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
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"
|
||||
ircontrol "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
|
||||
ircontrolsrv "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir/server"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
"github.com/nspcc-dev/neo-go/cli/input"
|
||||
|
|
|
@ -7,12 +7,14 @@ import (
|
|||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"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"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -52,6 +54,10 @@ func listTargets(cmd *cobra.Command, _ []string) {
|
|||
resp, err = control.ListTargetsLocalOverrides(client, req)
|
||||
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)
|
||||
|
||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
||||
|
|
88
cmd/frostfs-cli/modules/control/rebuild_shards.go
Normal file
88
cmd/frostfs-cli/modules/control/rebuild_shards.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"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)
|
||||
}
|
|
@ -4,10 +4,10 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"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"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
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/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -84,7 +84,7 @@ func setNetmapStatus(cmd *cobra.Command, _ []string) {
|
|||
body.SetStatus(control.NetmapStatus_MAINTENANCE)
|
||||
|
||||
if force {
|
||||
body.SetForceMaintenance()
|
||||
body.SetForceMaintenance(true)
|
||||
common.PrintVerbose(cmd, "Local maintenance will be forced.")
|
||||
}
|
||||
targetStatus = control.NetmapStatus_MAINTENANCE
|
||||
|
|
|
@ -19,6 +19,7 @@ func initControlShardsCmd() {
|
|||
shardsCmd.AddCommand(doctorCmd)
|
||||
shardsCmd.AddCommand(writecacheShardCmd)
|
||||
shardsCmd.AddCommand(shardsDetachCmd)
|
||||
shardsCmd.AddCommand(shardsRebuildCmd)
|
||||
|
||||
initControlShardsListCmd()
|
||||
initControlSetShardModeCmd()
|
||||
|
@ -28,4 +29,5 @@ func initControlShardsCmd() {
|
|||
initControlDoctorCmd()
|
||||
initControlShardsWritecacheCmd()
|
||||
initControlShardsDetachCmd()
|
||||
initControlShardRebuildCmd()
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -61,17 +61,18 @@ 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))
|
||||
for _, i := range ii {
|
||||
out = append(out, map[string]any{
|
||||
"shard_id": base58.Encode(i.GetShard_ID()),
|
||||
"mode": shardModeToString(i.GetMode()),
|
||||
"metabase": i.GetMetabasePath(),
|
||||
"blobstor": i.GetBlobstor(),
|
||||
"writecache": i.GetWritecachePath(),
|
||||
"pilorama": i.GetPiloramaPath(),
|
||||
"error_count": i.GetErrorCount(),
|
||||
"shard_id": base58.Encode(i.GetShard_ID()),
|
||||
"mode": shardModeToString(i.GetMode()),
|
||||
"metabase": i.GetMetabasePath(),
|
||||
"blobstor": i.GetBlobstor(),
|
||||
"writecache": i.GetWritecachePath(),
|
||||
"pilorama": i.GetPiloramaPath(),
|
||||
"error_count": i.GetErrorCount(),
|
||||
"evacuation_in_progress": i.GetEvacuationInProgress(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -80,10 +81,10 @@ func prettyPrintShardsJSON(cmd *cobra.Command, ii []*control.ShardInfo) {
|
|||
enc.SetIndent("", " ")
|
||||
commonCmd.ExitOnErr(cmd, "cannot shard info to JSON: %w", enc.Encode(out))
|
||||
|
||||
cmd.Print(buf.String()) // pretty printer emits newline, to no need for Println
|
||||
cmd.Print(buf.String()) // pretty printer emits newline, so no need for Println
|
||||
}
|
||||
|
||||
func prettyPrintShards(cmd *cobra.Command, ii []*control.ShardInfo) {
|
||||
func prettyPrintShards(cmd *cobra.Command, ii []control.ShardInfo) {
|
||||
for _, i := range ii {
|
||||
pathPrinter := func(name, path string) string {
|
||||
if path == "" {
|
||||
|
@ -105,7 +106,8 @@ func prettyPrintShards(cmd *cobra.Command, ii []*control.ShardInfo) {
|
|||
sb.String()+
|
||||
pathPrinter("Write-cache", i.GetWritecachePath())+
|
||||
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()),
|
||||
shardModeToString(i.GetMode()),
|
||||
)
|
||||
|
@ -121,7 +123,7 @@ func shardModeToString(m control.ShardMode) string {
|
|||
return "unknown"
|
||||
}
|
||||
|
||||
func sortShardsByID(ii []*control.ShardInfo) {
|
||||
func sortShardsByID(ii []control.ShardInfo) {
|
||||
sort.Slice(ii, func(i, j int) bool {
|
||||
return bytes.Compare(ii[i].GetShard_ID(), ii[j].GetShard_ID()) < 0
|
||||
})
|
||||
|
|
|
@ -6,10 +6,10 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -117,10 +117,10 @@ func setShardMode(cmd *cobra.Command, _ []string) {
|
|||
req.SetBody(body)
|
||||
|
||||
body.SetMode(mode)
|
||||
body.SetShardIDList(getShardIDList(cmd))
|
||||
body.SetShard_ID(getShardIDList(cmd))
|
||||
|
||||
reset, _ := cmd.Flags().GetBool(shardClearErrorsFlag)
|
||||
body.ClearErrorCounter(reset)
|
||||
body.SetResetErrorCounter(reset)
|
||||
|
||||
signRequest(cmd, pk, req)
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"crypto/sha256"
|
||||
"errors"
|
||||
|
||||
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"
|
||||
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/server/ctrlmessage"
|
||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"errors"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/server/ctrlmessage"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -44,7 +44,7 @@ func verifyResponse(cmd *cobra.Command,
|
|||
GetSign() []byte
|
||||
},
|
||||
body interface {
|
||||
StableMarshal([]byte) []byte
|
||||
MarshalProtobuf([]byte) []byte
|
||||
},
|
||||
) {
|
||||
if sigControl == nil {
|
||||
|
@ -60,7 +60,7 @@ func verifyResponse(cmd *cobra.Command,
|
|||
var sig frostfscrypto.Signature
|
||||
commonCmd.ExitOnErr(cmd, "can't read signature: %w", sig.ReadFromV2(sigV2))
|
||||
|
||||
if !sig.Verify(body.StableMarshal(nil)) {
|
||||
if !sig.Verify(body.MarshalProtobuf(nil)) {
|
||||
commonCmd.ExitOnErr(cmd, "", errors.New("invalid response signature"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"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"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
asyncFlag = "async"
|
||||
restoreModeFlag = "restore-mode"
|
||||
shrinkFlag = "shrink"
|
||||
)
|
||||
|
||||
var writecacheShardCmd = &cobra.Command{
|
||||
Use: "writecache",
|
||||
Short: "Operations with storage node's write-cache",
|
||||
|
@ -26,10 +32,16 @@ func sealWritecache(cmd *cobra.Command, _ []string) {
|
|||
pk := key.Get(cmd)
|
||||
|
||||
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{
|
||||
Shard_ID: getShardIDList(cmd),
|
||||
IgnoreErrors: ignoreErrors,
|
||||
Async: async,
|
||||
RestoreMode: restoreMode,
|
||||
Shrink: shrink,
|
||||
}}
|
||||
|
||||
signRequest(cmd, pk, req)
|
||||
|
@ -68,6 +80,9 @@ func initControlShardsWritecacheCmd() {
|
|||
ff.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding")
|
||||
ff.Bool(shardAllFlag, false, "Process all shards")
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -49,14 +49,14 @@ func prettyPrintNodeInfo(cmd *cobra.Command, i netmap.NodeInfo) {
|
|||
cmd.Println("key:", hex.EncodeToString(i.PublicKey()))
|
||||
|
||||
var stateWord string
|
||||
switch {
|
||||
switch i.Status() {
|
||||
default:
|
||||
stateWord = "<undefined>"
|
||||
case i.IsOnline():
|
||||
case netmap.Online:
|
||||
stateWord = "online"
|
||||
case i.IsOffline():
|
||||
case netmap.Offline:
|
||||
stateWord = "offline"
|
||||
case i.IsMaintenance():
|
||||
case netmap.Maintenance:
|
||||
stateWord = "maintenance"
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
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/api/refs"
|
||||
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"
|
||||
|
@ -38,7 +38,6 @@ func initObjectHeadCmd() {
|
|||
_ = objectHeadCmd.MarkFlagRequired(commonflags.OIDFlag)
|
||||
|
||||
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("proto", false, "Marshal output in Protobuf")
|
||||
flags.Bool(rawFlag, false, rawFlagDesc)
|
||||
|
@ -49,7 +48,6 @@ func getObjectHeader(cmd *cobra.Command, _ []string) {
|
|||
var obj oid.ID
|
||||
|
||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
||||
mainOnly, _ := cmd.Flags().GetBool("main-only")
|
||||
pk := key.GetOrGenerate(cmd)
|
||||
|
||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||
|
@ -62,7 +60,6 @@ func getObjectHeader(cmd *cobra.Command, _ []string) {
|
|||
raw, _ := cmd.Flags().GetBool(rawFlag)
|
||||
prm.SetRawFlag(raw)
|
||||
prm.SetAddress(objAddr)
|
||||
prm.SetMainOnlyFlag(mainOnly)
|
||||
|
||||
res, err := internalclient.HeadObject(cmd.Context(), prm)
|
||||
if err != nil {
|
||||
|
|
|
@ -7,12 +7,12 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
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"
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
||||
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"
|
||||
|
|
|
@ -30,7 +30,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
verifyPresenceAllFlag = "verify-presence-all"
|
||||
verifyPresenceAllFlag = "verify-presence-all"
|
||||
preferInternalAddressesFlag = "prefer-internal-addresses"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -97,6 +98,7 @@ func initObjectNodesCmd() {
|
|||
|
||||
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(preferInternalAddressesFlag, false, "Use internal addresses first to get object info.")
|
||||
}
|
||||
|
||||
func objectNodes(cmd *cobra.Command, _ []string) {
|
||||
|
@ -170,7 +172,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 {
|
||||
splitInfo := errSplitInfo.SplitInfo()
|
||||
|
||||
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnrID, false); ok {
|
||||
if members, ok := tryGetSplitMembersByLinkingObject(cmd, splitInfo, prmHead, cnrID); ok {
|
||||
return members
|
||||
}
|
||||
|
||||
|
@ -183,6 +185,7 @@ 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 {
|
||||
result := make([]phyObject, 0, len(members))
|
||||
var hasNonEC, hasEC bool
|
||||
var resultGuard sync.Mutex
|
||||
|
||||
if len(members) == 0 {
|
||||
|
@ -191,31 +194,8 @@ func flattenComplexMembersIfECContainer(cmd *cobra.Command, cnrID cid.ID, member
|
|||
|
||||
prmHead.SetRawFlag(true) // to get an error instead of whole object
|
||||
|
||||
first := members[0]
|
||||
var addrObj oid.Address
|
||||
addrObj.SetContainer(cnrID)
|
||||
addrObj.SetObject(first)
|
||||
prmHead.SetAddress(addrObj)
|
||||
|
||||
_, err := internalclient.HeadObject(cmd.Context(), prmHead)
|
||||
var ecInfoError *objectSDK.ECInfoError
|
||||
if errors.As(err, &ecInfoError) {
|
||||
chunks := getECObjectChunks(cmd, cnrID, first, ecInfoError)
|
||||
result = append(result, chunks...)
|
||||
} else if err == nil { // not EC object, so all members must be phy objects
|
||||
for _, member := range members {
|
||||
result = append(result, phyObject{
|
||||
containerID: cnrID,
|
||||
objectID: member,
|
||||
})
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
commonCmd.ExitOnErr(cmd, "failed to flatten parts of complex object: %w", err)
|
||||
}
|
||||
|
||||
eg, egCtx := errgroup.WithContext(cmd.Context())
|
||||
for idx := 1; idx < len(members); idx++ {
|
||||
for idx := range members {
|
||||
partObjID := members[idx]
|
||||
|
||||
eg.Go(func() error {
|
||||
|
@ -225,24 +205,44 @@ func flattenComplexMembersIfECContainer(cmd *cobra.Command, cnrID cid.ID, member
|
|||
partAddr.SetObject(partObjID)
|
||||
partHeadPrm.SetAddress(partAddr)
|
||||
|
||||
_, err := internalclient.HeadObject(egCtx, partHeadPrm)
|
||||
var ecInfoError *objectSDK.ECInfoError
|
||||
if errors.As(err, &ecInfoError) {
|
||||
chunks := getECObjectChunks(cmd, cnrID, partObjID, ecInfoError)
|
||||
|
||||
resultGuard.Lock()
|
||||
defer resultGuard.Unlock()
|
||||
result = append(result, chunks...)
|
||||
|
||||
return nil
|
||||
} else if err == nil {
|
||||
return errMalformedComplexObject
|
||||
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
|
||||
}
|
||||
return err
|
||||
|
||||
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()))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -393,8 +393,6 @@ func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.
|
|||
|
||||
eg, egCtx := errgroup.WithContext(cmd.Context())
|
||||
for _, cand := range candidates {
|
||||
cand := cand
|
||||
|
||||
eg.Go(func() error {
|
||||
cli, err := createClient(egCtx, cmd, cand, pk)
|
||||
if err != nil {
|
||||
|
@ -405,7 +403,6 @@ func getActualPlacement(cmd *cobra.Command, netmap *netmapSDK.NetMap, pk *ecdsa.
|
|||
}
|
||||
|
||||
for _, object := range objects {
|
||||
object := object
|
||||
eg.Go(func() error {
|
||||
stored, err := isObjectStoredOnNode(egCtx, cmd, object.containerID, object.objectID, cli, pk)
|
||||
resultMtx.Lock()
|
||||
|
@ -449,11 +446,20 @@ 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) {
|
||||
var cli *client.Client
|
||||
var addresses []string
|
||||
candidate.IterateNetworkEndpoints(func(s string) bool {
|
||||
addresses = append(addresses, s)
|
||||
return false
|
||||
})
|
||||
addresses = append(addresses, candidate.ExternalAddresses()...)
|
||||
if preferInternal, _ := cmd.Flags().GetBool(preferInternalAddressesFlag); preferInternal {
|
||||
candidate.IterateNetworkEndpoints(func(s string) bool {
|
||||
addresses = append(addresses, s)
|
||||
return false
|
||||
})
|
||||
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
|
||||
for _, address := range addresses {
|
||||
var networkAddr network.Address
|
||||
|
@ -497,7 +503,6 @@ func isObjectStoredOnNode(ctx context.Context, cmd *cobra.Command, cnrID cid.ID,
|
|||
if errors.As(err, ¬Found) || errors.As(err, &removed) {
|
||||
return false, nil
|
||||
}
|
||||
cmd.Printf("failed to get object %s from client\n", objID.EncodeToString())
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
|
151
cmd/frostfs-cli/modules/object/patch.go
Normal file
151
cmd/frostfs-cli/modules/object/patch.go
Normal file
|
@ -0,0 +1,151 @@
|
|||
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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue